본문 바로가기
웹 크롤링

웹 크롤링 - [Python]파이썬으로 카카오 맵 API 사용하기(1) - 키워드 검색

by haries 2021. 3. 30.

목표

카카오 맵 API를 사용하여 검색한지점의 위도와 경도를 얻고,

folium으로 지도에 결과 값을 좌표를 찍겠습니다.

 

 

 

 

먼저 KAKAO DEVELOPER(developers.kakao.com)에서 4가지 Key를 발급받았고 이렇게

플랫폼(Android, IOS, Web) 중에 Web을 만든 상태에서 진행을 합니다. Web을 만들 때 Redirect URL을 입력해야 합니다.  저는 192.168.0.2:9999로 했고, 그냥 제가 아이패드에서도 주피터 노트북 쓰려고 열어놓은 내부 서버 IP입니다. (당연히 바깥에서 쓸 때는 외부 서버로 씁니다.)

주피터 서버를 열지 않으신 분들은 http://localhost:3000(개발자 임시 서버)으로 적으셔도 됩니다.

그러면 모든 준비가 됐네요.

 

 

 

카카오 맵 API 특징

카카오 맵 API는 검색 할 때, 최대 45개의 결과 값만 알려줍니다.. 이게 무슨 말이냐?

'제주도 전기충전소'로 검색을 하면 수많은 파란 점들과 알파벳 문자를 볼 수 있습니다. 카카오 API는 파란 점들은 검색해주지 않고 알파벳으로 띄워진 정보들만 알려줍니다. 이게 최대가 45개입니다.

 

그러니까 검색을 했을 때, 결괏값이 45개가 넘어버리면 46번째 검색 결과부터 확인이 불가능합니다. 본인이 검색하려는 게 작은 범주면 ( Ex) 서울대입구역 홍콩반점) 상관이 없는데, '동작구 CU ' 로 검색하면 다 찾아주지 못합니다.

 

 

 

 

따라서 해결하는 방법은 2가지인데, 하나는 검색어를 구체적으로 검색해서 45개 이내로 만들거나(일반적인 검색 방법임), 특정 구역을 내가 좌표로 지정해서, 그 안에서 검색하는 방법이 있습니다. 오늘은 첫 번째 방법으로 코딩해보겠습니다.

 

1) 특정 단어 검색(일반적인 카카오 API 사용법)

키워드는 '성산일출봉 전기충전소'로 하겠습니다.

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

 

##카카오 API
## region에는 '성산일출봉 전기충전소' 검색명이 들어갈 것임
## page_num은 1~3이 입력될 건데, 한 페이지 당 검색목록이 최대 15개임.
## 만약 page_num이 4이상이 되면 3페이지랑 같은 15개의 결과 값을 가져옴. 그래서 1~3만 쓰는 것임
## 입력 예시 -->> headers = {"Authorization": "KakaoAK f221u3894o198123r9"}
## ['meta']['total_count']은 내가 '성산일출봉 전기충전소'를 검색했을 때, 나오는 총 결과 값. 
## ['meta']['total_count']이 45보다 크면 45개만 가져오게 됨


def elec_location(region,page_num):
    url = 'https://dapi.kakao.com/v2/local/search/keyword.json'
    params = {'query': region,'page': page_num}
    headers = {"Authorization": "KakaoAK 여기다가 본인의 REST API 키"}

    places = requests.get(url, params=params, headers=headers).json()['documents']
    total = requests.get(url, params=params, headers=headers).json()['meta']['total_count']
    if total > 45:
        print(total,'개 중 45개 데이터밖에 가져오지 못했습니다!')
    else :
        print('모든 데이터를 가져왔습니다!')
    return places

 

## 이 함수는 위 함수 결과 값(1 ~ 45개) 하나하나 분리해서 저장할 것임
## 1번 결과 값 안에는 1번 충전소 이름, 위도, 경도, 전화번호, 도로명 주소 등이 있는데 각각 배열에 저장
## 우리는 충전소 ID, 충전소 이름, 위도, 경도, 도로명주소, 사이트주소를 저장할 것임

def elec_info(places):
    X = []
    Y = []
    stores = []
    road_address = []
    place_url = []
    ID = []
    for place in places:
        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'])
    return df

 

## 여러개으 키워드를 검색할 때 사용할 함수임
## location_name에는 ['성산일출봉 전기충전소, '한림공원 전기충전소', ... ,'모슬포 전기충전소']처럼 배열이 입력

def keywords(location_name):
    df = None
    for loca in location:
        for page in range(1,4):
            local_name = elec_location(loca, page)
            local_elec_info = elec_info(local_name)

            if df is None:
                df = local_elec_info
            elif local_elec_info is None:
                continue
            else:
                df = pd.concat([df, local_elec_info],join='outer', ignore_index = True)
    return df

 

##지도로 보여주기

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

 

 

 

위의 4개의 함수를 주피터로 실행하고 아래 코드에 원하는 키워드를 입력하면 지도에 보여줍니다.

## 여기 두 개 키워드처럼 가까운 거리에 있는 키워드를 입력하면 
## 중복해서 전기충전소를 검색할 가능성이 아주 놓기 때문에
## drop_duplicates를 해주고 인덱스 리셋을 해준다

location = ['성산일출봉 전기충전소', '광치기해수욕장 전기충전소']
df = keywords(location)
df = df.drop_duplicates(['ID'])
df = df.reset_index()

make_map(df)

이렇게 '성산일출봉 전기충전소', '광치기 해수욕장 전기충전소'에 있는 전기충전소를 가져왔습니다. 실제로 68개가 있지만 len(df)를 실행해본 결과 47개만 가져왔네요. 

 

다음 포스트에서는 위도와 경도를 이용해, 특정 구역에 뜨는 모든 결과 값을 가져오도록 하겠습니다. 

 

"코드 작동 테스트용"

import requests 
url = 'https://dapi.kakao.com/v2/local/search/keyword.json' 
params = {'query': "성산일출봉 전기충전소",'page': 1} 
headers = {"Authorization": "KakaoAK f64acb1ae8c664570e4f52f73776065744"}
places = requests.get(url, params=params, headers=headers).json()['documents']
total = requests.get(url, params=params, headers=headers).json()['meta']['total_count'] 
if total > 45: 
    print(total,'개 중 45개 데이터밖에 가져오지 못했습니다!') 
else : 
    print('모든 데이터를 가져왔습니다!') 
    print(places)

댓글