[공공데이터 포털 Open API] - Python을 활용해서 공공데이터 포털 국토교통부 데이터 로드 (글자 깨짐 방지)

2020. 11. 23. 10:22Project/지하철 데이터를 활용한 서울 상가거래 예측

from lxml import html
from urllib.parse import urlencode, quote_plus, unquote
import xml.etree.ElementTree as ET
import requests, bs4
import pandas as pd


#   parameter for request
#   pageNo를 다르게 주어도 다른데이터가 발생하지 않는 것을 확인
def download_open_api(YMD,CODE,pageNo = 1):
    
    # 1. URL 파라미터 분리하기.
    # Service URL
    xmlUrl = 'http://openapi.molit.go.kr/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcNrgTrade'
    My_API_Key = unquote('개인 서비스키')
    
    queryParams = '?' + urlencode(    # get 방식으로 쿼리를 분리하기 위해 '?'를 넣은 것이다. 메타코드 아님.
        {
            quote_plus('serviceKey') : My_API_Key,    # 필수 항목 1 : 서비스키 (본인의 서비스키)
            quote_plus('LAWD_CD') : CODE,          # 필수 항목 2 : 지역코드 (법정코드목록조회에서 확인)
            quote_plus('DEAL_YMD') : YMD,             # 픽수 항목 3 : 계약월
            quote_plus('pageNo') : pageNo
        }
    )
    response = requests.get(xmlUrl + queryParams)
    response.encoding = 'utf-8'
    return response.text

 

 위의 function은 아래의 출력결과를 return한다. 공공데이터 포털에 존재하는 코드 예시 방식으로 했을 때, 글자가 모두 깨지고 byte형태의 return을 보여주어 이를 수정하느라 많은 고생을 했다. 

 아래의 return형태는 xml이다. 

 

출력결과

 

 나는 국토교통부가 제공하는 201501부터 201912까지 상업업무용 데이터를 모두 가져오고 싶었다. 데이터마다 다르겠지만, pageNo가 공공데이터 포털에 parameter로 나와있지 않다. 하지만, 데이터에 pageNo가 존재하고, 추가적인 데이터가 있는지 확인할 필요가 있었다. 그리고 이러한 데이터의 누락이 발생하길 원하지 않았다.

 => 그 결과 추가적인 데이터가 있는 페이지도 존재했지만, 추가적인 데이터가 없더라도 pageNo는 2로 올라갔다. 그리고 이 경우 중복데이터를 return하는 결과를 보였다. 그렇기에 하나하나 손수 확인을 할 수 없어 page3까지 모두 다운받고 중복제거를 진행하였다. 이때, pageNo = 3에서는 더이상 중복제거시 추가되는 데이터가 없음을 확인할 수 있었다.

 이 과정은 데이터프레임을 형성 후에 확인해보자. 

 

 먼저 데이터프레임으로 만들어보자

 

법정동코드 조회자료.xls
0.05MB

 이 데이터는 서울의 법정동 코드 조회자료이다. 

서울 이외 지역의 법정동 코드가 필요하다면, www.code.go.kr/stdcode/regCodeL.do

 

법정동코드목록조회 - 행정표준코드관리시스템

 

www.code.go.kr

에서 조건에 맞는 csv file을 가져오길 바란다. 국토교통부 API의 경우 법정동코드 5자리를 쓰기에, 법정동코드의 분리가 필요하다. 

 

# dataframe의 colname
colname = ['년','월','일','건축년도','건물면적','대지면적','거래금액','건물주용도','유형','시군구','법정동','용도지역']

# 모든 서울 내 법정동 코드 가져오기 
df_code = pd.read_excel('법정동코드 조회자료.xls',encoding = 'cp949')
df_code = df_code.astype('str')
df_code['법정동코드'] = df_code['법정동코드'].apply(lambda x : x[:5])
df_code.drop(0,inplace = True)
code_list = list(df_code['법정동코드'].unique())

# 데이터를 담을 dataframe
df = pd.DataFrame(columns=colname)


# 일부 
DEAL_YMD = [201501,201502,201503,201504,201505,201506,201507,201508,201509,201510,201511,201512,
            201601,201602,201603,201604,201605,201606,201607,201608,201609,201610,201611,201612,
            201701,201702,201703,201704,201705,201706,201707,201708,201709,201710,201711,201712,
            201801,201802,201803,201804,201805,201806,201807,201808,201809,201810,201811,201812,
            201901,201902,201903,201904,201905,201906,201907,201908,201909,201910,201911,201912]

for YMD in DEAL_YMD[:] :    # 중간에 OpenAPI의 접속이 제한되었을 때, 중간부터 시작하기 위한 index 
    print('------- start {} -------'.format(YMD))
    for CODE in code_list : 
        data= download_open_api(YMD,CODE,pageNo=1)

        for item_line in ET.fromstring(data).find("body").find("items").findall("item") :
            tmp_dict = {'년':item_line.findtext('년'),
                        '월':item_line.findtext('월'),
                        '일':item_line.findtext('일'),
                        '건축년도':item_line.findtext('건축년도'),
                        '건물면적':item_line.findtext('건물면적'),
                        '대지면적':item_line.findtext('대지면적'),
                        '거래금액':item_line.findtext('거래금액'),
                        '건물주용도':item_line.findtext('건물주용도'),
                        '유형':item_line.findtext('유형'),
                        '시군구':item_line.findtext('시군구'),
                        '법정동':item_line.findtext('법정동'),
                        '용도지역':item_line.findtext('용도지역')
                       }
            df = df.append(tmp_dict,ignore_index=True)
    print('------- end {} -------'.format(YMD))

 

다음은 dataframe의 결과이다. 

이는 pageNo = 1의 데이터 결과이다. 그렇기에 전체 데이터를 가져온 것이 아니었다. 

 

for YMD in DEAL_YMD[:] :    # 중간에 OpenAPI의 접속이 제한되었을 때, 중간부터 시작하기 위한 index 
    print('------- start {} -------'.format(YMD))
    for CODE in code_list : 
        data= download_open_api(YMD,CODE,pageNo=2)    # pageNo = 2로 수정

        for item_line in ET.fromstring(data).find("body").find("items").findall("item") :
            tmp_dict = {'년':item_line.findtext('년'),
                        '월':item_line.findtext('월'),
                        '일':item_line.findtext('일'),
                        '건축년도':item_line.findtext('건축년도'),
                        '건물면적':item_line.findtext('건물면적'),
                        '대지면적':item_line.findtext('대지면적'),
                        '거래금액':item_line.findtext('거래금액'),
                        '건물주용도':item_line.findtext('건물주용도'),
                        '유형':item_line.findtext('유형'),
                        '시군구':item_line.findtext('시군구'),
                        '법정동':item_line.findtext('법정동'),
                        '용도지역':item_line.findtext('용도지역')
                       }
            df = df.append(tmp_dict,ignore_index=True)
    print('------- end {} -------'.format(YMD))

pageNo = 2로 수정하여 기존 df에 append를 진행한다. 

 

그 다음 중복을 제거한다. 

df = df.sort_values(['년','월','일']).drop_duplicates()

 

결과적으로 중복되지 않은 데이터가 증가했음을 확인할 수 있다. 

-> 나는 page3도 추가적으로 작업을 진행해보았다. 하지만, 데이터프레임에서 중복제거를 후에 다시 진행했을 때, 데이터 rows 수가 더이상 늘지 않는 것을 확인하고 이로 마무리하였다.