ストーリー
少し前に空き時間があったので、GitHubで .NET のオープンソースライブラリを見ていました。そこでクローラー関連のライブラリを見つけ、QQグループに参加しました。グループ内の達人たちが「うまくクロールすればするほど、早く捕まる」と議論しているのを見て、自分でもクローラー関連のものを作りたくなりました。しかしクローラーは危険なもので、勝手に他人のWebページをクロールするわけにもいきません。そこで友人を見つけ、彼のWebサイトで練習することにしました!

練習
.NET にはクローラー関連のライブラリがかなり多いので、今回は HtmlAgilityPack を選んでクローラーの練習をしました。
そもそもクローラーとは何でしょうか?
簡単に言うと:
クローラーの基本的な流れは、データをダウンロードし(HTTPリクエストを送信して応答を受け取る)、応答テキスト(text、json、htmlなど)を解析し、解析したデータを保存することです。
フレームワークを学ぶには、まず公式ドキュメントから始めるのが定番です。
アドレス: https://html-agility-pack.net/
Html パーサー
- From File(ファイルからHTMLドキュメントを読み込む)
- From String(指定された文字列からHTMLドキュメントを読み込む)
- From Web(インターネットリソースからHTMLドキュメントを取得)
- From Browser(WebBrowserからHTMLドキュメントを取得)
そこで私は From Web を選んでHTMLドキュメントを解析することにしました。コードは以下の通りです。
var html = @"https://dotnet9.com/";
HtmlWeb web = new HtmlWeb();
var htmlDoc = web.Load(html);
HTMLドキュメントを取得できたので、次はHTMLの内容を解析します。
Html セレクター
- SelectNodes()(XPath式に一致するノードリストを選択)
- SelectSingleNode(String)(XPath式に一致する最初のXmlNodeを選択)
Webサイトを開き、クロールしたい場所を特定します。今回は、そのサイトの「特色专辑」(特集アルバム)の下にあるすべての記事をクロールします。

デバッグモードを開くと、「特色专辑」は aタグ であることがわかります。さらにその上の要素を見ると、li があり、その上が ul です。これでノードを取得できます。

var allNodes = htmlDoc.DocumentNode.SelectNodes("//ul[@id='starlist']//li[@class='menu']");
もちろん、XPath を使ってノード内容を取得することもできます。
var singNodes = htmlDoc.DocumentNode.SelectSingleNode("/html[1]/body[1]/header[1]/div[3]/nav[1]/ul[1]/li[3]//ul[1]")
次に、この特集アルバムのサブメニューのURLを取得します。aタグ の href属性 がリンクのターゲットアドレスを指定しているので、まずサブメニュー内のすべてのリンクを取得する必要があります。

var singNodes = htmlDoc.DocumentNode.SelectSingleNode("/html[1]/body[1]/header[1]/div[3]/nav[1]/ul[1]/li[3]//ul[1]")
.ChildNodes.Where(o => o.Name=="li");
List<string> lstUrl = new List<string>();
foreach (var item in singNodes)
{
var aNodes = item.ChildNodes.Where(o => o.Name == "a").First();
string url = aNodes.Attributes["href"].Value;
lstUrl.Add(url);
}
サブメニューのいずれかを開くと、関連する記事のタイトル、説明、画像などが表示されます。これが私たちが欲しいコンテンツです!解析方法は先ほどと同じです。コードは以下の通りです。

foreach (var item in lstUrl)
{
htmlDoc = web.Load("https://dotnet9.com"+item);
var resultNodes = htmlDoc.DocumentNode.SelectSingleNode("//div[@class='pics-list-box whitebg']//ul")
.ChildNodes.Where(o=>o.Name=="li");
foreach (var itemResultNodes in resultNodes)
{
WebData webData = new WebData();
var aNodes = itemResultNodes.ChildNodes.Where(o => o.Name == "a").First();
webData.Url= aNodes.Attributes["href"].Value;
webData.Title = aNodes.ChildNodes["h2"].InnerText;
webData.Desc = aNodes.ChildNodes["p"].InnerText;
webData.Img = aNodes.ChildNodes["i"].ChildNodes["img"].Attributes["src"].Value;
Console.WriteLine($"タイトル:{webData.Title}-説明:{webData.Desc}-Img:{webData.Img}-{webData.Url}\r\n");
}
}
これで欲しいデータを取得できました!コードを実行すると、初めてのクローラーが成功しました。

まとめ
即興で最初のクローラーを書いてみました。もっといい方法があれば、ぜひ交流しましょう。独りで楽しむより、みんなで楽しむ方が良いですね。今回はここまでです。お役に立てれば幸いです。
最後に宣言します:総じて、技術自体に罪はありません。しかし、技術を使って他人のプライバシーや商業データをクロールするなら、それは法律を軽視していることになります。皆さん、それぞれの線引きを守ってくださいね。