読者です 読者をやめる 読者になる 読者になる

YAMAGUCHI::weblog

土足で窓から失礼いたします。今日からあなたの息子になります。 当年とって92歳、下町の発明王、エジソンです。

HTMLParserでHTMLを解析する

動機

そういえばXMLだけじゃなくてHTMLの解析もやってみたくなったから。

方法

HTMLもXML系のパーサ使えばいいんだろうけど、HTMLParserってのがあるので使ってみました。使い方としてはざっくり下のコードみたいな感じ。

from HTMLParser import HTMLParser, HTMLParseError

class TestHTMLParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
    
    def handle_starttag(self, tag, attrs):
        attrs = dict(attrs) # タプルだと扱いにくいので辞書にする
        print 'start', tag
        if 'div' == tag and 'class' in attrs:
             print '-->', attrs['class']

    def handle_endtag(self, tag):
        print 'end', tag

    def handle_data(self, data):
        print data

parser = TestHTMLParser()
parser.feed(htmlbody)
parser.close()

これで、たとえばhtmlbodyの中身が

<html>
<head><title>てすと</title></head>
<body>
  <div>
    <div class='hoge'>ほげ</div>
  </div>
  <div class='piyo'>ぴよ</div>
</body>
</html>

だったとすると結果は

start html
start head
start title
てすと
end title
end head
start body
start div
start div
--> hoge
ほげ
end div
end div
start div
--> piyo
ぴよ
end div
end body
end html

という感じで、とりあえず階層構造関係なく

  • 開始タグにぶつかったらhandle_starttag()を呼ぶ
  • 終了タグにぶつかったらhandle_endtag()を呼ぶ
  • タグで挟まれてる中身はhandle_data()で処理

という簡単な作りになっているようです。
実際に「あるタグの中にいる」という様な処理をさせるときは自分はサブクラスのメンバとしてin_hoge_tagの様なフラグを用意して、handle_starttag()内でif文の条件として与えてます。
しかし階層構造が深くなると難しいので、ベストプラクティス的なものがあれば是非知りたいところです。