Data/Python

[OPEN API] 행정안정부 도로명주소, 지번주소, 영문, 우편번호 변환(API 활용방법, 키발급,Python 예제 코드, 수집)

뚱요 2024. 1. 30. 10:08
반응형

 

도로명 주소 검색, 영문 주소 변환., 상세주소 내역, 지도를 행정안전부에서 주소기반산업지원 서비스의 하나로 OPEN API를 제공하고 있습니다. 
실무에서 지번 주소를 도로명 주소로 일관 전환 및 영문주소로 변환해야 할 일이 생겼었고 작성한 내용이 도움 되었으면 하여 작성한 코드 공유합니다. 참고로 지속적으로 사용하는 코드가 아니라서 단순하게 시퀀셜 한 줄글 코드로 작성하였습니다.
공공데이터 포털 API 사용법이 궁금하시다면 하단의 링크를 확인해 주세요!
[Data/DB] - [OpenAPI] 공공데이터 포털 API 사용법

 

1.0 사용 방법

1) 원하는 API를 키발급 신청하기

  • API 종류 : 도로명주소/영문주소/좌표제공/상세주소/지도 제공 API 중 택 1
  • API 유형 :팝업/검색 API 중 택 1
  • 나머지는 개인에 맞춰 선택 후 신청하기 클릭하기

여기 예시에서는 영문주소 검색 API를 신청하였다.

2) 발급된 승인키 복사

완료 시 신청내역 상세에 해당 사용 기간 동안만 유효한 승인키가 발급됩니다. 이 승인키 전체를 복사해 두면 요청 시에 사용합니다.

 

2.0 Open API 활용 - Python Code

코드는 실제 주소에서 먼저 regular expression을 통해서 전처리를 해줍니다.
여기서 불필요한 상세내용을 삭제하고 시구로 까지만 가져왔습니다.

1) API 요청 보내기 위한 정보 확인

영문주소 검색 API 정보 확인 시 API 신청란에 신청한 팝업/검색 API 중 도로명주소, 영문주소, 좌표제공, 상세주소, 지도제공 선택 후 API정보/요청변수/출력결과보기에서 요청 URL과 요청 변수 확인해야 기본적으로 요청을 할 수 있습니다.
 https://business.juso.go.kr/addrlink/openApi/searchApi.do

API 정보 확인 시 요청 URL과 요청 변수 필수 값을 활용합니다.
요청 URL  : https://business.juso.go.kr/addrlink/addrEngApiJsonp.do ,  https://business.juso.go.kr/addrlink/addrEngApi.do 중 출력 결과에 따라 택 1
요청변수명 : confmKey, currentPage, countPerPage, keyword, resultType 중 앞 4가지는 필수로 지정

이제 이 확인한 API 정보와 요청변수, 출력결과를 보고 코드에 적절하게 작성해야 합니다.

2) API 요청 보내기

설치가 되지 않았다면 하단의 코드로 먼저 설치합니다.

~$ pip3 install requests

 

requests 모듈을 이용하여 get 메소드로 해당 url에 필수 파라미터 값을 보내어 요청합니다. 파라미터값으로  https://business.juso.go.kr/addrlink/addrEngApi.do url에 1페이지에 1개의 값만 json 형태로 받기로 요청을 했습니다.

json은 딕셔너리 자료형과 형태가  유사하고 xml은 html처럼 태그 안에 되어 있습니다.

요청받은 내용을 print() 하면 텍스트 형태로 출력해보면 3) API 응답값 해석하기와 같이 나옵니다.

pprint는 텍스트 형태의 데이터를 가독성 있게 출력해 주는 모듈입니다.
import requests
import pprint

url='https://business.juso.go.kr/addrlink/addrEngApi.do'
api_key= /* 여기에 string 값으로 API 키 값 할당*/
주소 = '30112 세종특별자치시 도움6로 42' /* 여기에 주소 입력 */ 
param={
    'confmKey':api_key,
    'currentPage':'1',
    'countPerPage':'1',
    'keyword':주소,
    'resultTyepe':'json'
    }
req= requests.get(url,param)
print(req)

pp = pprint.PrettyPrinter(indent=4)
pp.pprint(req.text)

3) API 응답값 해석하기

'<?xml version="1.0" '
 'encoding="UTF-8"?><results><common><totalCount>1</totalCount><currentPage>1</currentPage><countPerPage>1</countPerPage><errorCode>0</errorCode><errorMessage>정상</errorMessage></common><juso><roadAddr><![CDATA[42 '
 'Doum 6-ro, Sejong-si]]></roadAddr><jibunAddr><![CDATA[572 Eojin-dong, '
 'Sejong-si]]></jibunAddr><zipNo><![CDATA[30112]]></zipNo><admCd><![CDATA[3611011000]]></admCd><rnMgtSn><![CDATA[361103258085]]></rnMgtSn><bdKdcd><![CDATA[0]]></bdKdcd><siNm><![CDATA[Sejong-si]]></siNm><sggNm><![CDATA[]]></sggNm><emdNm><![CDATA[Eojin-dong]]></emdNm><liNm><![CDATA[]]></liNm><rn><![CDATA[Doum '
 '6-ro]]></rn><udrtYn><![CDATA[0]]></udrtYn><buldMnnm><![CDATA[42]]></buldMnnm><buldSlno><![CDATA[0]]></buldSlno><mtYn><![CDATA[0]]></mtYn><lnbrMnnm><![CDATA[572]]></lnbrMnnm><lnbrSlno><![CDATA[0]]></lnbrSlno><emdNo><![CDATA[02]]></emdNo><korAddr><![CDATA[세종특별자치시 '
 '도움6로 42]]></korAddr></juso></results>'

위의 코드를 돌리면 하단의 이미지와 같은 응답값이 나옵니다. 해석하면 Response [200]으로 정상적으로 응답하였으며 <common> 태그 안 총 검색 데이터 수, 페이지 번호, 페이지당 출력할 결과 수, 에러코드, 에러메시지가 나옵니다.
그다음 <juso> 태그 안에 영문 도로명주소, 지번주소, 우편번호 등이 담겨 있다.  응답받은 값 확인 시 검색한 주소의 첫 페이지 중 1개를 json 형식으로 받았습니다. 그리고 그 내부에는 영문주소가 <roadAddr> 정보와 <zipNo> 우편번호 등의 정보가 태그 안에 들어있는 것을 알 수 있습니다. 그렇다면 이제 juso태그 안에 있는 필요한 정보만 가져오면 됩니다.

4) 응답값으로 영문주소 가져오기

BeautifulSoup라는 HTML, XML 문서를 위한 파이썬 패키지를 활용해서 데이터를 활용해서 태그 안에 값을 가져옵니다. 응답값의 lxml-xml 형식으로 객체를 가져와서 <roadAddr> 객체를 모두 가져옵니다. 이것을 확인해 보면 태그와 그 안의 텍스트가 같이 나옵니다.

여기서 'lxml-xml' 은 파서(parser)로 브라우저는 HTML 언어로 쓰인 문자열을 DOM TREE 라는걸로 변환하게 되는데 이 파서 는 DOM TREE 결과를 가져오는 속도와 방식의 차이로 볼 수 있습니다. 

https://www.crummy.com/software/BeautifulSoup/bs4/doc/#differences-between-parsers

from bs4 import BeautifulSoup

xml_obj = bs4.BeautifulSoup(req.text,'lxml-xml')
rows = xml_obj.findAll('roadAddr')
rows

 

텍스트만 필요한 경우 위의 값에서 getText() 통해서 안의 텍스트만 가져옵니다. 

rows.getText()

이런 식으로 원하는 xml 태그에서 텍스트 값을 가져올 수 있습니다. 이제 이것을 응용해서 원하는 값들을 엑셀 형식으로 넣습니다.

3.0 Open API 실전예제 - Python Code

import pandas as pd
import requests
import bs4

# tmpdf=pd.read_excel("directory\\엑셀파일명.xlsx")

tmpdf = pd.DataFrame( {
    "도로명주소":[ "대전광역시 서구 청사로 189 정부대전청사 3동","30112 세종특별자치시 도움6로 42 (어진동)"]})
tmpdf['도로명주소']=tmpdf.도로명주소.str.extract(r'^((?:[\w0-9 ]+){2,6}[동|로|길]{1}[ ]{0,2}[0-9]{0,4}[-0-9]{1,4})',expand=False)
tmpdf.도로명주소=tmpdf.도로명주소.str.replace(',','')
tmpdf.도로명주소

tmpdf라는 객체에 주소 데이터프레임 형태로 바꾸어 준다. 다만 도로명 주소 검색 시 행정구역도(시군구, 읍면동, 리) 이외 값이 들어오는 경우 에러가 발생하는 경우가 있어 정규표현식(regular expression) 통해서 불필요한 내용을 제거해 줍니다.
                  예. 대전광역시 서구 청사로 189 정부대전청사 3동  >  대전광역시 서구 청사로 189

url='https://business.juso.go.kr/addrlink/addrEngApi.do'
api_key='발급한 API 붙여넣기'
arr_eng=[]
arr_zip=[]

param={
    'confmKey':api_key,
    'currentPage':'1',
    'countPerPage':'1',
    'resultTyepe':'json',

}
    
for 상세주소 in tmpdf['도로명주소'] :
    param['keyword']=상세주소
    print(상세주소)

    req=requests.get(url,param)
    print(req)
    print(req.text)

    xml_obj = bs4.BeautifulSoup(req.text,'lxml-xml')
    영문주소 = xml_obj.findAll('roadAddr')
    우편번호 = xml_obj.findAll('zipNo')
    print(영문주소)
    if 영문주소 != '' or 우편번호 != '':
        arr_eng.append(영문주소)
        arr_zip.append(우편번호)
    else: pass
tmpdf['주소(영문)']=arr_eng
tmpdf['arr_zip']=arr_zip

그리고 위에 작성된 코드를 for문을 통해서 하나하나 request 하여 응답값 중 필요한 값만 가져와서 arr_eng, arr_zip에 하나씩 추가하여 줍니다. tmpdf 데이터프레임의 도로명 주소 내 주소를 한 번씩 다 훑으면 for문은 종료됩니다. 종료된 후 arr_eng, arr_zip 값을 tmpdf 데이터프레임에 추가해 주면 하단과 같이 테이블이 됩니다.

하단은 결과 같은 위의 코드를 쉽게 이해할 수 있도록 print를 통해 디버깅한 내용입니다. 도로명주소의 값이 하나씩 for문으로 들어와서 요청 보내고 그에 상응하는 응답값에서 필요한 정보만 beatifulsoup 패키지 통해서 가져온 것을 알 수 있습니다.

대전광역시 서구 청사로 189
<Response [200]>
<?xml version="1.0" encoding="UTF-8"?><results><common><totalCount>2</totalCount><currentPage>1</currentPage><countPerPage>1</countPerPage><errorCode>0</errorCode><errorMessage>정상</errorMessage></common><juso><roadAddr><![CDATA[189 Cheongsa-ro, Seo-gu, Daejeon]]></roadAddr><jibunAddr><![CDATA[920 Dunsan-dong, Seo-gu, Daejeon]]></jibunAddr><zipNo><![CDATA[35208]]></zipNo><admCd><![CDATA[3017011200]]></admCd><rnMgtSn><![CDATA[301703166051]]></rnMgtSn><bdKdcd><![CDATA[0]]></bdKdcd><siNm><![CDATA[Daejeon]]></siNm><sggNm><![CDATA[Seo-gu]]></sggNm><emdNm><![CDATA[Dunsan-dong]]></emdNm><liNm><![CDATA[]]></liNm><rn><![CDATA[Cheongsa-ro]]></rn><udrtYn><![CDATA[0]]></udrtYn><buldMnnm><![CDATA[189]]></buldMnnm><buldSlno><![CDATA[0]]></buldSlno><mtYn><![CDATA[0]]></mtYn><lnbrMnnm><![CDATA[920]]></lnbrMnnm><lnbrSlno><![CDATA[0]]></lnbrSlno><emdNo><![CDATA[01]]></emdNo><korAddr><![CDATA[대전광역시 서구 청사로 189]]></korAddr></juso></results>
[<roadAddr>189 Cheongsa-ro, Seo-gu, Daejeon</roadAddr>]
30112 세종특별자치시 도움6로 42
<Response [200]>
<?xml version="1.0" encoding="UTF-8"?><results><common><totalCount>1</totalCount><currentPage>1</currentPage><countPerPage>1</countPerPage><errorCode>0</errorCode><errorMessage>정상</errorMessage></common><juso><roadAddr><![CDATA[42 Doum 6-ro, Sejong-si]]></roadAddr><jibunAddr><![CDATA[572 Eojin-dong, Sejong-si]]></jibunAddr><zipNo><![CDATA[30112]]></zipNo><admCd><![CDATA[3611011000]]></admCd><rnMgtSn><![CDATA[361103258085]]></rnMgtSn><bdKdcd><![CDATA[0]]></bdKdcd><siNm><![CDATA[Sejong-si]]></siNm><sggNm><![CDATA[]]></sggNm><emdNm><![CDATA[Eojin-dong]]></emdNm><liNm><![CDATA[]]></liNm><rn><![CDATA[Doum 6-ro]]></rn><udrtYn><![CDATA[0]]></udrtYn><buldMnnm><![CDATA[42]]></buldMnnm><buldSlno><![CDATA[0]]></buldSlno><mtYn><![CDATA[0]]></mtYn><lnbrMnnm><![CDATA[572]]></lnbrMnnm><lnbrSlno><![CDATA[0]]></lnbrSlno><emdNo><![CDATA[02]]></emdNo><korAddr><![CDATA[세종특별자치시 도움6로 42]]></korAddr></juso></results>
[<roadAddr>42 Doum 6-ro, Sejong-si</roadAddr>]

 

반응형