[python-크롤링] 나라장터 API를 활용한 연구용역 데이터 수집

·

·

,

오늘은 나라장터에서 원하는 연구용역을 불러오는 작업을 수행하고자 합니다.

정부에서 발주하는 용역, 공사, 물품 등은 대부분 나라장터를 거쳐서 나옵니다. 물론, 아직도 많은 기관이 나라장터가 아니라 각 기관 홈페이지나 다른 방법을 통해 공고를 내는 경우가 많습니다. 덕분에 KBID처럼 다양한 기관의 공고들을 모아서 분석해주는 서비스가 성공적으로 활용되고 있습니다.

제가 KBID처럼 다양한 기관의 공고들을 수집하고자 하는 것은 아닙니다. 하지만, 최소한 나라장터에 올라오는 공고만큼은 수집하여 활용할 수 있도록 원하는 용역을 스크랩하는 코드를 짜볼까 합니다. 나라장터 자체는 검색이나 활용이 어렵게 만들어져 있어서, 나라장터를 들여다보는 것 만으로는 원하는 용역을 찾기가 쉽지 않기 때문입니다.

나라장터에서 원하는 용역을 스크랩하기 위해서는 우선 공공데이터포털(data.go.kr)에서 나라장터 apiKey를 발급받아야 합니다. 저는 “나라장터 입찰공고정보서비스” 인증키를 받았습니다. “나라장터 입찰공고정보서비스”는 공공데이터포털 데이터셋 검색에서 [ 조달청 – 입찰공고정보 ]를 검색하시면 확인할 수 있습니다.

제가 활용한 “나라장터 입찰공고정보서비스” 외에 나라장터에서 제공하는 데이터 목록은 조달정보개방포털(data.g2b.go.kr) 공공데이터 부분에 가시면 확인하실 수 있습니다.

“나라장터 입찰공고정보서비스” 상세기능(조달청 혹은 공공데이터 포털 홈페이지 참조)을 보면, “입찰공고목록 정보에 대한 용역조회”가 있는데, 이번에 제가 사용할 데이터입니다.

원래는 “입찰공고목록 정보에 대한 용역조회”가 아니라 “나라장터검색조건에 의한 입찰공고용역조회”를 사용하고 싶었는데, “나라장터검색조건에 의한 입찰공고용역조회” 요청주소가 제대로 작동하지 않아서 “입찰공고목록 정보에 대한 용역조회”를 사용하였습니다.

필요한 모듈 불러오기

우선은 필요한 모듈을 불러옵니다. (전부 필요한 모듈은 아니고, 제가 자주 쓰는 모듈도 포함되어 있습니다.)

import requests
import pandas as pd
from pandas.io.json import json_normalize
import sqlite3
from datetime import datetime
import numpy as np
Code language: JavaScript (javascript)

apiKey, Ddir, DB 등록하기

필요한 코드를 짜기 전에 apiKey를 등록하고, Ddir를 지정해줍니다. 아래 기록된 apiKey는 제대로 작동하지 않는 예시용 Key입니다. Ddir는 스크랩한 내용을 저장할 db가 위치하는 주소입니다. db는 sqlite3를 사용하고 있습니다.

apikey = 'fakeapikey'
Ddir = '/content/drive/My Drive/[10]Colab/[02]Project_DeepLearning/[00]Data/'
Tdir = '/content/drive/My Drive/[10]Colab/[02]Project_DeepLearning/[00]Test/'
con = sqlite3.connect(Ddir + 'data02.db')
c = con.cursor()
Code language: JavaScript (javascript)

OpenAPI 테스트

Api로 한 번에 요청할 수 있는 데이터는 1,000개로, 1,000개가 넘는 데이터를 요청하기 위해서는 반복해서 데이터를 요청해야합니다. Loop문을 만들기 전에, Loop문을 만들 수 있는 최소한의 코드를 짜고, 코드가 제대로 작동하는지 확인하겠습니다.

url은 “나라장터 입찰공고정보서비스”의 “나라장터검색조건에 의한 입찰공고용역조회”의 요청주소에 테스트를 위한 요청변수를 추가한 것입니다. 데이터 타입은 json, 시간은 2020년 2월 25일 00시 00분부터 2020년 2월 25일 23시 59분이며, 1페이지의 999개 데이터를 요청하는 주소입니다.

url = '<http://apis.data.go.kr/1230000/BidPublicInfoService/getBidPblancListInfoServc?inqryDiv=1&type=json&inqryBgnDt=202002250000&inqryEndDt=202002252359&pageNo=1&numOfRows=999&ServiceKey=>' + apikey
Code language: HTML, XML (xml)

요청주소 url을 불러와서, 불러온 json 문서를 DataFrame으로 변환하는 코드입니다. 중간에 api를 제대로 요청했는지 확인하기 위해 “print(requests.get(url))”를 추가했습니다. 그리고 불러온 데이터가 몇 건이나 되는지 확인하기 위해 “print(len(df.index))”를 추가했습니다.

r = requests.get(url).json()
print(requests.get(url))
data = r['response']['body']['items']
df = pd.DataFrame.from_dict(json_normalize(data), orient='columns')
print(len(df.index))
Code language: PHP (php)

아쉽게도 위 코드의 결과는 “<Response [200]>”과 “0”입니다. “나라장터검색조건에 의한 입찰공고용역조회”이 제대로 데이터를 불러오지 못해서 0건이 되었습니다. url을 아래와 같이 수정하여, “입찰공고목록 정보에 대한 용역조회”로 변경하고 위 코드를 실행하면, 결과가 “<Response [200]>”과 “999”가 되는 것을 확인할 수 있습니다.

url = '<http://apis.data.go.kr/1230000/BidPublicInfoService/getBidPblancListInfoServc?inqryDiv=1&type=json&inqryBgnDt=202002250000&inqryEndDt=202002252359&pageNo=1&numOfRows=999&ServiceKey=>' + apikey
r = requests.get(url).json()
print(requests.get(url))
data = r['response']['body']['items']
df = pd.DataFrame.from_dict(json_normalize(data), orient='columns')
print(len(df.index))
Code language: HTML, XML (xml)

“나라장터검색조건에 의한 입찰공고용역조회” 스크랩

테스트 코드가 제대로 작동하는 것을 확인했으니, 이제 원하는 데이터를 스크랩하려고 합니다. 저는 업종코드 “1169”인 용역 중 오늘 하루 00시 00분부터 23시 59분 사이에 올라온 용역만 스크랩 할 예정입니다. 오늘을 기준으로 어제까지 올라온 용역은 다 수집했다는 전제 하에, 지금 시점에서 오늘 올라온 용역만 스크랩해서 추가하면 되기 때문입니다.

저는 업종코드 “1169”인 용역만 필요하여, 요청주소에 요청변수 “&indstrytyCd=1169”를 추가하였습니다. 다른 업종코드가 필요하시거나, 모든 용역이 필요한 경우 요청변수 “&indstrytyCd=”를 제외하거나 수정하시면 됩니다.

위 테스트 코드와 동일하게 url을 우선 지정해줍니다. 다만, 테스트 코드와 달리, 아래 url에는 업종코드가 추가되었고, 시간과 페이지 등이 제외되었습니다.

url = '<http://apis.data.go.kr/1230000/BidPublicInfoService/getBidPblancListInfoServcPPSSrch?inqryDiv=1&type=json&indstrytyCd=1169>'
Code language: HTML, XML (xml)

아래는 url_dayone을 url을 기반으로 생성하여, page 1부터 page가 끝날 때 까지 Loop하도록 하는 코드입니다.

def dayone(url):
    result = pd.DataFrame()
    page = 1
    while True:
        url_dayone = url + '&inqryBgnDt=' + datetime.today().strftime("%Y%m%d") + '0000' + '&inqryEndDt=' + datetime.today().strftime("%Y%m%d") + '2359' + '&pageNo=' + str(page) + '&numOfRows=999&ServiceKey=' + apikey
        r = requests.get(url_dayone).json()
        data = r['response']['body']['items']
        df = pd.DataFrame.from_dict(json_normalize(data), orient='columns')
        if len(df.index) == 0:
            break
        result = result.append(df)
        page += 1
    file = 'dayone' + '(' + datetime.today().strftime("%Y%m%d") + ')'
    result.to_sql(file, con, if_exists='replace', index=False)
Code language: PHP (php)

위 코드를 실행하면, dayone def가 생성됩니다. 이제, dayone def에 원하는 url 변수를 입력해서 요청하면, 오늘 하루 업종코드 1169인 용역이 스크랩되고, 앞서 지정한 db에 ‘dayone(20200311)’ 형태로 저장됩니다.

dayone(url)

db에 저장된 데이터를 데이터프레임으로 확인하고 싶으시면, 아래의 코드를 실행하시면 됩니다.

sql_dayone = """select * from 'dayone(20200226)'"""
df = pd.read_sql(sql_dayone, con)
Code language: JavaScript (javascript)

지금까지 나라장터에서 원하는 연구용역 정보를 스크랩하는 방법을 살펴보았습니다. 위 코드는 api를 통해 데이터를 스크랩하는 과정까지 담고 있기 때문에, 이를 바탕으로 공공데이터포털(혹은 조달정보개발포털)에서 제공하는 다른 데이터와 결합하거나 비교하여 분석하는 것은 다른 기회가 있으면 공유하도록 하겠습니다. 워낙 KBID와 같은 서비스가 잘 되어있어서, 이런 작업이 불필요하게 느껴지기는 하기 때문에, 앞으로 추가적인 작업을 진행할지는 의문입니다.

아래는 코드 전문입니다.

전체코드

# 나라장터 공공데이터개방표준서비스 (PubDataOpnStdService)
import requests
import pandas as pd
from pandas.io.json import json_normalize
import sqlite3
from datetime import datetime
import numpy as np

## apikey 등록 & db 생성
apikey = 'fakeapikey'
Ddir = '/content/drive/My Drive/[10]Colab/[02]Project_DeepLearning/[00]Data/'
Tdir = '/content/drive/My Drive/[10]Colab/[02]Project_DeepLearning/[00]Test/'
con = sqlite3.connect(Ddir + 'data02.db')
c = con.cursor()

## OpenAPI 테스트
### 나라장터검색조건에 의한 입찰공고용역조회
url = '<http://apis.data.go.kr/1230000/BidPublicInfoService/getBidPblancListInfoServcPPSSrch?inqryDiv=1&type=json&indstrytyCd=1169&inqryBgnDt=202002250000&inqryEndDt=202002252359&pageNo=10&numOfRows=999&ServiceKey=>' + apikey
r = requests.get(url).json()
print(requests.get(url))
data = r['response']['body']['items']
df = pd.DataFrame.from_dict(json_normalize(data), orient='columns')
print(len(df.index))
writer = pd.ExcelWriter(Tdir + 'test02.xlsx', engine='openpyxl')
df.to_excel(writer, sheet_name='test02', header=True)
writer.save()
writer.close()

### 입찰공고목록 정보에 대한 용역조회
url = '<http://apis.data.go.kr/1230000/BidPublicInfoService/getBidPblancListInfoServc?inqryDiv=1&type=json&inqryBgnDt=202002250000&inqryEndDt=202002252359&pageNo=1&numOfRows=999&ServiceKey=>' + apikey
r = requests.get(url).json()
print(requests.get(url))
data = r['response']['body']['items']
df = pd.DataFrame.from_dict(json_normalize(data), orient='columns')
print(len(df.index))
writer = pd.ExcelWriter(Tdir + 'test02.xlsx', engine='openpyxl')
df.to_excel(writer, sheet_name='test02', header=True)
writer.save()
writer.close()

## 나라장터검색조건에 의한 입찰공고용역조회
### extraction
url = '<http://apis.data.go.kr/1230000/BidPublicInfoService/getBidPblancListInfoServcPPSSrch?inqryDiv=1&type=json&indstrytyCd=1169>'
def dayone(url):
    result = pd.DataFrame()
    page = 1
    while True:
        url_dayone = url + '&inqryBgnDt=' + datetime.today().strftime("%Y%m%d") + '0000' + '&inqryEndDt=' + datetime.today().strftime("%Y%m%d") + '2359' + '&pageNo=' + str(page) + '&numOfRows=999&ServiceKey=' + apikey
        r = requests.get(url_dayone).json()
        data = r['response']['body']['items']
        df = pd.DataFrame.from_dict(json_normalize(data), orient='columns')
        if len(df.index) == 0:
            break
        result = result.append(df)
        page += 1
    file = 'dayone' + '(' + datetime.today().strftime("%Y%m%d") + ')'
    result.to_sql(file, con, if_exists='replace', index=False)
dayone(url)

### preparation
sql_dayone = """select * from 'dayone(20200226)'"""
df = pd.read_sql(sql_dayone, con)
df.columns
Code language: PHP (php)
“[python-크롤링] 나라장터 API를 활용한 연구용역 데이터 수집”에 대한 2개의 응답
  1. Jaeun Ha 아바타

    글을 잘보고 있습니다
    제가 나라장터를 이용하여 데이터를 모으고 분석을 해보려고하는데 이러한 프로그래밍은 어떻게 배우셨는지 알 수 있을까요?
    저도 비슷하게 공부해보고싶어서요

    1. gurumii 아바타

      안녕하세요.
      답변은 ‘저도 잘 모르겠습니다.’ 그냥 하다보니, 하고 있었습니다. 다만 지금까지 상황에 가장 큰 기여를 한 부분은 처음에 시간이 많이 걸려도, 일단 파이썬으로 해본다인 것 같습니다.

댓글 남기기