파이썬(Python)과 BeautifulSoup을 이용하여 HTML Parser를 만들어보자.
얼마 전 Node.js를 이용하여 간단한 Parser를 만들어보았습니다.
오늘은 Python을 이용하여 비슷한 파싱 프로그램을 만들어보려고 합니다.
Python 3 설치 확인
일단 현재 서버의 Python 환경을 확인해봐야 합니다.
# python Python 2.6.6 (r266:84292, Jun 18 2012, 14:18:47) [GCC 4.4.6 20110731 (Red Hat 4.4.6-3)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>>
아쉽게도 2.6이네요. 저는 3.x를 선호하니까 우선 최신 버전을 설치하고 진행하겠습니다.
먼저 3.6.1 설치를 위한 소스 파일을 다운로드받았습니다.
$ tar xzf Python-3.6.1.tgz $ cd Python-3.6.1 $ ls -tlr total 1020 drwxr-xr-x 24 501 501 4096 Mar 21 15:32 Tools -rw-r--r-- 1 501 501 101166 Mar 21 15:32 setup.py -rw-r--r-- 1 501 501 9066 Mar 21 15:32 README.rst drwxr-xr-x 3 501 501 4096 Mar 21 15:32 Python -rw-r--r-- 1 501 501 41359 Mar 21 15:32 pyconfig.h.in drwxr-xr-x 2 501 501 4096 Mar 21 15:32 Programs drwxr-xr-x 2 501 501 4096 Mar 21 15:32 PCbuild drwxr-xr-x 5 501 501 4096 Mar 21 15:32 PC drwxr-xr-x 2 501 501 4096 Mar 21 15:32 Parser drwxr-xr-x 4 501 501 4096 Mar 21 15:32 Objects drwxr-xr-x 13 501 501 4096 Mar 21 15:32 Modules drwxr-xr-x 2 501 501 4096 Mar 21 15:32 Misc -rw-r--r-- 1 501 501 58935 Mar 21 15:32 Makefile.pre.in drwxr-xr-x 8 501 501 4096 Mar 21 15:32 Mac -rw-r--r-- 1 501 501 12773 Mar 21 15:32 LICENSE drwxr-xr-x 33 501 501 4096 Mar 21 15:32 Lib -rwxr-xr-x 1 501 501 7122 Mar 21 15:32 install-sh drwxr-xr-x 2 501 501 4096 Mar 21 15:32 Include drwxr-xr-x 2 501 501 4096 Mar 21 15:32 Grammar drwxr-xr-x 18 501 501 4096 Mar 21 15:32 Doc -rw-r--r-- 1 501 501 159739 Mar 21 15:32 configure.ac -rwxr-xr-x 1 501 501 483582 Mar 21 15:32 configure -rwxr-xr-x 1 501 501 35740 Mar 21 15:32 config.sub -rwxr-xr-x 1 501 501 42856 Mar 21 15:32 config.guess
빌드합니다.
$ ./configure --prefix=/app/python361 $ make $ make install
빌드가 정상 완료되면 해당 경로를 PATH에 추가하여 줍니다. 여기서는 /app/python361/bin입니다.
이제 python3, pip3과 같은 실행 파일을 사용할 수 있는 상태가 되었습니다.
pip3가 꼭 필요합니다. BeautifulSoup를 받아야 하기 때문이죠.
(* pip에 대해서는 /index.php/aws/752 에서도 언급이 된 바 있더군요.)
BeautifulSoup 설치
그러면 이제 BeautifulSoup를 설치하도록 합니다.
# pip3 install beautifulsoup4
Collecting beautifulsoup4
Downloading beautifulsoup4-4.6.0-py3-none-any.whl (86kB)
100% |????????????????????????????????| 92kB 480kB/s
Installing collected packages: beautifulsoup4
Successfully installed beautifulsoup4-4.6.0
뒤 예제에서는 requests 모듈도 사용합니다. 설치되어 있지 않다면 함께 설치해 둡니다.
# pip3 install requests
웹 페이지 소스 가져오기
BeautifulSoup가 잘 설치되었다면 실제로 페이지 웹 소스를 획득해봅니다.
아래 Python 프로그램은 sarc.io의 웹 소스를 읽어 들인 후 output.html 파일에 그 내용을 저장하는 일을 합니다.
import urllib.request
if __name__ == "__main__":
print("Parsing HTML...")
requestUrl = urllib.request.Request("http://sarc.io")
parsingHtml = urllib.request.urlopen(requestUrl).read()
print(parsingHtml)
f = open("./output.html", "wb")
f.write(parsingHtml)
f.close()
여기서는 바이너리 모드(wb)로 저장했습니다. urlopen().read()의 결과가 문자열이 아니라 바이트이기 때문입니다. 저장된 output.html을 열어보면 실제로 어떤 HTML이 내려왔는지 확인할 수 있습니다.
미션: Apache 프로젝트 이름만 출력하기
< 미션 : www.apache.org 홈페이지의 개별 아파치 프로젝트 이름만 뽑아 출력한다 >
꼭 먼저 www.apache.org 웹 소스를 확인해 보시기 바랍니다.
<div class="col-md-12" id="by_name"><!-- id for eventual use by whimsy; do not move -->
<h4>By Name</h4>
<div class="col-lg-4 col-sm-4">
<div class="row">
<div class="col-lg-6 col-md-12 border-right">
<ul class="list-unstyled" style="margin-bottom: 0px;">
<li><a href="http://httpd.apache.org/" title="Apache Web Server (httpd)">HTTP Server</a></li>
<li class="letter-header">A</li>
<li><a href="http://accumulo.apache.org/" title="Sorted, distributed key/value store">Accumulo</a></li>
<li><a href="http://ace.apache.org/" title="Centralized life cycle management and deployment of OSGi based and related modular software artifacts for distribution.">ACE</a></li>
<li><a href="http://activemq.apache.org/" title="Distributed Messaging System">ActiveMQ</a></li>
<li><a href="http://airavata.apache.org/" title="Workflow and Computational Job Management Middleware">Airavata</a></li>
<li><a href="http://allura.apache.org/" title="Forge software for hosting software projects">Allura</a></li>
<li><a href="http://ambari.apache.org/" title="Hadoop cluster management">Ambari</a></li>
<li><a href="http://ant.apache.org/" title="Java-based build tool">Ant</a></li>
<li><a href="http://any23.apache.org/" title="Anything to Triples">Any23</a></li>
<li><a href="http://apex.apache.org/" title="Enterprise-grade unified stream and batch processing engine">Apex</a></li>
<li><a href="http://apr.apache.org/" title="Apache Portable Runtime libraries">APR</a></li>
<li><a href="http://archiva.apache.org/" title="Build Artifact Repository Manager">Archiva</a></li>
<li><a href="http://aries.apache.org/" title="Enterprise OSGi application programming model">Aries</a></li>
링크 태그 추출하기
일단 <a ...>xxx</a> 형태를 뽑아내는 프로그램을 만들어봅니다.
from bs4 import BeautifulSoup
import requests
response = requests.get('http://www.apache.org')
parsedHtml = response.text
soup = BeautifulSoup(parsedHtml, 'html.parser')
print(soup.find_all('a'))
이걸로는 쉽지 않겠네요. 링크 태그가 모두 출력되기 때문에 원하는 프로젝트 이름만 걸러내기 어렵습니다.
텍스트와 title 속성 확인하기
이번에는 텍스트만 추출하여 보겠습니다.
from bs4 import BeautifulSoup
import requests
response = requests.get('http://www.apache.org')
parsedHtml = response.text
soup = BeautifulSoup(parsedHtml, 'html.parser')
links = soup.find_all('a')
for link in links:
print(link.text)
아니면 title 속성만 출력해볼 수도 있습니다.
from bs4 import BeautifulSoup
import requests
response = requests.get('http://www.apache.org')
parsedHtml = response.text
soup = BeautifulSoup(parsedHtml, 'html.parser')
links = soup.find_all('a')
for link in links:
print(link)
if link.has_attr('title'):
print(link.attrs['title'])
By Name 영역 이후의 링크만 출력하기
이런저런 시행착오 끝에 아래처럼 정리할 수 있었습니다.
from bs4 import BeautifulSoup
import requests
response = requests.get('http://www.apache.org')
parsedHtml = response.text
soup = BeautifulSoup(parsedHtml, 'html.parser')
links = soup.find_all()
start = False
for link in links:
if start == True:
if link.has_attr('href') and link.has_attr('title'):
print(str(link.text))
if str(link) == '<h4>By Name</h4>':
start = True
print('True')
핵심은 <h4>By Name</h4>가 나온 이후부터 링크를 검사하는 것입니다. 그리고 href와 title 속성을 모두 가진 태그만 골라 출력하면, 단순한 알파벳 헤더나 다른 메뉴를 어느 정도 제외할 수 있습니다.
테스트할 때는 먼저 전체 HTML을 파일로 저장해 두고, 브라우저의 개발자 도구나 텍스트 검색으로 기준이 되는 태그를 확인해보면 좋습니다. 웹 사이트의 HTML 구조는 바뀔 수 있으므로, 결과가 갑자기 달라진다면 먼저 원본 HTML에서 By Name 영역이 그대로 유지되고 있는지 확인해야 합니다.