본문 바로가기
웹 크롤링

웹 크롤링 - [Python]파이썬으로 카카오 맵 API 사용하기(2) - 특정 범위 검색

by haries 2021. 3. 31.

목표

'위도, 경도, 키워드를 조합해, 46개 이상의 모든 결과 값을 가져오겠습니다.'

 

 

 

지난 포스팅에서 키워드 만으로 결과 값을 도출해 내었습니다. 하지만 키워드만 검색해서는 46개 이상의 결과를 가져올 수 없었습니다. '성산일출봉 전기충전소'의 검색 결과는 60여개가 넘게 나오는데, 가져올 수 있는 건 45개만 가져옵니다. 총 검색 결과 개수만 알려주고, 결과는 45개만 가져오게 해주는 나쁜 카카오API........

 

여기서 굴복할 수 없져 ㅋㅋㅋ

이런 문제를 해결하고자 위도와 경도, 키워드 3가지를 이용해 모든 결과를 가져오도록 하겠습니다.

위도, 경도, 키워드를 이용한 검색

검색 키워드는 '공영주차장'으로 정했고, 이번에는 검색 범위를 정해봅시다.

대략적으로 범위를 직사각형으로 잡았으면 왼쪽 아래 꼭지점의 위도, 경도 좌표를 www.google.co.kr/maps에 접속해서 확인하겠습니다. 대략 빨간 점의 지점의 좌표는 (33.466496, 126.452614), 오른쪽 위 지점은 (33.528120, 126.574597)

입니다.

 

위도는 33.46에서 33.52로 커졌고, 경도는 126.45에서 126.57로 커졌습니다. 대충 0.01을 기준으로 위도는 6만큼, 경도는 12가 커졌네요.(0.01은 제가 정한 값이고, 더 큰 범위를 검색 하신다면 0.1 혹은 1로 하셔도 무방합니다. 어차피 위, 경도 값이 0.1 or 1인 사각형 내의 결과 값이 46개 이상이면 알아서 4등분 해서 검색해줄 겁니다.

 

이제 다 정해졌습니다. 코딩을 하도록 하겠습니다.

키워드 : 공영주차장

경도 : 126.45(꼭 사각형 왼쪽 아래로 해주세요)

위도 : 35.52(꼭 사각형 왼쪽 아래로 해주세요)

좌표이동 : 0.01

움직일 경도 : 12

움직일 위도 : 6

 

 

import requests
import pandas as pd
import numpy as np
import folium
from folium.plugins import MiniMap
import requests
import folium

 

##카카오 API
def whole_region(keyword, start_x,start_y,end_x,end_y):
    #print(start_x,start_y,end_x,end_y)
    page_num = 1
    # 데이터가 담길 리스트
    all_data_list = []
    
    while(1):
        url = 'https://dapi.kakao.com/v2/local/search/keyword.json'
        params = {'query': keyword,'page': page_num, 
                 'rect': f'{start_x},{start_y},{end_x},{end_y}'}
        headers = {"Authorization": "KakaoAK 본인 rest API키를 입력하세요"}
        ## 입력예시 -->> headers = {"Authorization": "KakaoAK f64acbasdfasdfasf70e4f52f737760657"}
        resp = requests.get(url, params=params, headers=headers)

        search_count = resp.json()['meta']['total_count']
        print('총 개수',search_count)
        
        if search_count > 45:
            print('좌표 4등분')
            dividing_x = (start_x + end_x) / 2
            dividing_y = (start_y + end_y) / 2
            ## 4등분 중 왼쪽 아래
            all_data_list.extend(whole_region(keyword, start_x,start_y,dividing_x,dividing_y))
            ## 4등분 중 오른쪽 아래
            all_data_list.extend(whole_region(keyword, dividing_x,start_y,end_x,dividing_y))
            ## 4등분 중 왼쪽 위
            all_data_list.extend(whole_region(keyword, start_x,dividing_y,dividing_x,end_y))
            ## 4등분 중 오른쪽 위
            all_data_list.extend(whole_region(keyword, dividing_x,dividing_y,end_x,end_y))
            return all_data_list
        
        else:
            if resp.json()['meta']['is_end']:
                all_data_list.extend(resp.json()['documents'])
                return all_data_list
            # 아니면 다음 페이지로 넘어가서 데이터 저장
            else:
                print('다음페이지')
                page_num += 1
                all_data_list.extend(resp.json()['documents'])

 

def overlapped_data(keyword, start_x, start_y, next_x, next_y, num_x, num_y):
    # 최종 데이터가 담길 리스트
    overlapped_result = []

    # 지도를 사각형으로 나누면서 데이터 받아옴
    for i in range(1,num_x+1):   ## 1,10
        end_x = start_x + next_x
        initial_start_y = start_y
        for j in range(1,num_y+1):  ## 1,6
            end_y = initial_start_y + next_y
            each_result= whole_region(keyword, start_x,initial_start_y,end_x,end_y)
            overlapped_result.extend(each_result)
            initial_start_y = end_y
        start_x = end_x
    
    return overlapped_result

 

def make_map(dfs):
    # 지도 생성하기
    m = folium.Map(location=[33.4935,126.6266],   # 기준좌표: 제주어딘가
                   zoom_start=12)

    # 미니맵 추가
    minimap = MiniMap() 
    m.add_child(minimap)

    # 마커
    for i in range(len(dfs)):
        folium.Marker([df['Y'][i],df['X'][i]],    # 제주 어딘가
                  tooltip=dfs['stores'][i],
                  popup=dfs['place_url'][i],
                  ).add_to(m)
    return m

 

데이터 입력해봅시다!

# 시작 x 좌표 및 증가값
keyword = '공영주차장'
start_x = 126.45
start_y = 33.47
next_x = 0.01
next_y = 0.01
num_x = 12
num_y = 6

 

overlapped_result = overlapped_data(keyword, start_x, start_y, next_x, next_y, num_x, num_y)

# 최종 데이터가 담긴 리스트 중복값 제거
results = list(map(dict, collections.OrderedDict.fromkeys(tuple(sorted(d.items())) for d in overlapped_result)))
X = []
Y = []
stores = []
road_address = []
place_url = []
ID = []
for place in results:
    
    X.append(float(place['x']))
    Y.append(float(place['y']))
    stores.append(place['place_name'])
    road_address.append(place['road_address_name'])
    place_url.append(place['place_url'])
    ID.append(place['id'])

    ar = np.array([ID,stores, X, Y, road_address,place_url]).T
    df = pd.DataFrame(ar, columns = ['ID','stores', 'X', 'Y','road_address','place_url'])
    
print('total_reuslt_number = ',len(df))

 

display(df.head())

 

make_map(df)

제주시에 있는 공영주차장 약 270여개소를 다 가져왔습니다. 코드의 작동원리는 간단합니다.

 

만약 위, 경도가 각각 0.01의 길이를 가진 사각형 내에 결과 값이 46개 이상이 있다면, 그 사각형을 4등분 하여 all_data_list배열 안에 사각형 좌표 4개를 각각 저장합니다. 다른 사각형은 결과 값이 45개 이하라면, 그 사각형 좌표를 all_data_list안에 저장합니다. 이제 all_data_list안에 저장된 모든 사각형은 결과 값이 45개 이하이기 때문에, overlapped_data 함수를 이용해 사각형 좌표 모든 결과 값을 가져올 수 있습니다.

 

 

 

활용방안

내가 원하는 지역을 위도, 경도로 정하고, 원하는 것을 검색하면 카카오 API가 모든 정보를 가져다 줄 수 있습니다. 저는 이름, 위도, 경도, 사이트 주소, 도로명 주소만 가지고 왔지만, API로 전화번호, 카테고리정보(합정 스타벅스를 검색했다면 음식점 > 카페 > 스타벅스)도 얻을 수 있습니다.

 

더 나아가 서초구 지역에 모든 CU매장을 찾아낼 수 있고, 서울시 전체의 주유소 위치를 알아낼 수 있고, 강남구에 있는 성형외과의 위치와 전화번호를 모두 가져올 수 있습니다. 지도를 이용한 데이터를 구하기 어려운 분들에게 유용하게 사용될 수 있습니다.

댓글