AngleSharpで上州屋の釣果情報をぶっこ抜く2

前回までのあらすじ

my-income-is-small.hatenablog.com
継続的アウトプット!継続的アウトプット!

進捗

  • 11件目以降のデータも取得する→できた
  • dataGridのサイズを動的に変える→まあできた
  • 魚の名前を抽出する→まだ
  • 縮小した釣果画像を出す→あえてやらない
  • リファクタリング(もうかよ)→少しやった

ソース

    public partial class Form1 : Form
    {
        private int pageNum = 1;
        private int numOfArticlesPerPage = 10;

        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_ClickAsync(object sender, EventArgs e)
        {
            var urlstring = urlTextBox.Text;
            await GetHTMLFromStream(urlstring);
        }

        /// <summary>
        /// 指定したサイトのHTMLをストリームで取得する
        /// </summary>
        /// <param name="urlstring">URL</param>
        /// <returns></returns>
        private async Task GetHTMLFromStream(string urlstring)
        {
            // データ表示の設定
            SetDataGridViewProperty();

            // 指定したサイトの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から県を抽出する
            ExtractArea(doc);
            
            // HTMLから釣り場と釣り人を抽出する
            ExtractPointAndAngler(doc);

            // HTMLから店員コメントを抽出する
            ExtractComment(doc);
        }

        /// <summary>
        /// DataGridViewの設定
        /// </summary>
        private void SetDataGridViewProperty()
        {
            // DataGridViewの列数とヘッダーの設定
            chokaDataGridView.ColumnCount = 4;
            chokaDataGridView.Columns[0].HeaderText = "prefecture";
            chokaDataGridView.Columns[1].HeaderText = "point";
            chokaDataGridView.Columns[2].HeaderText = "angler";
            chokaDataGridView.Columns[3].HeaderText = "comment";

            // DataGridViewの列幅・改行の設定
            chokaDataGridView.Columns[3].DefaultCellStyle.WrapMode = DataGridViewTriState.True;
        }

        /// <summary>
        /// info__areaから県を抽出する
        /// </summary>
        /// <param name="doc">HTMLドキュメント</param>
        private void ExtractArea(IHtmlDocument doc)
        {
            var info_area = doc.GetElementsByClassName("info__area");
            int i = 0;
            foreach (var element in info_area)
            {
                string prefecture = string.Empty;
                foreach (var p in element.GetElementsByClassName("info__tag"))
                {
                    prefecture += p.TextContent.ToString();
                }
                chokaDataGridView.Rows.Add(prefecture, string.Empty, string.Empty, string.Empty);
                // DataGridViewのコメント列の幅を自動調整する
                i++;
            }
        }

        /// <summary>
        /// info_detailから釣り場と釣り人を抽出する
        /// </summary>
        /// <param name="doc">HTMLドキュメント</param>
        private void ExtractPointAndAngler(IHtmlDocument doc)
        {
            var info_details = doc.GetElementsByClassName("info__detail");
            int i = 0;
            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();
                    }
                }
                chokaDataGridView.Rows[i + (numOfArticlesPerPage * pageNum - numOfArticlesPerPage)].Cells[1].Value = point;
                chokaDataGridView.Rows[i + (numOfArticlesPerPage * pageNum - numOfArticlesPerPage)].Cells[2].Value = angler;
                i++;
            }
        }

        /// <summary>
        /// info_commentを抽出する
        /// </summary>
        /// <param name="doc">HTMLドキュメント</param>
        private void ExtractComment(IHtmlDocument doc)
        {
            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();
                }
                chokaDataGridView.Rows[i + (numOfArticlesPerPage * pageNum - numOfArticlesPerPage)].Cells[3].Value = comments;
                // DataGridViewのコメント列の幅を自動調整する
                chokaDataGridView.AutoResizeColumn(3, DataGridViewAutoSizeColumnMode.AllCells);
                chokaDataGridView.AutoResizeRow(i + (numOfArticlesPerPage * pageNum - numOfArticlesPerPage), DataGridViewAutoSizeRowMode.AllCells);
                i++;
            }
        }

        /// <summary>
        /// 次の10件を取得する
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void nextButton_Click(object sender, EventArgs e)
        {
            pageNum++;
            var urlstring = urlTextBox.Text + "/search.php?pref=&page=2";
            await GetHTMLFromStream(urlstring);
        }
    }

カラムのNo.をハードコーティングしたりしてて酷いけど次で直すから許して

動作確認

OKボタン押したとき

f:id:satoshi_cs12:20190816135558p:plain
だいぶ見れるようになってきた。凝りだすとキリがないし、文字情報だけにとどめておく意味もあるからUIはしばらくこれでいいかな。

Nextボタン押したとき

f:id:satoshi_cs12:20190816135836p:plain
次の10個をちゃんと表示してる

次バージョン以降

すぐやりたいこと

  • 魚の名前を抽出する
  • 県別に情報を抽出する(多分、店の所在地しかできないが)
  • リファクタリング(もうかよ)

システム的な伸びしろ

  • fimoからも引っ張る
  • 自動化してデイリー情報を自分のスマホにプッシュする
  • 年単位でのリピート釣行をしやすい上州屋店員の特性を利用して、統計的に次シーズンのターゲットの出来を予想し、釣行予算を的確に立てて無駄釣具・不足釣具の発生を防止する