본문 바로가기
Programming/python

[오픈 API] 공공 데이터 주소 활용하여 위도, 경도 좌표 찾기

by 조창대 2022. 7. 26.
반응형

웹 프로젝트를 위하여 국내 무인민원발급기 현황 데이터를 활용하려 했는데 아뿔싸 주소 컬럼은 있는데 위도, 경도가 없다~
전국 무인민원발급기의 위치 정보를 표시하는 지도를 웹 서비스에 넣으려고 했기에 좌표가 꼭 필요했다.
그래서 카카오의 오픈 API를 활용해서 좌표를 뽑았다.


오픈 API

API를 제공하는 기관/웹 사이트에서 여러 사람이 공동으로 사용할 필요가 있는 자원을 공유함으로써 개발자가 이를 활용하며 웹이나 앱을 개발할 수 있게끔 공개한 프로그래밍 인터페이스.
즉, 데이터를 개발에 용이한 형식으로 개방하여 직접 관련 응용 프로그램이나 서비스를 개발할 수 있도록 한다. 기관의 데이터가 변경되면 API로 연결된 이용자도 실시간으로 데이터를 제공받아 이용하는 방식이다.

카카오 API

개인 Key 발급

1. https://developers.kakao.com

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

2. 로그인하고 '내 애플리케이션' 클릭
3. '애플리케이션 추가하기' 진행



4. REST API 키 값 메모장에 복사-저장해두기
5. 'WEB 플랫폼 등록' 클릭


개인 key를 발급받았으면 오픈 api를 활용할 준비는 끝난 셈이다.
개인 key를 이용하여 카카오에 위경도 추출 결과를 요청하는 코드를 작성해보자.

 
1
2
3
4
5
6
7
8
9
import requests, json
import pandas as pd
import time
 
def get_location(address):
    url = 'https://dapi.kakao.com/v2/local/search/address.json?query=' + address
    headers = {"Authorization""KakaoAK 여기에 개인키 입력"}
    api_json = json.loads(str(requests.get(url,headers=headers).text))
    return api_json
cs

address를 매개변수로 갖는 get_location 함수를 정의했다.
url의 ? 뒤 쿼리 스트링 부분에 address 매개변수를 배치한 url을 requests.get 메소드로 요청하고, 응답결과를 text로 받고 json 파일로 저장한 변수가 api_json이다.
그리고 get_location() 함수는 json 파일 형식의 api_json을 반환한다.
예를 들어 get_location("서울특별시 종로구 종로1길 36, 대림빌딩 (수송동)")을 실행하면

 

# api_json.json
{
    "documents": [
        {
            "address": {
                "address_name": "서울 종로구 수송동 146-12",
                "b_code": "1111012400",
                "h_code": "1111061500",
                "main_address_no": "146",
                "mountain_yn": "N",
                "region_1depth_name": "서울",
                "region_2depth_name": "종로구",
                "region_3depth_h_name": "종로1.2.3.4가동",
                "region_3depth_name": "수송동",
                "sub_address_no": "12",
                "x": "126.978988255925",
                "y": "37.5735051436739"
            },
            "address_name": "서울 종로구 종로1길 36",
            "address_type": "ROAD_ADDR",
            "road_address": {
                "address_name": "서울 종로구 종로1길 36",
                "building_name": "종로구청",
                "main_building_no": "36",
                "region_1depth_name": "서울",
                "region_2depth_name": "종로구",
                "region_3depth_name": "수송동",
                "road_name": "종로1길",
                "sub_building_no": "",
                "underground_yn": "N",
                "x": "126.978988255925",
                "y": "37.5735051436739",
                "zone_no": "03152"
            },
            "x": "126.978988255925",
            "y": "37.5735051436739"
        }
    ],
    "meta": {
        "is_end": true,
        "pageable_count": 1,
        "total_count": 1
    }
}


이런 형식의 json 파일이 반환된다.
우리는 여기서 api_json['documents'][0]['address']의 x, y key값을 활용할 것이다.
만약 요청에 오류가 생기면 'documents' 요소가 빈 딕셔너리의 형태로 반환된다.


활용할 공공데이터를 살펴보자.

df_drop.csv
0.36MB

https://www.localdata.go.kr/lif/lifeKMWMapDataView.do?menuNo=40001 LOCALDATA 의 무인민원발급기 전체 데이터를 다운로드하고, 좌표 추출에 필요한 주소 컬럼만 뽑아서 주소가 있는 컬럼의 이름을 addr로 설정하고 csv 파일로 만들어놓은 상태이다.


이대로 for문으로 돌리며 차례대로 request해서 x,y 좌표를 받으면 될 것 같지만 문제가 하나 있다.
데이터 중에 오른쪽 끝에 [ ] 꺽쇄로 감싸진 주소가 추가로 기재된 데이터들이 눈에 띈다. [ ] 로 감싸진 주소는 지번주소로, 도로명 주소 표기법을 이용해서 x, y 좌표를 찾으려 할 때 걸림돌이 된다.

꺽쇄를 기준으로 도로주소와 지번주소를 분리한 다음 지번주소를 데이터에서 삭제해보자.

1
2
3
4
5
6
7
8
test_data = pd.read_csv('data/df_drop.csv', encoding='utf-8')
 
 
for i in range(0,5070,1):
    split=[]
    if test_data['addr'][i].endswith(']'):
        split = test_data['addr'][i].split('['# split은 리스트 반환
        test_data['addr'][i] = split[0]
cs

전체 데이터가 5069개이므로 range의 범위를 이렇게 설정했다.
']'로 끝나는 값이라면 '['를 기준으로 값을 분리하고 '[' 전 데이터와 후 데이터가 요소로 있는 리스트를 반환하도록 하여 도로명 주소가 있는 리스트의 0번째 인덱스만 도로 컬럼 'addr'의 값으로 넣어주었다.


이제 addr 컬럼에서 지번주소를 모두 삭제했으니 주소를 get_location 함수의 인수로 대입해서 x, y 좌표를 추출하는 함수 result_location을 작성해보자.

1
2
3
4
5
6
7
8
9
def result_location(i):
    api_json = get_location(test_data['addr'][i])
    if api_json['documents']:
        address = api_json['documents'][0]['address']
        test_data.loc[i,'x'= address['x']; 
        test_data.loc[i,'y'= address['y']        
    else:
        test_data.loc[i,'x'], test_data.loc[i,'y'= NoneNone
    print(i, '번째 변환 완료...')
cs

result_location의 인수로 정수값 i을 넣어주면 test_data 데이터 프레임의 addr 컬럼의 i 번째 주소값이 호출되어 get_location의 인수로 넘어간다. 이렇게 넘어간 주소값은 get_location의 매개변수가 되어 url의 쿼리스트링에 위치하게 되어 요청에 알맞은 응답을 json 파일로 반환한다.
반환된 api_json은 result_location 함수 내에서 api_json이라는 참조변수에 할당된다.
그 다음 test_data에 x, y 컬럼을 생성하고 각각의 컬럼에 x(경도), y(위도) 값을 넣어주게 된다.
만약, 오류가 생긴다면 x, y 컬럼에 각각 None이 추가된다.

 

1
2
3
4
5
6
7
8
9
10
11
= 0
while i<=len(test_data['addr']):
    try:
        result_location(i)
        i+=1
    except:
        print('time.sleep 적용합니다.')
        time.sleep(2)
        result_location(i)
        i+=1
test_data.to_csv('include_xy.csv', encoding='utf-8-sig')
cs

위에서 정의한 함수를 활용해서 while문을 돌리며 i에 값을 할당하며 주소값을 얻고, 얻은 주소값으로 카카오 api로부터 좌표값을 받는다.
5000개가 넘는 값을 처리하다보니, 중간에 api가 갑자기 응답하지 않을 때가 있어서 try문을 넣어서 응답하지 않을 땐 2초 텀을 두어 원활하게 처리하도록 했다.

좌표값을 받은 test_data를 'include_xy.csv' 파일로 저장할 때, 'utf-8-sig' 인코딩 방식은 엑셀에서 파일을 열 때도 한글이 깨지지 않으니 'utf-8'과 함께 기억해두면 좋다.

 

* 결과 *

'include_xy.csv' 파일을 열어보면 이렇게 x, y 좌표가 잘 추출된 걸 확인할 수 있다.

 


* 전체보기 *

 
 
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
37
38
39
40
41
42
43
44
45
46
import requests, json
import pandas as pd
import time
 
 
def get_location(address):
    url = 'https://dapi.kakao.com/v2/local/search/address.json?query=' + address
    # 'KaKaoAK '는 그대로 두시고 개인키만 지우고 입력해 주세요.
    # ex) KakaoAK 6af8d4826f0e56c54bc794fa8a294
    headers = {"Authorization""KakaoAK 18fa7c3977bc313b0659499b554180de"}
    api_json = json.loads(str(requests.get(url,headers=headers).text))
#   address = api_json['documents'][0]['address']
    return api_json
 
def result_location(i):
    api_json = get_location(test_data['addr'][i])
    if api_json['documents']:
        address = api_json['documents'][0]['address']
        test_data.loc[i,'x'= address['x']; 
        test_data.loc[i,'y'= address['y']        
    else:
        test_data.loc[i,'x'], test_data.loc[i,'y'= NoneNone
    print(i, '번째 변환 완료...')
 
# -------------------------------------------------------------------
test_data = pd.read_csv('data/df_drop.csv', encoding='utf-8')
 
 
for i in range(0,5070,1):
    split=[]
    if test_data['addr'][i].endswith(']'):
        split = test_data['addr'][i].split('['# split은 리스트 반환
        test_data['addr'][i] = split[0]
 
 
= 0
while i<=len(test_data['addr']):
    try:
        result_location(i)
        i+=1
    except:
        print('time.sleep 적용합니다.')
        time.sleep(2)
        result_location(i)
        i+=1
test_data.to_csv('include_xy.csv', encoding='utf-8-sig')
cs
반응형