AtCoderの自動化プラグインを作りたくてスクレイピングを始めた。 Haskellでスクレイピングでググると Haskell で楽しい Web スクレイピング という記事がヒットした。 scalpelというライブラリを使えばいいらしい。
試しにAtcoderのページにログインして見ることにした。 が、意外と手こずった。まず、scalpel単体ではセッションの管理ができない。 仕方ないので、Cookieを操作するミドルウェアを書いていたのだが途中で車輪の再生産は良くないと思い ライブラリを探した。 次に見つけたのはhttp-conduit-browserというライブラリ。 使って見てビルドしようとしたらパッケージが壊れていた。よくよくpackage descriptionを読むと
This package creates a monad representing things that browsers do, letting you elegantly describe a browsing session. This package wraps the http-conduit package by Michael Snoyman. This package is deprecated, use http://hackage.haskell.org/package/wreq instead
This package is deprecated, use http://hackage.haskell.org/package/wreq instead
はい。 全く英語が読めていない。
ここで紹介されているwreqというライブラリを使って見る。 すごく直感的だ。GETでアクセスするにはget :: String -> IO (Response ByteString)を呼べばよく、 POSTでアクセスするにはpost :: Postable a => String -> a -> IO (Response ByteString)を叩けば良い。 そうそう、こういうのでいいんだよ。
セッションを管理したくなったらNetwork.Wreq.Sessionの関数を使えば良い。使い方も簡単だ。 これで楽勝かと思ったが、AtCoderにログインしようとしたらcsrf tokens mismatchと言われてしまう。 しばらく悩んだ結果、application/jsonでフォームを送信していたのをapplication/x-www-form-urlencodedに直したらログインできた。
これを開発している途中にhlintのバグを見つけた。(実際はhaskell-src-extsのバグだが) HIE上でHaskellのコードを編集すると、勝手にhlintをかけてhintを表示してくれるのだが、なぜか hlintがパーズエラーを投げていた。しかし手元のターミナルで直接hlint Program.hsを叩いても全然再現しない。 バグを特定したところ、HIEがhlintをかけるとき勝手にTypeApplicationsの拡張を有効にすることがわかった。
runLintCmd :: FilePath -> [String] -> ExceptT [Diagnostic] IO [Idea]
runLintCmd fp args = do
(flags,classify,hint) <- liftIO $ argsSettings args
let myflags = flags { hseFlags = (hseFlags flags) { extensions = EnableExtension TypeApplications:extensions (hseFlags flags)}}
res <- bimapExceptT parseErrorToDiagnostic id $ ExceptT $ parseModuleEx myflags fp Nothing
pure $ applyHints classify hint [res]謎の仕様ではあるが、まあなんらかの理由があってこういうことをしているのだろう。 とりあえず回避策としてはNoTypeApplicationsプラグマを書いておけば良い。 真の問題はTypeApplicationsを有効にするとhlintが正しくパーズしてくれないということだ。 バグを調べたところhaskell-src-extsのissue が見つかった。TypeApplicationsが有効になっていると@:という演算子がパーズできなくなるというバグだ。 面白いのは@=とか@@などの演算子は正しくパーズできるというところだ。@:で始まる演算子のみがパーズできない。
1行修正すれば治るバグのようだったので修正してプルリクを投げた。マージされると嬉しいな。
話は変わるがneovim + vim-lsp経由でHIEを動かしているのだが、重すぎて正直使い物にならない。 カーソルを1つ動かすたびに数百ms待たされる。どれを使えば良いのやら。