본문 바로가기
프로젝트/봉사왕심봉사

[naver/google geocoding] 위도/경도 값 구하기

by 윤싱찬 2018. 9. 3.

지오코딩이 뭔가요?

 

지오코딩(Geocoding)은 고유명칭(주소나 산,호수의 이름등)을 가지고 위도와 경도의 좌표값를 얻는 것을 말한다. 이처럼 고유명칭이나 개별이름등을 가지고 검색하는것과는 달리 반대로 위도와 경도값으로부터 고유명칭을 얻는것은 리버스 지오코딩(reverse Geocoding)이된다. [1][2]

일반적으로 지도상의 좌표는 위도,경도의 순서로 좌표값을 가지나 GeoJSON처럼 경도,위도의 순서로 좌표값을 표현하는 경우도 있다.[3]

또한 [위도,경도,고도]의 GCS처럼 위도와 경도의 좌표에 고도 값을 지원하는 지오코딩를 사용하는 경우도 있다.[4]

 

=========

 

이상 위키백과의 설명이다.

우리가 손쉽게 사용할 수 있는 것은 google geocoding api나 naver api를 사용하는 것이다.

두 가지 방법을 설명하고 어떤 것을 사용할지 정해보자.

 

1. 구글 지오코딩

https://developers.google.com/maps/documentation/geocoding/intro

공식문서 참고!

 

크게 xml방식과 json 방식으로 결과 값을 받을 수 있는데, 우리는 beatifulsoup을 사용하여

xml을 활용하는 것이 더 익숙하기에, xml로 받아오기 작업!

 

그리고 테스트를 해보자.

주소창에 http://maps.googleapis.com/maps/api/geocode/xml?address=천안시라고 쳐보면 아래의 xml값 형태로 잘 나오는 것을 확인할 수 있다.

그 중 봉사왕 심봉사 프로젝트에 필요한 것은 <geometry> 안의 <location>의 <lat>과 <lng>beatifulsoup으로 가져와보자.

1
2
3
4
5
6
7
8
9
import requests
from bs4 import BeautifulSoup
 
headers = {"accept-agent":"Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko)\
    Version/11.0 Mobile/15A5341f Safari/604.1"}
base_url = "http://maps.googleapis.com/maps/api/geocode/xml?address="
url = base_url+"천안시"
resp = requests.get(url, headers=headers)
html = BeautifulSoup(resp.text, "lxml")
cs


header를 내 환경으로 바꿔주고 위의 xml파일을 읽어와보자!

주소창으로 들어갔을 때와 같은 값이 출력이 된다.

1
2
lat = html.select("location > lat")
lng = html.select("location > lng")
cs

 


위의 방식으로 해당 값을 뽑아보면,

리스트 형태로 출력이 된다.

그러기에 아래와 같은 방법으로 값을 뽑는다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
from bs4 import BeautifulSoup
 
headers = {"accept-agent":"Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko)\
    Version/11.0 Mobile/15A5341f Safari/604.1"}
base_url = "http://maps.googleapis.com/maps/api/geocode/xml?address="
url = base_url+"천안시"
resp = requests.get(url, headers=headers)
html = BeautifulSoup(resp.text, "lxml")
lat = html.select("location > lat")
lng = html.select("location > lng")
print(lat[0].get_text())
print(lng[0].get_text())
 
cs

여기까지가 단순 값을 가져오는 코드.
2. 네이버 지오코딩
공식 문서에서 제공해준 코드https://developers.naver.com/docs/map/overview/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 본 예제는 Python에서 주소좌표변환 api를 호출하는 예제입니다.
import os
import sys
import urllib.request
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
encText = urllib.parse.quote("천안시")
url = "https://openapi.naver.com/v1/map/geocode?query=" + encText # json 결과
# url = "https://openapi.naver.com/v1/map/geocode.xml?query=" + encText # xml 결과
request = urllib.request.Request(url)
request.add_header("X-Naver-Client-Id",client_id)
request.add_header("X-Naver-Client-Secret",client_secret)
response = urllib.request.urlopen(request)
rescode = response.getcode()
if(rescode==200):
    response_body = response.read()
    print(response_body.decode('utf-8'))
else:
    print("Error Code:" + rescode)
cs


client_id와 client_secret은 네이버 개발자페이지에서 api키를 받아서 넣어주면 된다.네이버는 결과 값이 json으로 출력된다.xml 사용하려면 아래 주석풀고 사용!


여기서 그럼 간단하게 json값을 가져오면 되겠네~ 라고 생각을 했었지만,여기서 문제가 2가지가 있다.
1. 저 결과 값 타입이 json이 아닌 string이라는 것,2. 파이썬에는 true, false가 아닌 True, False라는 점
그래서 isRoadAddress 값을 대문자로 바꿔주고,전체를 다시 json으로 바꾼 후에 값을 가져와야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import os
import sys
import urllib.request
client_id = "ZbNWupFQAk_cmBAmLstr"
client_secret = "5_knjXBU5Z"
encText = urllib.parse.quote("천안시")
url = "https://openapi.naver.com/v1/map/geocode?query=" + encText # json 결과
# url = "https://openapi.naver.com/v1/map/geocode.xml?query=" + encText # xml 결과
request = urllib.request.Request(url)
request.add_header("X-Naver-Client-Id",client_id)
request.add_header("X-Naver-Client-Secret",client_secret)
response = urllib.request.urlopen(request)
rescode = response.getcode()
if(rescode==200):
    response_body = response.read()
    mystr = response_body.decode('utf-8')
    # false, true를 대문자로 replace를 해주고,
    mystr = mystr.replace('true',"True")
    mystr = mystr.replace('false',"False")
    
    # string -> json 타입으로 바꾸자
    mydic = eval(mystr)
    
    # 차례대로 끼워맞추다 보면 아래의 값으로 출력 할 수 있다.
    print(mydic['result']['items'][0]['point']['y'])
    print(mydic['result']['items'][0]['point']['x'])
else:
    print("Error Code:" + rescode)
cs

 

 

3. 평가

 

코드의 길이와 단순함은 구글 지오코딩이 더 좋다.

또한 다양한 값으로 테스트를 해본 결과 구글은 일치하는 주소가 없어도 유연성 있게 잘 찾아주지만, 네이버는 그런 것조차 용납하지 않는다.

주소값에 일치하는 것이 없으면 에러가 나는데 에러 빈도율이 네이버가 훨씬 많았다.

 

예를들어 띄어쓰기가 적용이 안된 "명지대학교자연캠퍼스" 라고 구글과 네이버 api에 직접 넣어봤더니,

 

 

이래서 조금더 유연성 있는 구글맵을 사용하였다.

 

 

4. 활용

 

우리 코드에 적용 시키기 위해 pandas로 1365 데이터 크롤링 한 것에서 '주소' 컬럼의 값을 가져온 후에,

loc 함수로 지오코딩을 통해 받은 위도, 경도 값을 넣어준다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
 
df = pd.read_csv('1365_continue.csv',encoding='ansi')
 
# 주소 값에서 너무 자세한 주소를 넣으면 안나올 가능성이 높아,
# 큰 단위로 잘라서 붙여서 addrlist 에 저장하였다.
 
addrlist = []
for i in range(len(df)):
    addr = df['주소']
    splitdata = addr[i].split(' ')
    if '' in splitdata:
        si = splitdata.index('')
        del splitdata[si]
    print(' '.join(splitdata[0:4]))
    addrlist.append(' '.join(splitdata[0:4]))
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import requests
from bs4 import BeautifulSoup
 
headers = {"accept-agent":"Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko)\
    Version/11.0 Mobile/15A5341f Safari/604.1"}
base_url = "http://maps.googleapis.com/maps/api/geocode/xml?address="
 
for i in range(len(df)):
    url = base_url+addrlist[i]
    resp = requests.get(url, headers=headers)
    html = BeautifulSoup(resp.text, "lxml")
    try:
        lat = html.select("location > lat")
        lng = html.select("location > lng")
        print(lat[0].get_text())
        print(lng[0].get_text())
        df.loc[i, '위도'= lat[0].get_text()
        df.loc[i, '경도'= lng[0].get_text()        
        
    # 만약 주소 값으로 정확한 값이 출력이 안된다면, '모집기관'의 주소로 다시 api를 쏴준다.
    
    except:
        base_url = "http://maps.googleapis.caom/maps/api/geocode/xml?address="
        url = base_url+df['모집기관'][i]
        resp = requests.get(url, headers=headers)
        html = BeautifulSoup(resp.text, "lxml")
        try:
            lat = html.select("location > lat")
            lng = html.select("location > lng")
            print(lat[0].get_text())
            print(lng[0].get_text())
            df.loc[i, '위도'= lat[0].get_text()
            df.loc[i, '경도'= lng[0].get_text()
            print('\n\n')
        except:
            pass
cs

 

잘 저장되었으면

1
2
# 저장
df.to_csv('aaa.csv',encoding='ansi')
cs

 

 

로 저장하면 끝!