본문 바로가기
Python notes/Others

크롤링) OpenAPI로 코로나19 예방접종 및 감염현황 데이터 크롤링 (+파이썬 Beautifulsoup으로 xml, json 파싱, 엑셀 저장)

by 성실한 나무 2021. 9. 18.

공공데이터포털 접속하기

공공데이터포털(링크: https://www.data.go.kr/index.do)에서는 국가에서 보유하고 있는 다양한 데이터를 국민들에게 개방하고 있다. 회원가입을 하고 로그인을 한 후, 원하는 데이터의 키워드를 검색하면 관련된 데이터들의 목록을 확인할 수 있다. 상당수 데이터의 사용 비용이 무료이고, 이용허락범위에 제한이 없다.

 데이터들은 파일데이터, 오픈API, 표준데이터셋으로 형태가 구분되어 있다. 그 중에서도 오픈API 형태로 되어 있는 코로나19 데이터들을 파싱해보고자 한다. 오픈API의 데이터포맷은 XML과 JSON이 주를 이룬다.

 이에따라 XML 형태로 되어 있는 보건복지부 코로나19 감염 현황과 JSON 형태로 되어 있는 코로나19 예방접종 통계를 파싱한다.

공공데이터포털 오픈API

 

파이썬으로 OpenAPI를 통해 JSON, XML 포맷 데이터 파싱하기

 JSON포맷 데이터 파싱을 위해 필요한 패키지는 다음과 같다. requests, urllib, datetime은 데이터를 불러오기 위한 패키지들이고, json은 불러온 데이터를 json 형태로 파싱하기 위한 패키지이다.  한편, bs4의 BeautifulSoup은 xml 데이터 파싱을 위해 사용한다. pandas의 각종 함수들은 데이터 편집을 위해 사용하고, matplotlib는 데이터 시각화를 위해 필요한 패키지이다. 여기서는 데이터를 편집해서 엑셀로 저장하는 것까지 기록하고, 시각화는 별도로 포스팅할 예정이다.

 

JSON 포맷 데이터 파싱: 코로나 백신 예방접종 통계

 먼저, 헤더를 정의해야 한다. 헤더는 User-agent, referer, Authorization 정보를 딕셔너리의 형태로 포함하고 있어야 한다. 여기서 주의할 점은 각 Key값의 대소문자 구분이다. 'User-agent'를 'user-agent'로 작성하면 에러가 발생한다. 또 다른 한가지는 'Authorization'에 공공데이터포털에서 발급받은 고유한 인증키를 입력해야 한다는 점이다. 공공데이터 포털에서 원하는 데이터를 고르고 데이터를 활용하겠다는 "활용신청" 버튼을 누르면 마이페이지에서 그 내역을 확인할 수 있다. "마이페이지 > 오픈API > 계발계정 > 데이터 목록 클릭"을 통해 "개발계정 상세보기"로 들어가면, 서비스정보에서 일반 인증키들을 확인할 수 있다. 둘 중에 하나를 'Authorization'에 스트링 형태로 입력하면 된다. 내 경우에는 "Infuser 일반인증키(decoding)"으로 입력했더니 정상작동 했다. 다음으로는 params를 정의하여 query를 만들고 이를 base_url과 합쳐서 최종 url을 정의하였다. requests.get함수에 url과 앞서 준비한 헤더 정보들을 입력하고, r이라는 객체에 담았다. r이 정상적으로 데이터를 받아올 경우 r.status_code는 200으로 반환이 될 것이고 그 이외에 오류가 있는 경우는 다른 번호가 반환될 것이다. 정상적으로 데이터를 받아온 것을 확인한 후, 'utf-8'로 인코딩을 하고, json.loads()함수 안에 텍스트를 입력하면, 딕셔너리형태로 데이터를 받아올 수 있다. 이 딕셔너리의 ['data'] key안에 데이터 값들이 담겨 있다. 데이터를 보기 쉽게 DataFrame 형태로 변경하였다.

파이썬 Json 파싱

 

 해당 data에 어떤 key들이 있는지 확인하기 위해 .keys() 함수를 사용한다. 그리고 공공데이터포털에서 제공하는 각 컬럼명들의 정의를 확인해서 하나씩 이름을 변경해주었다.

파이썬 Json 파싱 결과 엑셀로 저장

 

 마지막으로 정제한 df를 .to_excel("파일명.xlsx") 함수를 통해 파싱한 데이터를 엑셀에 저장하였다. 백업해 놓은 파일은 이후, read_excel() 함수로 다시 불러올 수 있다.

 

XML 포맷 데이터 파싱: 코로나 현황 통계

 XML 포맷 데이터를 Json으로 바꿔주는 편리한 패키지들도 있다. 구글에서 검색을 해보면 xmltodict, untangle 등이 나온다. 이 패키지들은 pip install을 통해 간단하게 설치가 가능한다. 그런데 나는 왜인지 md5 에러가 뜨면서 이런 패키지들이 제대로 설치가 되지 않았다. 그래서 그냥 BeautifulSoup과 파이썬 반복문을 통해 데이터를 파싱하였다. 

 앞에서와 마찬가지로 헤더를 정의하고, 쿼리와 파라미터를 포함하는 url을 정의하였다. requests.get()을 통해 불러온 데이터는 BeautifulSoup함수에 'lxml-xml' 파라미터를 입력하여 파싱하였다.

 BeautifulSoup으로 파싱한 결과를 xml 객체에 담고 다시 findAll('item')로 item 데이터들을 rows 객체에 담았다.

 첫행에 있는 컬럼명들을 keys 리스트에 따로 담아서 이를 기반으로 열이름만 있는 데이터프레임을 만들었다. 여기에 하나씩 values를 넣을 것이다. 

BeautifulSoup으로 xml 파싱

 

 rows에 있는 각 행 값들을 temp_dict에 임시적으로 담고 데이터프레임에 한 행, 한 행 반복시켜 데이터를 입력하였다. 마무리는 to_exel로 데이터를 백업하였다.

 

Json, XML 포맷 데이터 파싱 코드

 아래는 Json 포맷 데이터를 파싱하기 위한 파이썬 코드이다. 코로나 백신 예방접종 통계 데이터를 얻을 수 있다.

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
import requests, urllib, json, datetime as dt
from pandas import read_excel, DataFrame, merge
from matplotlib import pyplot as plt
from bs4 import BeautifulSoup
 
#header 정의: User-agent, referer, Authorization(인증키)
HEADERS={'User-agent':"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"
         'referer':None,
         'Authorization''Infuser 인증키입력자리'}
 
#파라미터, 쿼리, url 정의
PAGE=1
PERPAGE=18*(dt.datetime.now()-dt.datetime(2021,3,1)).days
 
params={'page':PAGE, 'perPage':PERPAGE}
query=urllib.parse.urlencode(params)
base_url="https://api.odcloud.kr/api/15077756/v1/vaccine-stat?"
url=base_url+query
 
#데이터 불러오기
r=requests.get(url, headers=HEADERS)
if r.status_code != 200:
    err_msg='%d, %s' %(r.status_code, r.reason)
    raise Exception(err_msg)
r.encoding='utf-8'
vacc_dict=json.loads(r.text)
 
data=DataFrame(vacc_dict['data'])
keys=data.keys()
df=data.rename(columns={keys[0]:"전일까지의누적통계_1차접종", keys[1]:"전일까지의누적통계_2차접종", keys[2]:"통계기준일자", keys[3]:"당일통계_1차접종", keys[4]:"당일통계_2차접종", keys[5]:"지역명칭", keys[6]:"전체누적통계_1차접종", keys[7]:"전체누적통계_2차접종" })
df['통계기준일자']=df['통계기준일자'].str.replace("00:00:00""")
df.to_excel("코로나예방접종.xlsx")
 
cs

 아래는 xml 포맷 데이터를 파싱하기 위한 파이썬 코드이다. 코로나 현황 통계 데이터를 얻을 수 있다.

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
47
48
#header 정의: User-agent, referer, Authorization(인증키)
HEADERS={'User-agent':"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"
         'referer':None,
         'Authorization''Infuser 인증키입력자리'}
 
#파라미터, 쿼리, url 정의
KEY="인증키입력자리"
ENDDT=dt.datetime.now().strftime("%Y%m%d")
 
params={'serviceKey':KEY, 'endCreateDt':ENDDT}
query=urllib.parse.urlencode(params)
base_url="http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19InfStateJson?"
url=base_url+query
 
#데이터 불러오기
r=requests.get(url, headers=HEADERS)
if r.status_code != 200:
    err_msg='%d, %s' %(r.status_code, r.reason)
    raise Exception(err_msg)
r.encoding='utf-8'
response=r.text
 
#BeautifulSoup으로 'xml'파싱
xml=BeautifulSoup(response, 'lxml-xml')
rows=xml.findAll('item')
keys=[]
 
#column명을 포함하는 DataFrame정의
for tag in rows[0].findAll():
    index=str(tag).find(">")
    key=str(tag)[1:index]
    keys.append(key)
df=DataFrame(columns=keys)
 
#각 요소를 추출해서 df에 추가
for row in rows:
    data=row.findAll()
    temp_dict={}
    for j in range(len(data)):
        index=str(row.findAll()[j]).find(">")
        tag=str(row.findAll()[j])[1:index]
        temp_dict[tag]=data[j].text
    df=df.append(temp_dict, ignore_index=True)
ndf=df.rename(columns={'accDefRate':'누적확진률''accExamCnt':"누적검사수"'accExamCompCnt':"누적검사완료수"'careCnt':"치료중환자수"'clearCnt':"격리해제수",
       'createDt':"등록일시분초"'deathCnt':"사망자수"'decideCnt':"확진자수"'examCnt':"검사진행수"'resutlNegCnt':"결과음성수"'seq':"감염현황고유값",
       'stateDt':"기준일"'stateTime':"기준시간"'updateDt':"수정일시분초"'accDefRate/':"?"})
ndf.drop("?", axis=1)
ndf.to_excel("코로나현황.xlsx")
cs

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

댓글