ネギのメモ帳

Twitterに書ききれないことをたまに書いたりするかもしれないスペース

Haskellで簡単なDL&テキスト処理

WEBサイトのページをちょちょっとダウンロードしてきて,
簡単なテキスト処理をして必要な情報を抜き出す的なやつ.
HTMLパーサすら要らないくらいの簡単な処理.
今までそういう仕事はRubyでちゃちゃっと書いてたけど,
Haskellでも書いてみようっていうコーナー.
パーサコンビネータ(Parsec)とやらの勉強も兼ねて.


import Network.HTTP ( getRequest
                    , getResponseBody
                    , simpleHTTP)
import Text.Parsec ( parse, try, (<|>), ParseError
                   , char, string, anyChar, alphaNum
                   , many1, between)
import Text.Parsec.String (Parser)
import Control.Applicative ((<$>), (<*>), (*>))
import Data.List (isInfixOf)

main :: IO ()
main = do
    html <- simpleGet uri
    mapM_ (print' . generateURI) $ filter containFLV $ lines html
      where print' = either print putStrLn

uri :: String
uri = "http://dengekibunko.dengeki.com/webradio/"

simpleGet :: String -> IO String
simpleGet uri = getResponseBody =<< simpleHTTP (getRequest uri) 

containFLV :: String -> Bool
containFLV = (".flv" `isInfixOf`)

generateURI :: String -> Either ParseError String
generateURI line = buildURI <$> (pickID line)
  where buildURI objectID = concat [dir, objectID, ".m4v"]
        dir = "http://seel.peevee.tv/userdir/h.264sd/"

pickID :: String -> Either ParseError String
pickID = parse pickOut ""
  where pickOut = search $ between (string "PeeVeeObject(\"") (string ".flv")
                  $ many1 $ alphaNum <|> (char '/')

search :: Parser a -> Parser a
search expr = foldr1 (\e f -> try e <|> shiftl f) (repeat expr)
  where shiftl = (anyChar *>)


mainは, URIからHTMLを得て, 行で分割して, ふるいに掛けて, 処理・書き出し.

search関数はこちらの記事を参照した
http://tnomura9.exblog.jp/15023782/ .
ただし明示的な再帰で表現されていたので,
畳み込みで表現しなおしてみた
(畳み込み方を間違えて無限ループと戦ったりしつつ).


正規表現をちょっと使えば出来る程度のことを
パーサコンビネータでやるのは若干面倒くさくも感じるが,
可読性やメンテナンス性, 再利用可能性を考えると確かに便利そう.


2015.06.26 追記
print'関数を定義して, LeftとかRightをshowしないようにした
(なんかもっとかっこいいやり方がある気もするが…).
調べた限りではeither関数を使うのがよさそう.