The jonki

呼ばれて飛び出てじょじょじょじょーんき

BeautifulSoupとhtml5lib

BeautifulSoupを使ったパースプログラムでこんなエラーがでたことはないだろうか。

"HTMLParser.HTMLParseError: malformed start tag"

要はBeautifulSoupのおつむじゃ理解出来ないHTMLタグがあるということ。どうやらスクリプトタグなんかを使ったちょっと複雑なものが理解出来ないみたい。

$ python hoge.py 
Traceback (most recent call last):
  File "hoge.py", line 78, in <module>
    main()
  File "hoge.py", line 40, in main
    soup = BeautifulSoup(html)
  File "build/bdist.linux-i686/egg/BeautifulSoup.py", line 1499, in __init__
  File "build/bdist.linux-i686/egg/BeautifulSoup.py", line 1230, in __init__
  File "build/bdist.linux-i686/egg/BeautifulSoup.py", line 1263, in _feed
  File "/usr/local/lib/python2.5/HTMLParser.py", line 108, in feed
    self.goahead(0)
  File "/usr/local/lib/python2.5/HTMLParser.py", line 148, in goahead
    k = self.parse_starttag(i)
  File "/usr/local/lib/python2.5/HTMLParser.py", line 226, in parse_starttag
    endpos = self.check_for_whole_start_tag(i)
  File "/usr/local/lib/python2.5/HTMLParser.py", line 301, in check_for_whole_start_tag
    self.error("malformed start tag")
  File "/usr/local/lib/python2.5/HTMLParser.py", line 115, in error
    raise HTMLParseError(message, self.getpos())
HTMLParser.HTMLParseError: malformed start tag, at line 947, column 153

そこでそいつは困るといったときに利用すると解決するかもしれないもの、それがhtml5libだったりする。妥当じゃないHTMLに対してもある程度は持ちこたえるみたい。ただ今度は逆にhtml5libは無理だけど、BeautifulSoupなら解析できるといHTMLもあるみたい。理由はこの辺の特性をあまり調べてないのでよう分からん。


「要はどっちもプログラムで試せばいいんでしょ」


と理系にあるまじき考えのもと回避策を探る。でやっぱ同じ考え(というと失礼だがw)に行き着く人が見つかるもんだ。

【taichino.com】 BeautifulSoupでパースエラーが出て困る件

とりあえずBeautifulSoupでやってみて例外発生したらhtml5libで、と何とも単純で分かりやすい方法。
実はアメーバブログをホゲホゲしてたときにこの問題にぶつかったんだけど、この方法で今のところ問題ない。

まぁほぼコピペなんだけど、HTMLParseError以外の例外をキャッチしたら終了するようにしておいた。パーサは別でもあとはsoupインスタンスを同じように扱えるから、この部分だけ修正で動くようになったのは非常に良かった。Perlで書き直そうかな…とか思ってたので危ないところだったw

html = urllib.urlopen(url).read()
try:
        soup = BeautifulSoup(html)
        print "case: BeautifulSoup"
except HTMLParseError:
        parser = HTMLParser(tree=treebuilders.getTreeBuilder("beautifulsoup"))
        soup = parser.parse(html) 
        print "case: html5lib"
except:
        sys.exit("パースエラー") 
環境

今回lxml使ってないけど入れるならこんな感じ。依存性に注意。easy_installはやっぱり便利だ。

# yum install libxml2 libxml2-devel
# yum install libxslt libxslt-devel 
# easy_install lxml

# easy_install html5lib

# easy_install BeautifulSoup