ボット・クローラー Advent Calendar 2016 2日目の記事です。
2ちゃんねるは相変わらず現役で動いていらっしゃるようなのですが、板により微妙に挙動(パラメータ設定)が違うっぽい。
@kobake_ 板の設定によって違いますよー◎海外のニュースを取り扱うことが多い極東板など、 https://t.co/ieVO60hDM0 BBS_UNICODE=pass になってます!
— 🍀ほっぺたぴとー☘ (@hoppetapitoo) November 30, 2016
{板のURL}/SETTING.TXT というファイルの内容により板の属性が取得できることを教えてもらったので、とりあえず数ある2ちゃんねるの板のどれが UNICODE 対応しているのか調べてみることにした。
(UNICODE対応はとても大事である。🍣を表示できるか否かがここに懸かっている)
得られた結果
以下のように「"unicode": "SUPPORTED"」となっている要素が UNICODE 対応の板である。
"http://hanabi.2ch.net/hawaii/": { "url": "http://hanabi.2ch.net/hawaii/", "name": "ハワイ州", "unicode": "SUPPORTED" },
ソース解説
ソース全体は上に挙げたリポジトリを見てもらうとして、かいつまんで処理を解説する。
板リストHTLMの取得
まずは板一覧を取得するために2ちゃんねるのメニューページから全リンクを取得する。
http://menu.2ch.net/bbsmenu.html に全板へのリンクが張ってあるのだが、注意点としてこのページは昔ながらの Shift_JIS であることに留意したい。
string menuUrl = "http://menu.2ch.net/bbsmenu.html"; string menuHtml = await HtmlGetter.GetHtml(menuUrl, "shift_jis");
ここで登場する HtmlGetter.GetHtml は自作関数だが、第2引数を省略すれば文字コードは自動判定、明示的に指定すればその文字コードでデコードを行うようになっている。
板リストHTMLから板URLを抽出する
using CsQuery; .... new CQ(menuHtml).Find("a").Each(_a => // ← jQueryっぽいところ { var a = new CQ(_a); var url = a.Attr("href"); // ← jQueryっぽいところ var name = a.Text().Trim(); // ← jQueryっぽいところ if (Regex.IsMatch(url, "^http://[^/]+/[^/]+/$")) // 板っぽいURLだったら { Task.Run(async () => { await ScrapeBoard(url, name); // 板情報のスクレイピングに進む }).Wait(); } });
取得できた HTML の DOM 分析する方法はいくつかあるが、今回は CsQuery という NuGet パッケージを用いている。これを使うと jQuery 的な構文で要素解析を行うことができる。
CsQuery の導入は簡単で、Package Manager Console にて
Install-Package CsQuery
を実行するだけで導入できる。(これで CQ クラスが使えるようになる。
SETTING.TXT の解析
各板の SETTING.TXT を解析する。これの文字コードも Shift_JIS であることに注意。
async Task ScrapeBoard(string boardUrl, string boardName) { // 設定テキスト読み込み -> settings Dictionary<string, string> settings = new Dictionary<string, string>(); string settingUrl = boardUrl + "SETTING.TXT"; string settingText = await HtmlGetter.GetHtml(settingUrl, "shift_jis"); settingText = settingText.Replace("\r\n", "\n"); var lines = settingText.Split('\n'); foreach(var line in lines) { var kv = line.Split('='); if (kv.Length < 2) continue; settings[kv[0]] = kv[1]; } // オブジェクト構築 var boardInfo = new Models.BoardInfo { url = boardUrl, name = boardName }; if(settings.ContainsKey("BBS_UNICODE") && settings["BBS_UNICODE"] == "pass") { boardInfo.unicode = "SUPPORTED"; } m_boards[boardUrl] = boardInfo; }
とりあえず収集できた情報はどんどん m_boards に詰めていく。
JSON出力
m_boards に溜まった情報を最終的に JSON 形式で出力する。
これも簡単で NuGet の Json.NET を用いれば一発である。
(インストール方法は先ほどの CsQuery と同様に Install-Package Newtonsoft.Json みたいな感じ)
using Newtonsoft.Json; .... string json = JsonConvert.SerializeObject(m_boards, Formatting.Indented);
これで一発である。
result.json のできあがり。