HeyCHのブログ

慢性疲労のへいちゃんです

【C#】5ちゃんねるビューアーを作ろう その2

今回の内容は前回の内容の続きとして書いていきます。
heych.hatenablog.com

前回でTreeViewにメニューを表示するところまでいきました。
今回は、メニューをクリックしたときにスレッド一覧を表示する方法について書いていこうと思います。

TreeNodeのTagにURLを設定する

TreeNode.Tagにはobjectを設定することができ、ここにURLを設定しておくことで、
TreeNodeがクリックされた時(treeView1_AfterSelect)にそのURLを使う事ができます。
現在a要素のhref属性はcurrentAttr変数に入っているので、正規表現を使ってURLを取り出していきます。
以下のようにする事で、TreeViewのTreeNodeがクリックされた時にURLが内容のMessageBoxを表示することができます。
f:id:HeyCH:20200417002836p:plain

        private void Form1_Load(object sender, EventArgs e) {
            string html = "";
            //まずHTMLの取得
            HttpWebRequest req = WebRequest.CreateHttp("https://www2.5ch.net/5ch.html");
            using (var res = req.GetResponse()) {
                using (var r = res.GetResponseStream()) {
                    using (var sr = new StreamReader(r, Encoding.GetEncoding(932))) {
                        html = sr.ReadToEnd();
                    }
                }
            }
            //Treeの作成
            bool stTag = false;
            bool edTag = false;
            bool isAttr = false;
            string currentTag = null;
            string currentAttr = null;
            string tmp = null;
            Regex regex = new Regex("(?:href=\")(.*)(?:\")");
            for (int i = 0; i < html.Length; i++) {
                if (html[i] == '<') {
                    if (html[i + 1] == '/') {
                        if (tmp != null) {
                            if (currentTag == "B") {
                                TreeNode tn = new TreeNode(tmp);
                                treeView1.Nodes.Add(tn);
                            } else if (currentTag == "a" && treeView1.Nodes.Count > 0) {
                                string href = "https:" + regex.Match(currentAttr).Groups[1].Value+ "subback.html";
                                TreeNode tn = new TreeNode(tmp);
                                tn.Tag = href;
                                treeView1.Nodes[treeView1.Nodes.Count - 1].Nodes.Add(tn);
                            }
                        }
                        edTag = true;
                    } else {
                        stTag = true;
                        tmp = null;
                        currentTag = null;
                        currentAttr = null;
                    }
                } else if (html[i] == '>') {
                    if (edTag) currentTag = null;
                    stTag = false;
                    edTag = false;
                    isAttr = false;
                } else if (html[i] == ' ' && stTag) {
                    isAttr = true;
                } else {
                    if (isAttr) currentAttr += html[i];
                    else if (stTag) currentTag += html[i];
                    else if(!edTag &&(currentTag=="B" || currentTag=="a")) {
                        tmp += html[i];
                        if (tmp == "他のサイト") break;
                    }
                }
            }
        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) {
            if (e.Node.Tag == null) return;
            MessageBox.Show((string)e.Node.Tag);
        }

※上のusingのところに「using System.Text.RegularExpressions;」を追加しています。

URLの内容からスレッドを1個1個取得してListViewに表示する

ダウンロードする「subback.html」は以下のようになっているため、正規表現でhref属性値とスレッドタイトルを取得します。

<div><small id="trad">
<a href="1586871167/l50">1: コロナのおかげで「あっ、別になくても生きていけるな」と思ったもの ライブハウス、各種イベント、居酒屋 他にも多数 [261472595] (833)</a>
<a href="1586867407/l50">2: 除菌アルコールが手に入らない。嫌儲民は持ってるの???★2 [253542839] (430)</a>
<a href="1586874929/l50">3: 【悲報】IMF「大恐慌以来の不況がくる」と警告するも、市場はガン無視でダウ24000ドル回復 [633473628] (274)</a>
<a href="1586863131/l50">4: 安倍首相、「日本は世界で最も手厚い休業補償をしています」 [476729448] (995)</a>
<a href="1586864501/l50">5: 二階幹事長「国民全員に10万円配るぞ」※ [726690265] (106)</a>
<a href="1586876734/l50">6: 感情論抜きに、高井議員はなぜセクキャバに行ったの? コロナの感染リスクあるのに。 怖くなかったのかな [623653551] (32)</a>

まずはListViewのプロパティを設定してあげます。
ListViewを選択して▲をおして、ビューを「Details」にしてやり、FullRowSelectプロパティを「True」に、HeaderStyleを「None」に設定してやります。
f:id:HeyCH:20200417014106p:plain

それから、以下のようにコードを書く事で、スレッド一覧が取得できるようになります。

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) {
            if (e.Node.Tag == null) return;
            string html = "";
            Regex regex = new Regex("(?:<a href=\")(.*)(?:\">)(.*)(?:</a>)");
            //まずHTMLの取得
            try {
                HttpWebRequest req = WebRequest.CreateHttp((string)e.Node.Tag);
                using (var res = req.GetResponse()) {
                    using (var r = res.GetResponseStream()) {
                        using (var sr = new StreamReader(r, Encoding.GetEncoding(932))) {
                            html = sr.ReadToEnd();
                        }
                    }
                }
            } catch (Exception ex) {
                MessageBox.Show("HTMLの取得に失敗しました。","エラー",MessageBoxButtons.OK,MessageBoxIcon.Error);
            }
            //正規表現でhref属性値とスレッドタイトルを取得する
            if (listView1.Columns.Count <= 0) listView1.Columns.Add("スレッドタイトル");
            listView1.Items.Clear();
            try {
                var st = html.LastIndexOf("<small id=\"trad\">");
                var ed = html.IndexOf("</small>", st);
                var ms = regex.Matches(html);
                foreach (Match m in ms) {
                    if (m.Index < st || m.Index > ed) continue;
                    var item = new ListViewItem(m.Groups[2].Value);
                    item.Tag = m.Groups[1].Value;
                    listView1.Items.Add(item);
                }
            } catch (Exception ex) {
                MessageBox.Show("HTMLの読取に失敗しました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            listView1.Columns[0].Width = -1;
        }

f:id:HeyCH:20200417014835p:plain