F#で2ch専用ブラウザをつくろう

この記事はF# Advent Calendar 2014 12日目の記事です。前日は @gab_km さんの『Visual F# Power Tools の紹介』でした。

F#でアプリケーションを書こう!

今回はいつもと少し趣向を変えて、F#でのアプリケーションプログラミングについて取り上げてみたいと思います。題材として取り上げるのは「2ちゃんねる」の専用ブラウザです。といってもその開発すべてを取り上げるとさすがに退屈になりそうなので、アプリケーションのキモとなる掲示板データのアクセス部分にフォーカスして解説してみます。

「2ちゃんねる」にはさまざまな内容をとりあつかう掲示板があり、これらの掲示板はその内容にあわせて便宜上カテゴリー分けされています。さらに、掲示板は特定の話題ごとにスレッドという単位で、ユーザーからの書きこみをまとめています。「2ちゃんねる」にアクセスするためのAPIについて特にオフィシャルなドキュメントは存在しないのですが、ネット上の有志の方による資料がいくつか存在します。ここでは参考資料として「専ブラ開発への道」を紹介しておきますが、このブログの記事を理解する上ではこれらのドキュメントを参照する必要は特にありません。

カテゴリーごとの掲示板情報の取得

さきほど掲示板はその内容にあわせて「便宜上」カテゴリー分けされていると書いたのですが「便宜上」と書いたのには訳があります。というのもカテゴリーと掲示板の論理的な関係を厳密に取得するAPIというのは特になく、掲示板の一覧を表示するHTML文書の形でしか用意されていないのです。よって、カテゴリーと関連付けて掲示板の一覧情報を入手するには、以下のような形式で記述されているHTML文書をパースすることになります。

スレッドリスト

掲示板の一覧から掲示板のURLを取得することができます。このURL配下のsubjects.txtというテキストファイルに掲示板のスレッド一覧の情報が収められています。形式は以下のような形式になっており、一行がひとつのスレッドに対応する形になっています。

nnnnnnnnnnは10桁の整数であり、スレッドが生成された日時を1970年1月1日00時00分00秒からの経過秒数で表現しています。またxは1-1000の整数であり、スレッドに対する書き込み数を表します。

書き込み一覧

スレッドに対する書き込みの一覧もテキストファイルとしてアクセス可能です。ボードのURLおよびスレッドのIDに基づいて以下のようなURLを合成します。

ダウンロードしたファイルは以下のような形式になっており、一行がひとつの書き込みに対応する形になっています。なお、特に断りがない場合、このAPIでやりとりされるテキストはShift JISでエンコードされているので注意してください。

ここでも項目の区切りが'<>’になっていることにさえ気を付ければ非常にパースしやすい形式ですが、落とし穴がいくつかあります。

  • 時刻の表現の基本形はyyyy/MM/dd(曜) HH:mm:ss.ffという形式ですが掲示板/スレッドによっては.ffがありません
  • 同様にIDの部分がない掲示板/スレッドもあります
  • スレッド名称は最初の一行にのみ存在します

掲示板データの読み込み

これらのカテゴリー、掲示板、スレッドおよび書き込みを以下のようなF#のデータ型として表現することにします。

掲示板にAPIを通じてアクセスするということは、HTTPでダウンロードしたテキストファイルを、これらのデータ構造に変換するということになります。テキストをダウンロードするコードは.NETのライブラリを用いると簡単に実装できるので、ここではテキストファイルを解析して上記のデータ型に変換するところの処理について詳しくみてみましょう。

スレッド(書き込みのリスト)の読み込み

まずは掲示板の情報そのものともいえるスレッド(書き込みのリスト)の読み込みからみていきましょう。

先ほどしめした書き込みの形式は正規表現であらわせので、それにマッチした結果に基づいてMoPostの値を返せばよいということなります。ただし、パースの処理とMoPostへの変換処理を区別するという観点で、ここでは正規表現のマッチングはActive Patternとして定義してあります。

スレッドリストの読み込み

スレッドの一覧もほぼ同様のパターンで実装できます。正規表現の複雑さという観点ではこちらの方が簡単なので特に問題ないでしょう。

カテゴリーと掲示板の読み込み

以上で見たように、スレッドおよび書き込みの情報そのものは正規表現であらわせる形のものでしたので、繰り返し文字列に対してマッチングさせていけばよいだけでした。カテゴリーおよび掲示板の情報そのものも正規表現を使えば問題なく解析できるのですが、厄介なのがカテゴリーと掲示板の階層関係の解析です。

おおざっぱにいうとカテゴリーおよび掲示板の一覧を表示するHTMLは以下のような構造になっています。

カテゴリー名および掲示板にマッチする行以外は無視すると、このHTMLファイルはカテゴリー名(CategoryTitle)および掲示板(Board)のリストとしてみなすことができます。このリストを(カテゴリー、掲示板のリスト)というTupleにうまく分割するととができればMoCategoryに変換できそうです。

まず、カテゴリー名および掲示板の正規表現に対応するActive Patternを記述しておきましょう。

Active Patternにマッチした結果を中間表現として一時的にTokenというUnionで表現することにしましょう。こうすれば件のHTML文書はTokenのリストとして表現できます。

これで、このリストに対してパターンマッチングを適用し、(カテゴリー名、掲示板のリスト)という組を取り出し、それをMoCategoryに変換することができるようになりました。

あと「掲示板のようにみえる行」が最初にあるので、それを読みとばすという処理を追加すると完成です。

以上、掲示板のカテゴリー、掲示板、スレッドおよび書き込みのデータをパースする関数が定義できました。以下のようにSystem.Net.WebRequestと組み合わせれば、一応簡単なクライアントモジュールの仕上がりとなります。

以上の関数ほかをモジュールとして定義しているソースはこちらから入手可能です。

13日目は @omanuke さんです。

One thought on “F#で2ch専用ブラウザをつくろう