Development

파이썬(Python)과 BeautifulSoup을 이용하여 HTML 파싱 (HTML Parser 만들기)

강철지그·2017년 7월 9일·조회 9,604

파이썬(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>가 나온 이후부터 링크를 검사하는 것입니다. 그리고 hreftitle 속성을 모두 가진 태그만 골라 출력하면, 단순한 알파벳 헤더나 다른 메뉴를 어느 정도 제외할 수 있습니다.

테스트할 때는 먼저 전체 HTML을 파일로 저장해 두고, 브라우저의 개발자 도구나 텍스트 검색으로 기준이 되는 태그를 확인해보면 좋습니다. 웹 사이트의 HTML 구조는 바뀔 수 있으므로, 결과가 갑자기 달라진다면 먼저 원본 HTML에서 By Name 영역이 그대로 유지되고 있는지 확인해야 합니다.

댓글 0

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.