2015년 9월 16일 수요일

파이썬 3.4 BeautifulSoup로 웹페이지 파싱(parsing)하기

파이썬 BeautifulSoup로 웹페이지 파싱(parsing)하기

# ==> 필요한 자료 텍스트 형태로 뽑아내기, 아주 아주 기초편

나는 프로가 아니다. 그런데 남이 써놓은 걸 따라하면 도저히 안된다. 그래서 내가 하나하나 깨달은 대로 적어보았다. 

# 오늘 할 일은 riss 에서 '발성'이라고 검색한 100편의 논문제목을 뽑아내는 일이다. 사실 이것은 시스템에서 그렇게 어렵지 않게 할 수 있는 일이다. 그러나 100편이 10000편이 되면 어떨까? 또 십만편의 논문 제목을 뽑을 일은 없을까.

# 숫자가 늘어날수록 사람 손은 기계를 이길 수 없다. 예전부터 논문 찾는 프로그램을 개발해야 한다고 생각했는데, 그걸 실현해줄 수 있는 것이 바로 파싱이라는 것을 최근에 알게 되었다.

# 파싱, 웹페이지에 있는 정보를 뽑아내는 일이다.

# url을 불러들이는 함수는 파이썬 3.4를 기준으로 urllib.request.urlopen이다. 그런데 매번 비슷한 함수를 쓰기 때문에 일단 앞 부분(urllib.request)를 ur로 불러들이기로 한다.

# 프로그래머들 사이에서는 이렇게 자의적으로 축약하는 것이 가독성을 해칠 수 있으므로 피하라고 한다. 참고.
import urllib.request as ur
# '''인터넷 페이지에서 정보를 뽑아내는 것을 파싱(parsing)이라고 한다. 그 짓을 하기 위해서는 페이지의 정보가 겉으로 보이는 그래픽 이면에 수많은 html 명령어로 이뤄져 있음을 먼저 인지해야 한다. urlopen으로 불러들이면 페이지의 html을 그대로 불러들인다. 이 형태는 읽기도 힘들고 의미를 뽑아내기도 힘들다 그래서 BeautifulSoup이라는 모듈을 이용한다. 모듈은 또 뭣인가. 말하자면 부가기능이라고 보면 된다. 아이폰에 수많은 앱이 있듯이 파이썬에 수많은 모듈이 있다. BeautifulSoup는 그 이름처럼 굉장히 많은 사람들에게 칭찬받는 유용한 모듈 이다.'''

from bs4 import BeautifulSoup as bs


# riss에서 '발성'으로 검색한 후 url 주소를 복사한다(ctr-c, ctr-v)
# url 객체에 저장한다. 꼭 객체로 저장할 필요는 없다. 그러나 객체는 공짜니까 마음껏 만들어도 된다.

url='http://www.riss.kr/search/Search.do?detailSearch=false&viewYn=CL&query=%EB%B0%9C%EC%84%B1&queryText=&strQuery=%EB%B0%9C%EC%84%B1&iStartCount=0&iGroupView=5&icate=bib_t&colName=bib_t&exQuery=&pageScale=100&strSort=RANK&order=%2FDESC&onHanja=false&keywordOption=0&searchGubun=&p_year1=&p_year2=&dorg_storage=&mat_type=&mat_subtype=&fulltext_kind=&t_gubun=&learning_type=&language_code=&ccl_code=&language=&inside_outside=&fric_yn=&image_yn=&regnm=&gubun=&kdc=&resultSearch=false&listFlag=&keywordRecom=&keywordRecom='
r=ur.urlopen(url).read()

# 원래는 BeautifulSoup(r) 로 입력해야 한다. 여기에서는 편의를 위해서 글자수를 줄였다.
soup=bs(r)

# 이렇게 하면 r은 페이지의 html 이 저장되어 있고, soup는 html을 정보 빼내기 좋은 형태로 바꿔놓은 xml 형태의 새 객체가 된다. html 형태에서는 자료를 핸들링하기가 어렵지만 뷰티풀수프로 끓인 자료는 말 그대로 뷰티풀하게 자료를 빼낼 수 있다.

r
soup

# r과 soup를 각각 눌러봐서 도대체 이 파일들 안에 무엇이 들어있는지 비교해보자.

# 크롬 등 브라우져에서 F12를 누르면 복잡한 html 식이 간단하게 정리되어서 나온다. 예를 들어 이 홈페이지 내용에서 필요한 내용이 있는 곳으로 가려면 ctr-f를 누르고 찾기를 원하는 단어를 눌러주면 된다. 예를 들면 나는 논문 목록이 있는 부분을 찾기 위해서 '합창음악'을 찾았다. 그러면 html 언어를 다 모른다 하더라도 대강 '합창음악'이 어떤 명령어들로 둘러쌓여 있는지는 알 수 있다.

# 합창음악은 'srList'라는 이름의 클래스(class)에 둘러쌓여 있는 것을 확인할 수 있다. 나도 수퍼초보이고 명령어도 하나도 모르지만, '합창음악의 효과적인'이라는 문구가 넓게는 <div class='srList'>에 속해 있다는 것은 알 수 있었다. 물론 여러번의 삽질 끝에 일어난 일이다. 앞뒤 볼 것 없이 'div class='srList'에 있는 내용만 뽑아내서 객체에 저장해! 라는 문구는 아래와 같이 쓴다.

srlists=soup.find_all('div',{'class':'srList'})

# 문법을 자세히 보면 알겠지만, 문법은 굉장히 위계적으로 되어 있다. 위치를 지정하고, 상자를 지정하고, 모양을 지정하고, 그 뒤에 제목을 지정하는 식이다. 여기에서는 감으로 'label'에 내가 원하는 논문 제목정보가 있다는 것을 알았다. riss창에 보면 '발성'이라는 단어가 진하게 표시되어 있다. 그리고 자세히 보면 <strong>발성</strong> 으로 되어 있는 것을 확인할 수 있다. 그러므로 <strong>으로 씌운 놈은 필경 두껍게 강조하라는 뜻일 것이다. 문법을 다 몰라도 대강 우리가 찾으려는 정보가 label이라는 것을 확인한 뒤 label에 있는 텍스트만 뽑아내라는 명령을 수행해보고자 한다.





# 이미 우리는 srlists에서 전체 페이지 중 논문제목을 담고 있는 부분만 떼어내서 객체로 만들었다. 그 중에서도 논문제목에 해당하는 놈만 뽑아내라는 명령을 내린다. srlists 안에 들어가서 label이라는 명령어가 붙어있는 놈을 꺼내라는 명령은 아래와 같다.
sr=srlists[0].find_all('label')

# 힘들게 뽑아냈으면, 한번 출력해봐야 한다.

for s in sr:
    print(s.text)

# 드디어 논문 제목이 출력되는 것을 볼 수 있다.

import codecs
# 그럼 이 파일을 현재 디렉토리에서 bs_test.txt라는 파일로 저장해보자.
f=codecs.open('bs_test.txt','w','utf-8')

# 귀찮으므로 그냥 따라한다.
for s in sr:
    f.write(s.text)
    f.write('\n')
f.close()


* 위 그림은 최종적으로 내가 뽑은 논문제목이 같은 폴더의 'bs_test.txt'라는 파일로 저장된 결과이다. 저기까지 꼬박 일박이일이 걸렸다. 이런 식으로 정보를 꺼내서 자르고 잘라 필요한 형태로 만들면 된다. 나도 안다. 아직 멀었다. 그러나 시작은 했다.

# 조만간 해당 주제로 논문을 자동을 찾아내고 초록도 긁어서 인용할 수 있는 형태로 만들어주는 프로그램도 가능할 것이다. 


댓글 1개:

  1. 좋은 글 보고 갑니다. 파싱 보다가 유라시아 여행기 보다 나갑니다 ㅎ

    답글삭제