no image
파이썬으로 여러 페이지에 있는 정부 보도자료 크롤링하기_페이지네이션 | Web Scraping
이전에 파이썬을 활용해 정부 보도자료를 크롤링하는 게시글을 올린 적이 있다. 이 블로그내 제법 인기글 중 하나인데, 그만큼 많은 분들이 관심을 갖고 있다는 뜻이기도 할 것이다. 워낙 프로토타입 형태로 올렸던 글이라, 아주 간단한 형태의 크롤링만 올렸었다. 관련 링크는 아래에 첨부하였다. 2020.02.23 - [Python/Web Crawling] - 파이썬으로 정부 보도자료 크롤링 하기 (Web crawling) 파이썬으로 정부 보도자료 크롤링 하기 (Web crawling) 우리나라의 사회/산업/경제 전반적인 상황을 보기위해 신문도 참고할 자료 중 하나이긴 하지만, 각 정부 부처에서 제공하는 보도자료가 사실에 근접해서 보기에 훨씬 유용한 자료라 생각한다. seanpark11.tistory.com 지금..
2021.10.04
no image
[Data Prep] 제멋대로인 주소(광역지자체명) 표준화하기
최근, 한글 주소를 활용해 데이터를 모아야 하는 일이 있었고, 현재도 프로젝트는 진행 중에 있다. 수행해야 하는 것은 기존에 갖고 있는 한글주소를 가지고, 토지이음이라는 토지의 활용과 관련된 정부의 웹사이트에서 데이터를 취합하는 것을 목표로 하고 있다.  결국, 기존 데이터와 새로운 곳의 데이터 형식에 대한 일치하는지, 일치하지 않다면 이를 어떻게 해결할 것인지가 필요하다. 이를 위해, 토지이음에서는 어떻게 데이터를 입력받고 있는지를 확인했다. 사이트에서는 아래와 같이 광역지자체를 선택하게끔 하고 있다.  한국에서 주소 관련 (수기입력을 하는)정보들은, 특히 광역지자체에 대해서는 저 위의 형식이 지켜지지 않는다. (예를 들면, '서울특별시'란 광역지자체도 '서울', '서울시' 등으로 다양하게 적음) 따라..
2021.08.15
no image
금감원 XML 파일(corp_code) python으로 읽어내기
최근 금감원에서 제공하고 있는 데이터를 접근하는 방법에 대해 고민하고 있다. 개인적으로 투자를 하고 있는 상황에서, 현재 금융업종에 종사하고 있지 않는 나에게 많은 데이터를 제공하고 있는 금감원은 사실 가장 중요한 정보의 출처이다. 하지만, 모든 회사에서 제공하는 사업보고서에 있는 내용들을 읽어야 하지만, 이 행위를 조금 더 편리하게 하는 방법에 대해 고민을 하고 있다. 이를 위해서 금감원은 필요한 데이터에 접근을 용이하게 하고, OPEN API를 적극적으로 활용할 수 있도록 최근 새롭게 홈페이지를 개편했다. 아래는 그 홈페이지이다. 옛날 DART 홈페이지보다는 훨씬 깔끔해졌고, 좀더 OPEN API 활용에 초점을 맞춘 듯 하다. https://opendart.fss.or.kr/ 전자공시 OPENDART..
2020.03.14
no image
[Data Prep] 파이썬을 활용한 공공데이터API 접근하기_미세먼지 통계 | BeautifulSoup, Open API
최근 3~4년간 사람들이 부쩍 미세먼지에 대한 관심이 높아졌다. 개인적인 생각은 미세먼지에 대해서 과거에 비해 심해졌다기 보다, 사람들의 인식이 삶의 질에 더 관심이 많아지면서 air quality에도 관심을 가졌기 때문일 것이라 생각하는데 내 생각이 맞는지 아니면 진짜 대기오염 수치가 나빠진 것인지에 대해 직접 데이터를 크롤링해 살펴보고자 한다. 크롤링 하기 전에, 다른 방법으로 공기오염 정도에 대한 통계를 제공하는지 살펴보려고 airkorea 사이트에 접속했다.  사이트에서 통계연보 및 월보의 형태로 데이터를 제공하기는 하지만, 매년 혹은 매월 데이터를 다운받아 취합하는 과정은 상당히 귀찮은 일이다.  내가 구현하고 싶은 것은 다음과 같다. - 공공데이터 포털에 제공하는 api에 접속- '종로구'에 ..
2020.03.04
[Data Prep] 파이썬 Openpyxl을 이용한 보도자료 내용 저장하기
앞서 BeautifulSoup을 활용해 보도자료의 리스트의 형태로 제목들을 가져왔다. https://seanpark11.tistory.com/10 파이썬으로 정부 보도자료 크롤링(crawling/scraping)하기우리나라의 사회/산업/경제 전반적인 상황을 보기위해 신문도 참고할 자료 중 하나이긴 하지만, 각 정부 부처에서 제공하는 보도자료가 사실에 근접해서 보기에 훨씬 유용한 자료라 생각한다. 하지만, 매번 정부의..seanpark11.tistory.com 하지만, 리스트는 python에 데이터를 저장하기엔 유용한 형태일지 몰라도 일반적으로 활용하기에는 제한이 된다. 따라서, 우리는 활용하기 편한 다른 형태로 데이터를 저장해야 하는데 그중 하나로 엑셀을 활용하고자 한다. python을 이용해 excel..
2020.03.02
no image
[Data Prep] 파이썬으로 산업부 보도자료 크롤링 하기
2021.10.04 - [Python/Web Crawling] - 파이썬으로 여러 페이지에 있는 정부 보도자료 크롤링하기_페이지네이션우리나라의 사회/산업/경제 전반적인 상황을 보기위해 신문도 참고할 자료 중 하나이긴 하지만, 각 정부 부처에서 제공하는 보도자료가 사실에 근접해서 보기에 훨씬 유용한 자료라 생각한다. 하지만, 매번 정부의 모든 부처들에 방문해 보도자료를 찾아보기는 번거롭다. 이를 해결하기 위해서 정부 부처 보도자료의 제목들을 가져오는 크롤링 프로그램을 한번 생각해봤다. 각 정부 부처의 보도자료 관련 주소를 리스트를 만들고,주소 리스트의 순서대로 보도자료의 제목들을 찾아 내가 원하는 제목리스트에 저장한다.사실 실제로 사용하기 위해서는 위 과정보다 더 나아가야 하지만, 내가 원하는 보도자료 제..
2020.02.23
반응형

이전에 파이썬을 활용해 정부 보도자료를 크롤링하는 게시글을 올린 적이 있다. 이 블로그내 제법 인기글 중 하나인데, 그만큼 많은 분들이 관심을 갖고 있다는 뜻이기도 할 것이다. 워낙 프로토타입 형태로 올렸던 글이라, 아주 간단한 형태의 크롤링만 올렸었다. 관련 링크는 아래에 첨부하였다.

 

2020.02.23 - [Python/Web Crawling] - 파이썬으로 정부 보도자료 크롤링 하기 (Web crawling)

 

파이썬으로 정부 보도자료 크롤링 하기 (Web crawling)

우리나라의 사회/산업/경제 전반적인 상황을 보기위해 신문도 참고할 자료 중 하나이긴 하지만, 각 정부 부처에서 제공하는 보도자료가 사실에 근접해서 보기에 훨씬 유용한 자료라 생각한다.

seanpark11.tistory.com

 

 

지금 돌이켜봤을 때, 위글에서 아쉬웠던 점이 몇 가지가 있는데 아래처럼 요약할 수 있을 것이다. 

 

1. requests란 훌륭한 라이브러리를 놔두고, urllib3를 사용했다는 점 (urllib이 파이썬 내장 라이브러리긴 하지만, 공식 홈페이지에서도 requests를 사용할 것을 장려하는 점을 고려하면 새로운 내용으로 바꾸는 것이 적절할 것)

2. 한번에 한 페이지의 내용만 가져오는 것을 이야기함

3. 동적으로 움직이는 반응형 웹사이트는 위 테크닉만으로는 어려운데, 새로운 기술에 대한 소개 필요

 

이중에서 이번 글을 통해 두가지를 해결할 수 있을 것 같은데, 그중 하나인 1번은 앞으로 크롤링하는데 자연스럽게 사용하면서 녹여낼 것이고 나머지 하나인 2번은 오늘 이야기할 페이지네이션을 통해 해결하고자 한다. 나머지 세번째는 셀레니움(Selenium)으로 일반적으로 해결할텐데, 이는 나중 글에서 논하고자 한다.

 

우선, 페이지네이션에 대해 살펴봐야 할텐데, 검색창을 통해 알아보면 페이지네이션은 웹개발에서 DB의 많은 정보를 한꺼번에 표현하기 어려우므로 페이지의 형태로 표현하는 것을 지칭한다. (게시판 등에서 쉽게 볼 수 있는 형태라 어렵진 않다) 이건 개발, 그중에서도 백엔드 단에서 하는 이야기이고, 우리가 관심있는 크롤링에서는 어떻다고 이야기할 수 있을까? 정확한 정의는 아직 찾지는 못했지만, 페이지 형태로 구현된 정보들을 페이지(일련번호)를 부여하면서 읽어온다고 이야기할 수 있을 것이다.


이전에 봤던 기획재정부 보도자료 홈페이지를 들어가보자. 가장 아래로 가보면 "1 2 3 4 5" 상자로 이뤄진 페이지로 갈 수 있는 박스가 있는 것을 확인할 수 있다. 이 상자들을 눌러보자. 그리고 그 URL을 한번 확인해보자.

https://www.moef.go.kr/nw/nes/nesdta.do?searchBbsId1=MOSFBBS_000000000028&menuNo=4010100&pageIndex=1

https://www.moef.go.kr/nw/nes/nesdta.do?searchBbsId1=MOSFBBS_000000000028&menuNo=4010100&pageIndex=2

https://www.moef.go.kr/nw/nes/nesdta.do?searchBbsId1=MOSFBBS_000000000028&menuNo=4010100&pageIndex=3

....

https://www.moef.go.kr/nw/nes/nesdta.do?searchBbsId1=MOSFBBS_000000000028&menuNo=4010100&pageIndex=1789

복사해보면 위처럼 나오게 되는데, 잘살펴보면 규칙이 하나 있다. ~do? 이후 &로 연결된 여러 파라미터들 중 pageIndex라는 것이 1,2,3,...으로 쭉 이어지는 것을 확인할 수 있다. (참고로, 글을 쓰는 현시점에 보도자료 맨 마지막페이지는 1789인데, 그게 맨 마지막에 있는 링크임을 확인할 수 있다.) 따라서, 해당 pageIndex에 대한 변화를 주는 것만으로도 해당 페이지에 대한 요청을 보낼 수 있다. 그렇다면, 이전에 사용했던 코드를 먼저 가져와서 참고하자.

 

import requests
from bs4 import BeautifulSoup

url = "http://www.moef.go.kr/nw/nes/nesdta.do?bbsId=MOSFBBS_000000000028&menuNo=4010100&pageIndex=1"
req = requests.get(url)
soup = BeautifulSoup(req, "html.parser")
soup.find_all('h3')

tag가 'h3'인 것은 이전 글에서 간단히 찾아봤으니 (물론 그때는 태그를 이용한 find_all을 사용하기 보다는 selector를 활용했지만), find_all을 통해 태그를 찾아준다면 위와 같이 리스트를 반환한다. 여기엔 태그가 여기저기 붙어있으니 좀 더 깔끔한 내용을 출력하기 위해서는 아래와 같이 적용해주면 된다.

for item in soup.find_all('h3'):
    print(item.text)


그럼 여기서 더 나아가서, 페이지별로 수집하기 위해서는 어떻게 하면될까? 앞서서도 설명했지만 pageIndex만 수정해주면 되는데, 여러가지 방법이 있지만 가장 간단한 방법으로 (5페이지만 가져오도록 하기 위한) 코드는 아래와 같이 작성하면 된다. 그리고 잘 나오는 것을 확인할 수 있다.

import requests
from bs4 import BeautifulSoup
# 원하는 태그의 내용을 담을 리스트(item_list)와 pageIndex를 비운 url 준비하기
item_list = []
url = "http://www.moef.go.kr/nw/nes/nesdta.do?bbsId=MOSFBBS_000000000028&menuNo=4010100&pageIndex="

# 보도자료 페이지별 웹사이트 요청해서 태그를 찾고, 그것을 리스트에 연장하기
for ind in range(1,6):
    req = requests.get(url+str(ind)).text
    soup = BeautifulSoup(req, "html.parser")
    item_list.extend(soup.find_all('h3'))

# 이쁘게 출력하기
for item in item_list:
    print(item.text)

 

Version
Python = v.3.8 (64 bit)
requests = v.2.26.0
Beautifulsoup = v.4.9.0
반응형
반응형

최근, 한글 주소를 활용해 데이터를 모아야 하는 일이 있었고, 현재도 프로젝트는 진행 중에 있다. 수행해야 하는 것은 기존에 갖고 있는 한글주소를 가지고, 토지이음이라는 토지의 활용과 관련된 정부의 웹사이트에서 데이터를 취합하는 것을 목표로 하고 있다. 

 

결국, 기존 데이터와 새로운 곳의 데이터 형식에 대한 일치하는지, 일치하지 않다면 이를 어떻게 해결할 것인지가 필요하다. 이를 위해, 토지이음에서는 어떻게 데이터를 입력받고 있는지를 확인했다. 사이트에서는 아래와 같이 광역지자체를 선택하게끔 하고 있다.

 

 

한국에서 주소 관련 (수기입력을 하는)정보들은, 특히 광역지자체에 대해서는 저 위의 형식이 지켜지지 않는다. (예를 들면, '서울특별시'란 광역지자체도 '서울', '서울시' 등으로 다양하게 적음) 따라서, 기존에 갖고 있던 한글 주소 데이터를 확인하기 위해 살펴봤는데, 아니나다를까 아래 사진처럼 제각각으로 쓰여있다. 

 

 

정보 조회를 위해서는 첫번째 사진처럼 통일된 형식이 필요한데, 그렇지 못한 상황에서 어떻게 해결해야 할까. 내가 생각한 방법은 다음과 같다.

 

1. 참고하기 위한 표준화된 리스트를 만든다.

2. 광역지자체 명명에 대한 일정한 규칙을 찾아 정규식을 만들어, 광역지자체만 찾는다.

3. 2단계에서 찾은 광역지자체에서 식별가능한 수준의 두 음절이 있는지를 확인해, 표준화된 광역지자체명으로 대체

 

이렇게만 설명하면, 어려우니 아래에서 하나씩 살펴보자.


 

 

 

1번은 크게 어렵지 않으므로, 제외하고 2번째 부터 살펴보자.

 

2. 광역지자체 명명에 대한 일정한 규칙을 찾아 정규식을 만들어, 광역지자체만 찾는다.

 

- 광역지자체는 크게 -도로 끝나거나, -시로 끝나는 특별시나 광역시 같은 곳들이다.

- 하지만, 강원도를 강원이라고 2글자로 줄여서 말하는 경우가 생긴다.

- 따라서, 정규식의 패턴은 ~도, ~시, ~@로 되는 정규식을 만든다. (Sido_pattern)

- 그리고 기존 데이터프레임의 주소에서 위 패턴으로 된 데이터를 찾아, 문자열로 반환한다. (Sido)

import re
Sido_pattern = r'(\w+[원,산,남,울,북,천,주,기,시,도,전,구])?'

for addr in df['address']:
    Sido = re.search(Sido_pattern, addr).group()

 

 

3. 2단계에서 찾은 광역지자체에서 식별가능한 수준의 두 음절이 있는지를 확인해, 표준화된 광역지자체명으로 대체

 

- 광역지자체를 줄여 명명해도, 최소 2글자 이상은 됨 (e.g. 충남, 충북, 강원, 경기 ...)

- 그렇기 때문에 앞의 두글자만으로 모든 문자열을 구분가능할 것이라는 가설

- 1단계에서 만든 Sido_list의 표준명과 주소데이터 문자열과 비교(item.find(Sido[i])), 여기서 i는 첫 두글자만 할 것이기 때문에 0과 1만 들어감

- 위 2단계와 합쳐보면 아래처럼 작성 가능

import re

Sido_list =['서울특별시', '부산광역시', '대구광역시', '인천광역시', '광주광역시', '대전광역시',
 '울산광역시', '세종특별자치시', '경기도', '강원도', '충청북도', '충청남도',
 '전라북도', '전라남도', '경상북도', '경상남도', '제주특별자치도']

for addr in df['address']:
    Sido = re.search(Sido_pattern, addr).group()
    # 표준이름과 비교하기
    for item in Sido_list:
    	if item.find(Sido[0]) != -1: # 첫글자가 표준이름 item 중에 있는지 확인
           if item.find(Sido[1]) != -1: # 두번째 글자가 표준이름 item 중에 있는지 확인
            	Sido_new = item
                print(Sido_new)

 

위 코드를 940여개의 데이터에 돌려본 결과, 2개 오류 발생건을 제외하면 잘 돌아가는 것을 확인할 수 있다. 다만, 실무적으로 활용할 때 오류 발생한 건들에 대해 다시 살펴보기 위해 예외처리 등 몇 가지 작업을 해주자. 

 

- 오류가 발생했을 때, 처리할 수 있도록 try ~ except 사용. 

- 발생한 오류의 위치와 데이터의 형태를 확인할 수 있게끔, 인덱스(ind)와 오류 리스트(error_list) 정의

- 그리고 이를 except절에서 ind, addr를 error_list에 넣고, for문 continue

 

import re

Sido_list =['서울특별시', '부산광역시', '대구광역시', '인천광역시', '광주광역시', '대전광역시',
 '울산광역시', '세종특별자치시', '경기도', '강원도', '충청북도', '충청남도',
 '전라북도', '전라남도', '경상북도', '경상남도', '제주특별자치도']

ind = 0
error_set = set()

for addr in df['address']:
    Sido = re.search(Sido_pattern, addr).group()
    for item in Sido_list:
        try:
            if item.find(Sido[0]) != -1:
                if item.find(Sido[1]) != -1:
                    Sido_new = item
                    print(Sido_new)
                    ind += 1
        except:
            error_set.add((ind, addr))
            continue

에러난 것들을 확인해보니까, 하나는 광역지자체가 누락된 경우이고 나머지 하나는 띄어쓰기를 해서 인식을 못한 경우였다. 좀 더 가다듬을 필요는 있겠지만, 급할 때는 실무적으로 활용은 가능할 듯하다. 

 

Version
Python = 3.8 (64 bit)

 

참고:

파이썬 표준라이브러리, https://docs.python.org/3/library/re.html

우아한 형제들 블로그, "쉽고 재밌는 정규식 이야기", https://techblog.woowahan.com/2505/

당근마켓 블로그, "주소 인식을 위한 삽질의 기록", https://medium.com/daangn/%EC%A3%BC%EC%86%8C-%EC%9D%B8%EC%8B%9D%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%82%BD%EC%A7%88%EC%9D%98-%EA%B8%B0%EB%A1%9D-df2d8f82d25

 


수정사항

수정일시 문제 해결
2021-08-22 error가 있으면, 루프를 돌면서 리스트에 중복 리스트 대신 set()를 사용하고, add로 추가

 

반응형
반응형

최근 금감원에서 제공하고 있는 데이터를 접근하는 방법에 대해 고민하고 있다.

 

개인적으로 투자를 하고 있는 상황에서, 현재 금융업종에 종사하고 있지 않는 나에게 많은 데이터를 제공하고 있는 금감원은 사실 가장 중요한 정보의 출처이다.

 

하지만, 모든 회사에서 제공하는 사업보고서에 있는 내용들을 읽어야 하지만, 이 행위를 조금 더 편리하게 하는 방법에 대해 고민을 하고 있다.

 

이를 위해서 금감원은 필요한 데이터에 접근을 용이하게 하고, OPEN API를 적극적으로 활용할 수 있도록 최근 새롭게 홈페이지를 개편했다.

 

아래는 그 홈페이지이다. 옛날 DART 홈페이지보다는 훨씬 깔끔해졌고, 좀더 OPEN API 활용에 초점을 맞춘 듯 하다.

 

https://opendart.fss.or.kr/

 

전자공시 OPENDART 시스템

DART의 공시정보 개방전용 홈페이지 「Open DART」서비스의 시범운영을 '20.1.21(화) 부터 실시합니다. 동 서비스에서는 기존 DART 에서 제공중인 「오픈API」와 「공시정보 활용마당」 서비스를 확대 개편하여 보다 다양한 공시정보를 편리하게 활용하실 수 있습니다. ※ 전체 공시서류를 오픈API로 제공할 예정이나, 시범운영기간에는 최근 3개년치의 공시서류를 제공합니다. '소통창구'-'의견수렴' 게시판을 통해 자유롭게 이용 의견을 남겨주시면 서비

opendart.fss.or.kr

여기서 내용들을 살펴보면, 회사의 코드들이 필요한 경우가 많은데 OpenDART에서 제공하는 CORP_CODE.xml 파일을 읽어서 우리가 필요한 코드를 추출해보려고 한다.

 

구현하려는 코드를 만들기 위해 하는 과정은 아래와 같다.

 

  1. XML 파일 데이터 구조를 파악한다. (우리가 필요한 데이터의 모양 파악)
  2. python에서 해당 XML파일을 읽게 만든다.
  3. 우리가 필요한 데이터를 찾아서 리스트에 추가한다.

 

우선, 우리가 데이터를 긁어와야 하는 파일을 살펴보면 아래와 같다.

(방법은 일반적인 웹브라우저에 파일을 드래그&드랍하면 된다.)

 

 

회사별로 list를 구성하고 그 아래에 corp_code, corp_name 등등 정보로 구성되어 있다.

 

이런 데이터를 읽기 위해서 나는 python에서 기본적으로 제공하고 있는 xml.etree.ElementTree 라는 모듈을 사용했다.

import xml.etree.ElementTree as ET

def main():
    tree = ET.parse('CORPCODE.xml')
    root = tree.getroot()
    
    print(root[0][0].text)
    print(root[0][1].text)

if __name__ == "__main__":
    main()

데이터를 잘 읽고 있는지 확인하기 위해서 print를 사용했고, 위의 사진과 비교해보면 첫번째 "다코", "00434003"이란 코드가 잘 출력되고 있는 것을 확인할 수 있다.

 

 

 

그럼 XML 파일을 잘 읽는 것은 확인했고, 우리가 원하는 코드와 회사명 데이터를 추출해보자.

 

추출해서 리스트에 추가하는 코드는 아래와 같다. root =~까지는 동일하고, 밑에 리스트 부분만 추가했다. 

import xml.etree.ElementTree as ET

def main():
    tree = ET.parse('CORPCODE.xml')
    root = tree.getroot()
    
    corp_code_list = []                  #코드를 넣어 둘 리스트
    corp_name_list = []                  #회사명을 넣어 둘 리스트
    
    for i in root.findall('list'):       #괄호 안의 element(여기선 'list')를 모두 찾는다.
        corp_code_list.append(i[0].text) #해당 element에 있는 [0]번째, 즉 코드를 리스트에 추가
        corp_name_list.append(i[1].text) #해당 element에 있는 [1]번째, 회사명을 리스트에 추가       

    print(corp_code_list[0])
    print(corp_name_list[0])

if __name__ == "__main__":
    main()

 

잘 구현됐는지 확인하기 위해서 print를 이용해 위 코드와 동일한 결과가 나오는지 확인해봤고, 결과는 성공이다.

 

(위 코드에서는 print가 생략되어 있지만, 처음 코드블럭에서 print했던 것이랑 비교를 위해 2개가 나온 캡처를 가져옴)

 

 

요새는 XML보다는 JSON을 활용하려고 한다지만, 공공데이터를 이용해서 무엇인가를 하려면 XML로 만들어진 경우가 많다.

 

따라서, XML을 읽는 xml.etree.ElementTree 모듈에 대한 공부가 필요할 수 있다. 더 심화된 내용이 필요하다면, 아래 URL로 들어가서 찾아보면 유용한 내용들을 찾을 수 있을 것이다.

 

https://docs.python.org/3.8/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.iterfind

 

xml.etree.ElementTree — The ElementTree XML API — Python 3.8.2 documentation

Source code: Lib/xml/etree/ElementTree.py The xml.etree.ElementTree module implements a simple and efficient API for parsing and creating XML data. Changed in version 3.3: This module will use a fast implementation whenever available. The xml.etree.cElemen

docs.python.org

 


 

주1) 원한다면 앞에서 사용했던 openpyxl 모듈을 이용해 여기에 리스트로 있는 것들을 엑셀에 넣어서 우리가 사용하기 더 편한 형태로 바꿔 사용할 수도 있다. (물론, 여기 코드는 사실 엑셀로 바꿔서 사용할 유인이 없다. 그 이유는 주2에)

 

주2) 여기서 잘 살펴보면 대략 7만여개 정도 코드가 존재하는데, 이는 금감원에 등록된 모든 회사나 투자기구 등을 포함하고 있다. 아마도 공시 의무가 있는 모든 주체에 대한 정보를 포함하고 있다.

반응형
반응형

최근 3~4년간 사람들이 부쩍 미세먼지에 대한 관심이 높아졌다. 개인적인 생각은 미세먼지에 대해서 과거에 비해 심해졌다기 보다, 사람들의 인식이 삶의 질에 더 관심이 많아지면서 air quality에도 관심을 가졌기 때문일 것이라 생각하는데 내 생각이 맞는지 아니면 진짜 대기오염 수치가 나빠진 것인지에 대해 직접 데이터를 크롤링해 살펴보고자 한다.

 

크롤링 하기 전에, 다른 방법으로 공기오염 정도에 대한 통계를 제공하는지 살펴보려고 airkorea 사이트에 접속했다.

 

 

사이트에서 통계연보 및 월보의 형태로 데이터를 제공하기는 하지만, 매년 혹은 매월 데이터를 다운받아 취합하는 과정은 상당히 귀찮은 일이다.

 

 

내가 구현하고 싶은 것은 다음과 같다.

 

- 공공데이터 포털에 제공하는 api에 접속

- '종로구'에 있는 측정소에서 매월 측정한 데이터를 읽고, 리스트에 저장한다.

- 리스트에 저장된 데이터를 엑셀에 옮겨 저장한다.

 

 

이를 위해서 공공데이터 포털(data.go.kr) 내에 있는 '대기오염통계 서비스'에 접속해 api에 인증키를 받자. 아래 링크를 통해 접속이 가능하며, 아래 스크린샷의 활용신청을 통해 인증키를 받을 수 있다. (물론, 이후에 마이페이지에서 일반 인증키를 받아야 한다.)

 

https://www.data.go.kr/dataset/15000583/openapi.do?mypageFlag=Y 

 

공공데이터포털

국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Dataset)와 Open API로 제공하는 사이트입니다.

www.data.go.kr

위의 일련의 과정을 거친 후에, 참고문서를 열어보면 내가 찾고자 하는 대기오염 통계에 대한 설명은 33p 부터 있으니 참고하기 바란다.

 

34p 하단에 있는 REST(URI)를 살펴보자.

 

http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnStatsSvc/getMsrstnAcctoLastDcsnDnsty?stationName=종로구&searchCondition=DAILY&pageNo=1&numOfRows=10&ServiceKey=서비스키

 

stationName : 측정소명

searchCondition : 데이터의 구분 조건인데, 매일(DAILY), 월별(MONTH), 연도별(YEAR)로 구분된다.

numOfRows : 데이터 수

ServiceKey : 내가 받은 인증키

 

위 URI에 서비스키 부분을 내 인증키로 바꿔 크롬 검색창에 검색해보면, 아래와 같이 나온다.

(searchConditon은 'MONTH', numOfRows는 500으로 설정)

 

xml로 쓰여진 미세먼지 통계 데이터에 올바르게 접근하고 있음을 확인할 수 있다. 이 페이지를 끝까지 내려보면 2018년 말까지 데이터만 등록되어 있는데, 아마 2019년 내용을 아직 업데이트하지 못했나 보다. 

 

(원한다면 한국환경공단에 민원은 넣거나 정보공개 청구를 하면 되긴 하지만, 공공기관 직원의 노고를 아는 나로서는 그렇게 하지 않았다.)

 

 

- 공공데이터 포털에 제공하는 api에 접속,

- '종로구'에 있는 측정소에서 매월 측정한 데이터를 읽고, 리스트에 저장한다.

- 리스트에 저장된 데이터를 엑셀에 옮겨 저장한다.

 

내가 원하는 동작을 하도록 만든 코드는 아래와 같다.

from openpyxl import Workbook
from bs4 import BeautifulSoup
import requests

def main():
    
    date = []                                 #측정한 연월을 저장할 리스트
    pm10 = []                                 #pm10 데이터를 저장할 리스트
    
    cert_key = "내가 받은 인증키"
    url = "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnStatsSvc/getMsrstnAcctoLastDcsnDnsty?
    stationName=종로구&searchCondition=MONTH&pageNo=1&numOfRows=500&ServiceKey=" + cert_key  

    req = requests.get(url)                    #공공데이터에 등록된 xml파일에 접근
    html = req.text
    soup = BeautifulSoup(html, 'html.parser')
    
    for i in soup.find_all("datatime"):        #측정년월, pm10평균을 각 리스트에 저장
        date.append(i.text)
        
    for i in soup.find_all("pm10avg"):
        pm10.append(i.text)
   
    wb = Workbook()                            #새로운 엑셀을 만들고, 그 이름을 미세먼지 통계로 지정   
    ws = wb.active
    file_name = '미세먼지 통계.xlsx'    
    
    ws.cell(row=1, column=1).value = "date"    #첫 행, 열 이름 지정
    ws.cell(row=1, column=6).value = "pm10"    #첫 행, 열 이름 지정
    
    for row_index in range(1,len(date)+1):     #리스트에 저장된 데이터를 순서대로 write
        ws.cell(row=row_index+1, column=1).value = date[(row_index-1)]
        ws.cell(row=row_index+1, column=2).value = pm10[(row_index-1)]
    
    wb.save(filename = file_name)              #마지막으로 파일이름으로 저장
    
if __name__ == "__main__":
    main()

 

이전에 데이터 크롤링(crawling, scraping)와 엑셀에 저장하는 것을 참고하면 될 듯하고, 여기서 조금 새로운 것은 엑셀 워크북을 만들어서 새롭게 저장하는 코드를 사용했다는 점만 다르다.

 

비슷한 방식으로 공공에서 제공하는 데이터들을 비교적 쉽고 빠르게 가져올 수 있어 다른 활용에 무궁무진해 보인다.

 

 

 

주1) XML을 읽어올 때, 대문자를 사용하지 않도록 한다. 예시에서 dataTime 이런식으로 측정년월이 되어 있는데, python으로 읽을 때는 find_all("datatime")으로 사용해야 한다.

 

주2) 위에서는 pm10만 가져왔지만, 오존이나 이산화황 등 다양한 대기질 관련 데이터도 추가 가능하다. 다만, 리스트가 추가되고 코드가 더 길어질 것이기 때문에 생략하기로 한다.

 

주3) 인증키는 다른사람과 공유하지 않도록 주의한다. 

 

주4) 리스트에 text형태로 저장하기 때문에 바로 활용하기가 어렵다. 따라서, 엑셀에서 제공하는 문자값을 숫자로 변환하는 기능을 활용하자. (사실, text가 아닌 숫자로 저장하는 방법을 아직 찾지 못했다.)

 

반응형
반응형

앞서 BeautifulSoup을 활용해 보도자료의 리스트의 형태로 제목들을 가져왔다.

 

https://seanpark11.tistory.com/10

 

파이썬으로 정부 보도자료 크롤링(crawling/scraping)하기

우리나라의 사회/산업/경제 전반적인 상황을 보기위해 신문도 참고할 자료 중 하나이긴 하지만, 각 정부 부처에서 제공하는 보도자료가 사실에 근접해서 보기에 훨씬 유용한 자료라 생각한다. 하지만, 매번 정부의..

seanpark11.tistory.com

 

하지만, 리스트는 python에 데이터를 저장하기엔 유용한 형태일지 몰라도 일반적으로 활용하기에는 제한이 된다.

 

따라서, 우리는 활용하기 편한 다른 형태로 데이터를 저장해야 하는데 그중 하나로 엑셀을 활용하고자 한다.

 

python을 이용해 excel을 동작하게 만드는 모듈은 여러가지가 있다고 하는데, 그중에서도 Openpyxl을 사용할 예정이다.

 

다른 모듈들보다 굉장히 활발하게 업데이트 하고 있는 모듈인 것 같다는 게, 처음 사용하던 때와도 조금씩 사용법이 달라져서 이번에 다시 이용해보면서 공부를 했어야 했다.

 

https://openpyxl.readthedocs.io/en/stable/tutorial.html

 

Tutorial — openpyxl 3.0.3 documentation

There is no need to create a file on the filesystem to get started with openpyxl. Just import the Workbook class and start work: A workbook is always created with at least one worksheet. You can get it by using the Workbook.active property: Sheets are give

openpyxl.readthedocs.io

위에 첨부한 document를 참고해서 나중에 틈틈이 공부할 필요가 있다. 

 

 

다시 본론으로 돌아가서, 내가 구현하고 싶은 동작은 다음과 같다.

 

이전에 저장했던 titles 리스트의 elements를 내가 이미 가지고 있는 press_gov.xlsx 파일 Sheet1에 저장하는 것이다.

 

 

Openpyxl을 활용한 excel 저장 코드는 다음과 같다.

from openpyxl import load_workbook  #이미 저장된 workbook을 불러오는 load_workbook을 import

def main():

########################################중 략################################################
    
    wb = load_workbook('press_gov.xlsx')      #( )안의 파일을 불러온다.
    sheet1 = wb.get_sheet_by_name('Sheet1')   #wb의 ( )안의 시트를 불러온다.
        
    for row_index in range(1,len(titles)+1):
        sheet1.cell(row=row_index, column=1).value = row_index             # 행번호 부여
        sheet1.cell(row=row_index, column=2).value = titles[(row_index-1)] # titles리스트 내의 요소를 입력
    
    wb.save(filename = 'press_gov.xlsx')   #위의 내용을 저장

if __name__ == "__main__":
    main()

 

 

이외에도 엑셀 파일하나를 만들어서 저장하는 방법도 있다. 아래처럼 말이다.

 

from openpyxl import Workbook

wb = Workbook()

 

하지만, 기존에 내가 활용해야 하는 엑셀 양식이 있어서 그것을 이용하기 위한 상황을 가정했기 때문에 Workbook을 사용하지 않았다. 

 

실제로 엑셀을 사용하는 많은 업무들이 기존에 있는 양식에 값만 넣으면 어떤 결과가 나오도록 하는게 시간을 단축하기에 더 바람직하니까 말이다.

 

어쨋든, 잘만 활용한다면 Openpyxl 모듈은 여러 군데에 활용할 수 있는 가능성이 보인다.

 

반응형
반응형

2021.10.04 - [Python/Web Crawling] - 파이썬으로 여러 페이지에 있는 정부 보도자료 크롤링하기_페이지네이션

우리나라의 사회/산업/경제 전반적인 상황을 보기위해 신문도 참고할 자료 중 하나이긴 하지만,

 

각 정부 부처에서 제공하는 보도자료가 사실에 근접해서 보기에 훨씬 유용한 자료라 생각한다.

 

하지만, 매번 정부의 모든 부처들에 방문해 보도자료를 찾아보기는 번거롭다.

 

이를 해결하기 위해서 정부 부처 보도자료의 제목들을 가져오는 크롤링 프로그램을 한번 생각해봤다.

 

  1. 각 정부 부처의 보도자료 관련 주소를 리스트를 만들고,
  2. 주소 리스트의 순서대로 보도자료의 제목들을 찾아 내가 원하는 제목리스트에 저장한다.

사실 실제로 사용하기 위해서는 위 과정보다 더 나아가야 하지만, 내가 원하는 보도자료 제목리스트를 만들기만 해도 유용하게 사용할 수 있으리라 생각한다.

(초보자라 모든 의견은 감사히 받습니다.)

 

우선, python에서 크롤링에 많이 사용하는 BeautifulSoup을 import한다.

import urllib.request
from bs4 import BeautifulSoup

 

그 다음 우리가 찾고 싶은 부처의 홈페이지에 방문한다. 

(여기서는 기획재정부랑 산업통상자원부를 활용할 예정이다.)

 

방문해서 F12를 눌러서 우리가 원하는 보도자료 제목이 어디에 저장되어 있는지 찾아보자.

 

 

기획재정부 보도자료 홈페이지

 

 

 

산업통상자원부 보도자료 홈페이지

 

 

기획재정부 보도자료 제목은 h3 밑의 a에 저장되어있고, 산업부 제목은 div의 "ellipsis"란 클래스 밑에 a에 저장되어 있음을 확인할 수 있다.

 

이걸 확인하고, 각 선택자(selector)를 마우스 우클릭해서 Copy > Copy Selector 로 복사하자.

 

각 홈페이지로 이동해서 주소(url)을 복사해 url_list에 요소로 만들고, 복사한 셀렉터들을 slect_list에 추가해 준다.

 

그리고 우리가 저장하고 싶은 보도자료 제목들을 넣어둘 titles 리스트도 만들어주자.

 

def main():

    titles = []	
    url_list = ["http://www.moef.go.kr/nw/nes/nesdta.do?bbsId=MOSFBBS_000000000028&menuNo=4010100",
                "http://www.motie.go.kr/motie/ne/presse/press2/bbs/bbsList.do?bbs_cd_n=81", 
                ]   
    slect_list = ["h3 > a",
                  'td.al > div > a']

 

복사한 셀렉터를 slect_list에 추가해주는데, 실제 복사 붙여넣기를 하면 위의 코드보다는 길게 입력이 된다. 이는 각 타이틀의 셀렉터를 특정해주기 때문에 그 경로가 세부적으로 추가되는 것이다.

 

(만약 복사한 그대로 쓰게 된다면 1페이지의 맨 위 혹은 특정 위치에 있는 제목만 긁어오게 된다.)

 

하지만, 우리는 페이지에 있는 특정 위치 뿐만 아니라 모든 보도자료 제목을 긁어오고 싶기 때문에 특정 경로의 셀렉터를 선택하는 게 아니라 위에 리스트처럼 만들어서 사용하기로 한다.

 

    for i in range(len(url_list)):                      #url_list 리스트 모든 요소 내에서
        url = url_list[i]                               #url_list 중 i번째를 url에 저장한다. 
        sourcecode = urllib.request.urlopen(url).read() 
        soup = BeautifulSoup(sourcecode, "html.parser")
        
        #저장된 url을 BeautifulSoup에 따라 html 구조를 분석. 사실 위에 2줄은 그냥 암기해도 됨
        
        selector = slect_list[i]                        #slect_list 중 i번째를 selector에 저장

        for j in soup.select(selector):
            titles.append(j.text)                       
            
        #저장된 selector를 찾아서 text를 추출한다음 titles 리스트에 첨가(append)한다

    print(titles)
    #titles에 잘 들어갔는지 확인한다.

#로 설명된 구문을 실제 print를 해보면 아래와 같이 나온다.

 

 


['마스크 등 시장교란행위 방지 추진상황 점검회의(2.21)', "구윤철 차관 '정부 출자기관 간담회' 개최", '세계은행(WB) 한국혁신주간 행사를 통해 한국의 개발 경험 및 최근 주요 정책 등을 공유', '홍남기 부총리, 한러 수교 30주년 제1차 준비위원회 개최', '김용범 기획재정부 제1차관 등 정부대표단, 세계은행(WB) 한국혁신주간 행사 참석을 위해 출국', '홍남기 부총리, 출장계획 변경 및 G20 재무장관회의 참석', '코로나19 관련 긴급 항공 운송물품 관세 인하 지원', '재정혁신 TF 출범회의 겸 제1차 지출구조개혁단 회의', '[보도참고] 2019년말 대외채무 동향 및 평가', '[보도참고]2019년 4/4분기 가계동향조사(소득부문) 결과', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(참고자료)제8차 한-일 수출관리정책대화 3...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(설명자료)한전공대 학생들에 대한 장학금은 ...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t무역금융 3.1조원 추가 지원 등 코로나19...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t무역위원회, 「천연가스 압축기 특허권 침해」...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t신학기용품, 아동용 봄철 의류 등 36개 제...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t산업기술 R&D 시스템, 대대적 혁신...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t「제5차 집단에너지 공급 기본계획(안)」공청...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(설명자료)두중은 최근 수년간 세계 석탄화력...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(설명자료)정부는 제9차 전력수급기본계획을 ...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n', '\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(설명자료)전기차 충전요금 할인특례 개편은 ...\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n']

 


기획재정부 쪽은 제목만 잘 추출되는 것을 확인할 수 있는데, 산업부에서 보도자료 제목에 탭(\t)이나 엔터(\n)가 있는 것을 확인할 수 있다. 

 

이것을 깔끔하게 해결해주기 위해서 strip()함수를 넣어주자. 

 

j.text -> j.text.strip()

    for i in range(len(url_list)):
        url = url_list[i]
        sourcecode = urllib.request.urlopen(url).read()
        soup = BeautifulSoup(sourcecode, "html.parser")
        
        selector = slect_list[i]

        for j in soup.select(selector):
            titles.append(j.text.strip())

    print(titles)

 

이에 대한 프린트 결과는 아래와 같이 깔끔하게 잘 나온 것을 확인할 수 있다.


['마스크 등 시장교란행위 방지 추진상황 점검회의(2.21)', "구윤철 차관 '정부 출자기관 간담회' 개최", '세계은행(WB) 한국혁신주간 행사를 통해 한국의 개발 경험 및 최근 주요 정책 등을 공유', '홍남기 부총리, 한러 수교 30주년 제1차 준비위원회 개최', '김용범 기획재정부 제1차관 등 정부대표단, 세계은행(WB) 한국혁신주간 행사 참석을 위해 출국', '홍남기 부총리, 출장계획 변경 및 G20 재무장관회의 참석', '코로나19 관련 긴급 항공 운송물품 관세 인하 지원', '재정혁신 TF 출범회의 겸 제1차 지출구조개혁단 회의', '[보도참고] 2019년말 대외채무 동향 및 평가', '[보도참고]2019년 4/4분기 가계동향조사(소득부문) 결과', '(참고자료)제8차 한-일 수출관리정책대화 3...', '(설명자료)한전공대 학생들에 대한 장학금은 ...', '무역금융 3.1조원 추가 지원 등 코로나19...', '무역위원회, 「천연가스 압축기 특허권 침해」...', '신학기용품, 아동용 봄철 의류 등 36개 제...', '산업기술 R&D 시스템, 대대적 혁신...', '「제5차 집단에너지 공급 기본계획(안)」공청...', '(설명자료)두중은 최근 수년간 세계 석탄화력...', '(설명자료)정부는 제9차 전력수급기본계획을 ...', '(설명자료)전기차 충전요금 할인특례 개편은 ...']


이런 식으로 환경부, 교육부, 과학기술정보통신부 등 다양한 정부 부처의 보도자료 제목들을 크롤링해올 수 있다.

 

그리고 이번에는 리스트로 저장만 해뒀지만, 이것을 텍스트나 엑셀 등을 활용해 더 유용하게 사용할 수도 있을 것이다.

 

또 크롤링을 할 때 항상 유의해야 할 것은 이것을 긁어올 때, 저작권이나 어떤 문제가 발생할지에 대해 항상 생각하자.

(나같은 경우 이번에 그런 것들을 피하기 위해서 일부러 정부 부처의 보도자료를 선정한 것도 있다.)

 


참고하면 좋을 글:

 

2021.10.04 - [Python/Web Crawling] - 파이썬으로 여러 페이지에 있는 정부 보도자료 크롤링하기_페이지네이션

반응형