Python: HTMLParserでウェブページを解析する

cron jobで動作させるクローラの実装に有用なHTMLParserのサンプルコードを記載します。

1 urllib.requestでヘッダとボディを取得する

  • urllib.requestでレスポンスを取得し、ヘッダに記載された文字コードでデコードします。
  • ヘッダに文字コードがない場合は、DECODE_CHARSETに含まれる文字コードでデコードを試みます。
import urllib.request

DECODE_CHARSET = ['iso-8859-1', 'utf-8', 'shift-jis', 'euc-jp']


def example_decode(html):
    for charset in DECODE_CHARSET:
        try:
            decode_html = html.decode(charset)
            return decode_html
        except:
            pass
    return html


def example_http_get(url):
    with urllib.request.urlopen(url) as response:
        charset = response.headers.get_content_charset()
        if charset:
            return response.read().decode(charset)
        return example_decode(response.read())
    return ""

2 HTMLParserでボディを解析する

  • HTMLParserを継承したExampleParserを定義します。
  • ExampleParserのfeedを呼ぶと、handle_starttagが呼ばれます。
  • handle_starttagには以下の引数が格納され、コンストラクタで与えられたtagとattrに等しい場合に値を出力します。
<a href=http://example.com>
tag = a
attrs = [ [ 'href', 'http://example.com' ] ]
from html.parser import HTMLParser

class ExampleParser(HTMLParser):
    def __init__(self, tag, attr):
        super(ExampleParser, self).__init__()
        self.tag = tag
        self.attr = attr

    def handle_starttag(self, tag, attrs):
        if tag == self.tag:
            for attr in attrs:
                if attr[0] == self.attr:
                    print(attr[1])


def example_print(tag, attr, html):
    parser = ExampleParser(tag, attr)
    parser.feed(html)

3 サンプルコード

サンプルコードは以下の通りです。

#!/usr/bin/env python3

import sys
import urllib.request

from html.parser import HTMLParser

DECODE_CHARSET = ['iso-8859-1', 'utf-8', 'shift-jis', 'euc-jp']


def example_decode(html):
    for charset in DECODE_CHARSET:
        try:
            decode_html = html.decode(charset)
            return decode_html
        except:
            pass
    return html


def example_http_get(url):
    with urllib.request.urlopen(url) as response:
        charset = response.headers.get_content_charset()
        if charset:
            return response.read().decode(charset)
        return example_decode(response.read())
    return ""


class ExampleParser(HTMLParser):
    def __init__(self, tag, attr):
        super(ExampleParser, self).__init__()
        self.tag = tag
        self.attr = attr
        self.attrs = []

    def handle_starttag(self, tag, attrs):
        if tag == self.tag:
            for attr in attrs:
                if attr[0] == self.attr:
                    print(attr[1])


def example_print(tag, attr, html):
    parser = ExampleParser(tag, attr)
    parser.feed(html)


if __name__ == "__main__":
    argv = sys.argv
    argc = len(argv)
    if argc != 4:
        print('usage: %s <tag> <attr> <url>' % argv[0])
        exit(1)
    example_print(argv[1], argv[2], example_http_get(argv[3]))
    exit(0)

このコードをexample-html-parser.pyという名前で実行すると以下の出力を得られます。

$ ./example-html-parser.py a href http://yahoo.co.jp
http//...
http//...
http//...
<snip>

4 HTMLParserの弱点

特定のタグが不完全なウェブサイトの場合はfeedメソッド実行時にエラーが発生します(例えばimgタグがきちんと閉じていない場合)。