AngleSharpで上州屋の釣果情報をぶっこ抜く
釣り情報は釣具店に集まるのが定番である。
釣具店は数多くあれど、釣り業界のIT化の遅れはスポーツ・レジャー界において屈指であり、店舗に赴くことなく情報を得られるのは上州屋・ポイント・キャスティングの3大釣具チェーンのみであると言ってよい。
3大釣具チェーンの中で最もコンテンツが自分にマッチしてるのは上州屋である。
ポイント
- 情報が釣果持ち込みに偏っており、子供が釣ったとんでもない小型魚など、不要コンテンツが多い
- どう釣ったかの情報が不足している
- 関東の情報が少ない
キャスティング
- 更新頻度が低い
- 釣れていない
しかし、上州屋の釣果情報にも以下の問題がある。
会社でも見たいので、いかにも釣り情報見てる、というような状態を作りたくない
よって、スクレイピングのモチベーションができた。自分の見たい情報を店舗によらず的確に収集したいし、その閲覧はあたかもデータの集約された表を眺めているようにしたい。
htmlを文字列解析して作れるかなと思ったけど、
文字列の解析をするのって嫌だ(人間が読むときにいちいち解析してるわけじゃないのに)
何かXMLって嫌だな
ということで、AngleSharpというものを使ってみることにした。
使い方
qiita.com
使い方も簡単でいいね。
超お試し版の作成
上州屋の釣果情報の「1釣果情報」あたりのhtmlは以下のようになっている。
<!--コンテンツ--> <div class="info__body"> <div class="info__bodyin"> <div class="info__head"> <div class="info__date"> <p class="info__date_txt"><span>‘19 </span>08月05日</p> </div> <div class="info__area"> <span class="info__tag">愛知県</span>豊川店 <span class="info__link"><a href="../shop/top.php?s=136">店舗情報</a></span> <span class="info__link"><a href="../shop/choka.php?s=136">店舗の釣り情報</a></span> </div> <p class="info__note">登録日:2019年08月06日<br>画像をクリックすると、拡大画像が表示されます</p> </div> <div class="info__photo_wrap"> <div class="slider info__photo"> <div class="slider-for" id="slider-for3307238"> <div class="sp-slide"><a href="../choka_img/3307238_1.jpg" class="boxer" data-gallery="gallery" title=""><img src="../choka_img/3307238_1.jpg" alt=""></a></div> <div class="sp-slide"><a href="../choka_img/3307238_2.jpg" class="boxer" data-gallery="gallery" title=""><img src="../choka_img/3307238_2.jpg" alt=""></a></div> <div class="sp-slide"><a href="../choka_img/3307238_3.jpg" class="boxer" data-gallery="gallery" title=""><img src="../choka_img/3307238_3.jpg" alt=""></a></div> </div> <div class="slider-nav-wrap"> <div class="slider-nav" id="slider-nav3307238"> <div class="sp-thumbnail"><span><img src="../choka_img/3307238_1.jpg" alt=""></span></div> <div class="sp-thumbnail"><span><img src="../choka_img/3307238_2.jpg" alt=""></span></div> <div class="sp-thumbnail"><span><img src="../choka_img/3307238_3.jpg" alt=""></span></div> </div> <div class="slick-nav-arrows"> <div class="slick-next"></div> <div class="slick-prev"></div> </div> </div> </div> </div> <div class="info__box"> <div class="info__boxin"> <div class="info__boxin02"> <div class="clearfix"> <p class="info__sttl">豊川店(愛知県):2019年08月05日の釣果</p> <p class="info__weather" style="display:">晴れ</p> </div> <table class="info__table"> <tbody> </tbody> </table> <div class="info__detail"> <table style="display:"> <tr> <th>釣り場</th> <td>豊川河口周辺</td> </tr> </table> <table style="float:right; display:"> <tr> <th>釣り人</th> <td>スタッフ 林</td> </tr> </table> </div> </div> <div class="info__comment"> <p>豊川河口周辺のハゼの様子を見てきました。<br /> 現場到着時は下げ潮。<br /> ルアータックルで少し遠投したところにハゼが固まっていました。<br /> のべ竿の方たちは苦戦気味でした。<br /> 日によって群れが溜まる場所が変わります。<br /> リール竿も用意していきましょう。<br /> 詳細は御来店の上スタッフにお問い合わせ下さい。</p> <p class="choka_item"> </p> </div> </div> </div> </div> </div> <!--コンテンツ-->
このように、info_detailクラスの中に釣り人と釣り場、info_commentクラスの中に詳細が記述されているのがわかる。本当はinfo_detailの中にコメントも入れてほしい所だが、そうなってないのが初学者たる私にはちょっと厄介であった。パイロット版を作ったら早速ダサい実装になってしまった。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using AngleSharp.Html.Dom; using AngleSharp.Html.Parser; namespace Chokascraper { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void button1_ClickAsync(object sender, EventArgs e) { var urlstring = textBox1.Text; dataGridView1.ColumnCount = 3; // 指定したサイトのHTMLをストリームで取得する var doc = default(IHtmlDocument); using (var client = new HttpClient()) using (var stream = await client.GetStreamAsync(new Uri(urlstring))) { // AngleSharp.Html.Parser.HtmlParserオブジェクトにHTMLをパースさせる var parser = new HtmlParser(); doc = await parser.ParseDocumentAsync(stream); } // HTMLからtitleタグの値(サイトのタイトルとして表示される部分)を取得する var title = doc.Title; var info_details = doc.GetElementsByClassName("info__detail"); foreach (var element in info_details) { string point = string.Empty; string angler = string.Empty; foreach (var children in element.GetElementsByTagName("tr")) { string label = string.Empty; foreach (var th in children.GetElementsByTagName("th")) { label += th.TextContent.ToString(); } foreach (var td in children.GetElementsByTagName("td")) { if (label == "釣り場") point += td.TextContent.ToString(); else if (label == "釣り人") angler += td.TextContent.ToString(); } } dataGridView1.Rows.Add(point, angler, ""); } var info_comments = doc.GetElementsByClassName("info__comment"); int i = 0; foreach (var element in info_comments) { string comments = string.Empty; foreach (var p in element.GetElementsByTagName("p")) { comments += p.TextContent.ToString(); } dataGridView1.Rows[i].Cells[2].Value = comments; i++; } } } }
結果
見た目はカスだが、とりあえず先頭10件の釣り人・釣り場・コメントの情報を得ることは出来ているみたい。