yunomuのブログ

酒とゲームと上から目線

HTMLパーサ

HTMLパーサに今まではTagSoupを使っていたんですけど、IOまわりをConduitで書き直してるついでになんか別のConduit対応パーサを使ってみようかなと思ってちょっと調べてみました。

とりあえず一番簡単というか最初に目についたのはhtml-conduitのsinkDocを使う方法。

import Data.Conduit
import qualified Data.Conduit.Binary as CB
import qualified Text.HTML.DOM as DOM

main :: IO ()
main = do
    doc <- runResourceT $ CB.sourceFile "test.html" $$ DOM.sinkDoc
    print doc

Text.XML.Documentが返ってくるので、あとは頑張りましょう。これほとんどxml-conduitじゃないか。

それと、同じモジュールのeventConduitを使う方法。

import Data.Conduit
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import qualified Text.HTML.DOM as DOM

main :: IO ()
main = do
    doc <- runResourceT $ CB.sourceFile "test.html" $= DOM.eventConduit $$ CL.consume
    print doc

これはConduitなので、なんか適当にconsumeしてもいいし、ちょっと使い勝手がいい気がする。

html-conduitを入れた時に入ったっぽいTagStreamでもだいたい似たようなことができるけど、こっちの方が使いやすそうな気がする。いや同じか。まあどっちでもいいかんじです。

import Data.Conduit
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import Text.HTML.TagStream

main :: IO ()
main = do
    doc <- runResourceT $ CB.sourceFile "test.html" $= tokenStream $$ CL.consume
    print doc

あとは、Text.HTML.TagStream.Parserを使って自分でパーサを組み立てる手もある。

このあたりは、Attoparsec用のHTMLパーサ関数ライブラリのようで、Data.Conduit.Attoparsec.sinkParserとかと組み合わせて使うんじゃないでしょうか。たぶん。

sinkDoc以外のやつらが結構使えるんじゃないかと思ってるんですが、別に今回は実装したわけでもないし、評価したわけでもないし、あーでもtagstreamが一番プリミティブっぽいからこの場合は無難かもなぁと思ったくらいで、あんまし意味は無いです。評価順を考えると実はどっちも大して変わらないのかもしれない。結局みんなTagStream.Parserにいきつくっぽいし、eventConduitかtokenStreamあたりがちょうどいいんじゃないでしょうか。
それはそうと今更ですがConduit.Listってかなり便利ですね。