본문 바로가기

Python/Crawler

Python - 네이버 지도 API를 이용하여 서브웨이 지도 만들기

 지도 시각화를 해본 적이 없어 라이브러리도 익힐겸, API도 사용해볼겸 진행해보았다.

 준비물은 다음과 같다.

- Jupyter Notebook

-  네이버 API 사용자 인증


 

1. 먼저 서브웨이 체인점들의 주소를 크롤링 해온다. 주소는 네이버에 검색해서 크롤링해오는 방법도 있지만, 서브웨이 홈페이지에서 직접 가져왔다.

 보면 2019년 6월 현재 350개의 매장이 존재하며 매장명과 매장주소를 같이 제공하고 있다. 페이지가 넘어가며 url이 바뀌는 편한 구조이기 때문에 크롤링은 쉽다. 머리를 잘 써서 page_number를 지정해주지 않아도 알아서 크롤링 해오게 할 수 있지만 무난하게 페이지를 지정해주었다.

def make_subway_list():
    url = 'http://subway.co.kr/storeSearch?page='
    page_num = 1
    subway_list = []
    
    for j in range(1,36):
        sourcecode = urllib.request.urlopen(url+str(j)).read()
        soup = BeautifulSoup(sourcecode, 'html.parser')
        for i in soup.find_all('div','title'):
            temp_text = i.get_text()
            temp_text = temp_text.replace('\xa0','')
            subway_list.append(temp_text)
    return subway_list

 또한 가져오는 도중 '\xa0'라는 값이 가져와 주소가 제대로 저장되지 않는 경우가 있어 이를 중간에 제거할 수 있도록 해주었다.

 

2. 가져온 주소를 네이버 API를 이용해 위경도로 바꾼다. AI-Application Service로 다량의 호출을 무료로 이용할 수 있다. 

사용한 서비스는 places - Geocoding로 주소의 텍스트를 입력받으면 좌표를 포함한 상세정보를 제공해주는 API이다. JSON으로 돌아오기 때문에 아래와 같은 형식으로 돌아온다.

{
    "status": "OK",
    "meta": {
        "totalCount": 1,
        "page": 1,
        "count": 1
    },
    "addresses": [
        {
        "roadAddress": "경상남도 거제시 연초면 효촌1길 10-1",
        "jibunAddress": "경상남도 거제시 연초면 연사리 93",
        "englishAddress": "10-1, Hyochon 1-gil, Yeoncho-myeon, Geoje-si, Gyeongsangnam-do, Republic of Korea",
        "addressElements": [
            {
            "types": ["SIDO"],
            "longName": "경상남도",
            "shortName": "경상남도",
            "code": ""
            },
            {
            "types": ["SIGUGUN"],
            "longName": "거제시",
            "shortName": "거제시",
            "code": ""
            },
            {
            "types": ["DONGMYUN"],
            "longName": "연초면",
            "shortName": "연초면",
            "code": ""
            },
            {
            "types": ["RI"],
            "longName": "연사리",
            "shortName": "연사리",
            "code": ""
            },
            {
            "types": ["ROAD_NAME"],
            "longName": "효촌1길",
            "shortName": "효촌1길",
            "code": ""
            },
            {
            "types": ["BUILDING_NUMBER"],
            "longName": "10-1",
            "shortName": "10-1",
            "code": ""
            },
            {
            "types": ["BUILDING_NAME"],
            "longName": "",
            "shortName": "",
            "code": ""
            },
            {
            "types": ["LAND_NUMBER"],
            "longName": "93",
            "shortName": "93",
            "code": ""
            },
            {
            "types": ["POSTAL_CODE"],
            "longName": "53209",
            "shortName": "53209",
            "code": ""
            }
        ],
        "x": "128.6521583",
        "y": "34.9070498",
        "distance": 0
        }
    ],
    "errorMessage": ""
}

몇 번의 수정을 거치면 원하는 x, y 좌표값만을 저장할 수 있다. 

def search_map(search_text):
    client_id = 'id'
    client_secret = 'secret'
    encText = urllib.parse.quote(search_text)
    url = 'https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query='+encText
    request = urllib.request.Request(url)
    request.add_header('X-NCP-APIGW-API-KEY-ID',client_id)
    request.add_header('X-NCP-APIGW-API-KEY',client_secret)
    response = urllib.request.urlopen(request)
    rescode = response.getcode()
    if(rescode==200):
        response_body = response.read()
#         print(response_body.decode('utf-8'))
        return response_body.decode('utf-8')
    else:
        print("Error Code:" + rescode)

header에 client_id, client_secret를 넣어 요청하기 때문에 꼭 인증키를 받아야 한다. 

 

3. 받은 x,y 좌표를 주소별로 저장하여 리스트로 만든다. 

def make_location(subway_list):
    x = []
    y = []
    for subway_location in subway_list:
        temp_map = search_map(subway_location)
        temp_map = json.loads(temp_map)
        try:
            temp_map = temp_map['addresses'][0]
            x.append(float(temp_map['x']))
            y.append(float(temp_map['y']))
        except IndexError:
            pass
    return x, y

x, y를 따로 저장하여 넣어주었다. 또한 서브웨이 홈페이지에서 주소가 잘못된 매장이 몇 개 있는데 이를 배제하기 위하여 try/except를 처리해주었다. 실제로 한 건을 처리해보면 다음과 같이 나타난다.

 

4. 이제 지도를 띄우고 마커를 추가해준다. 지도와 마커는 folium 라이브러리를 사용하여 다음과 같은 방식으로 띄운다.

map_osm = folium.Map(location=[37.4729081, 127.039306])
folium.Marker([37.4729081, 127.039306]).add_to(map_osm)
map_osm

여러개의 마커는 여러번 추가해주면 된다.

def make_marker(map_osm, x, y):
    for i in range(len(x)):
        folium.Marker([y[i], x[i]]).add_to(map_osm)

 

5. 강원도에는 서브웨이가 하나도 없는걸로 나타나는데 정말 없을까? 확인해본다.

서브웨이 미용실이 있다.

make_marker(map_osm, x, y)
map_osm
map_osm.save('save.html')

저장하여 나중에도 써먹도록 하자.

subway_map.html
0.27MB