공공데이터API사용하기

2 minute read



공공데이터 API 사용하기

  • 국토교통부_버스정류소정보의 데이터를 사용하여 좌표기반 근접 정류소 목록을 조회한다.
  • 목적 : 공공주택 근처의 버스정류장은 몇개인지 구하기 위해 사용하였다.
  • 일일 조회 트래픽이 많이 필요할 것 같아서 운영계정으로 신청했다.
  • 공공주택근처에 위치한 정류장의 개수를 구한다.

요청변수

항목명(한굴) 항목명(영문) 항목크기 항목구분 항목설명
서비스키 serviceKey - 필수 공공데이터 신청시 제공 받는 인증키
GPS Y좌표 gpsLati 20 필수 WGS84위도 좌표
GPS X좌표 gpsLong 20 필수 WGS84경도 좌표



출력결과

  • 형식 : XML
  • 결과
    • 정류소의 Y좌표
    • 정류소의 X좌표
    • 정류소 ID
    • 정류소명
    • 도시코드
  • 결과 예시
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
    <header>
        <resultCode>00</resultCode>
        <resultMsg>NORMAL SERVICE.</resultMsg>
    </header>
    <body>
        <items>
            <item>
                <citycode>도시코드</citycode>
                <gpslati>위도(Y)</gpslati>
                <gpslong>경도(X)</gpslong>
                <nodeid>정류소 ID</nodeid>
                <nodenm>정류소 명</nodenm>
                <nodeno>정류소 번호</nodeno>
            </item>
        </items>
        <numOfRows>10</numOfRows>
        <pageNo>1</pageNo>
        <totalCount>2</totalCount>
    </body>
</response>



데이터 사용

01. 전처리

  • 전처리 : LH주택공사에서 제공한 공공주택 도로명주소 데이터 사용
    1. 도로명주소가 담긴 API데이터를 수집하였다.
    2. 표준데이터셋 점검 서비스를 사용하여 변환하였고, 안나오는 데이터는 구글 Geocode Awesome Table을 이용했다.
    3. {도로명주소 : {'위도' : 36.3, '경도' : 127.3 }의 형식을 지닌 딕셔너리로 변환 lhdict

02. API 호출

  • python을 API호출.
  • 사용 라이브러리
from urllib.request import Request
from urllib.request import urlopen
from urllib.parse import urlencode
from urllib.parse import quote_plus
import json
import xml.etree.ElementTree as ET
from xml.etree.ElementTree import Element, dump, ElementTree

import pandas as pd

import time
from tqdm import tqdm


  • 호출코드
    • 버스정류장 정보를 조회할 필요없이 내가 필요한 정보는 전체 개수이다.
    • xml의 body 태그 아래에는 <totalCount>가 있는데 전체 개수를 의미하기 때문에 이것을 파싱하기로 했다.
idx = 0
errs =[]
result = dict()
for lh in tqdm(lhdict):

      url = 'http://openapi.tago.go.kr/openapi/service/BusSttnInfoInqireService/getCrdntPrxmtSttnList'
      queryParams = '?' + urlencode({ quote_plus('ServiceKey') : '제공된 ServiceKey', 
                                     quote_plus('gpsLati') :str(lhdict[lh]['위도']),    # 위도 
                                     quote_plus('gpsLong') : str(lhdict[lh]['경도']) }) # 경도


      request = Request(url + queryParams)
      request.get_method = lambda: 'GET'
      response_body = urlopen(request).read()
      resStr = response_body.decode('utf-8')

      xmldata = ET.fromstring(resStr)
      iterList = xmldata.findall('body')
      for ite in iterList:
          cnt = ite.find('totalCount').text
          result[str(idx)] = {'rnAdres':lh, 'bus_cnt': cnt}
          idx +=1
  
  • 이러면 result 딕셔너리를 반환해준다.
  • API 서버 상태로 인해 반도 못가서 자꾸 오류가 발생했다 ㅠㅠ
    • urlopen 어쩌구 에러
  • 그래서 예외처리하는 코드 + sleep을 추가했다.


from time import sleep

idx = 0
errs =[]
result = dict()
for lh in tqdm(lhdict):
  try: 
      url = 'http://openapi.tago.go.kr/openapi/service/BusSttnInfoInqireService/getCrdntPrxmtSttnList'
      queryParams = '?' + urlencode({ quote_plus('ServiceKey') : '제공된 ServiceKey', 
                                     quote_plus('gpsLati') :str(lhdict[lh]['위도']),    # 위도 
                                     quote_plus('gpsLong') : str(lhdict[lh]['경도']) }) # 경도
      
      sleep(2) # 로딩을 위해 2초 휴식

      request = Request(url + queryParams)
      request.get_method = lambda: 'GET'
      response_body = urlopen(request).read()
      resStr = response_body.decode('utf-8')

      xmldata = ET.fromstring(resStr)
      iterList = xmldata.findall('body')
      for ite in iterList:
          cnt = ite.find('totalCount').text
          result[str(idx)] = {'rnAdres':lh, 'bus_cnt': cnt}
          idx +=1
  except: # 예외처리
      # 에러가 나면  해당 주소를 출력하고 errs 리스트에 추가
      errs.append(lh)
      print(idx,' : ',lh)
      idx+=1 
        




사용 후기

  • 굉장히 오래걸린다… 약 15시간정도?
  • csv형식의 전국 버스정류장 위도, 경도가 담긴 데이터가 있다면 그걸 쓰는게 훨씬 빠를 것 같다.
  • 전국버스정류소 위치를 csv형식으로 국도교통부에서 제공하고있기 때문에 이것을 사용해볼 예정이다.
  • 국토교통부_전국 버스정류장 위치정보
    • 버스정보시스템(BIS)이 구축된 지자체 중 국가대중교통정보센터(TAGO)와 연계된 115개 지자체에 대한 버스정류장 위치정보 데이터입니다.