【C#】5ちゃんねるビューアーを作ろう Final
今回の内容は前回の内容の続きとして書いていきます。
heych.hatenablog.com
今回は「5ちゃんねるビューアーを作ろう Final」という事で、コードのまとめを書いてシリーズの完結としたいと思います。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Net; using System.IO; using System.Text.RegularExpressions; namespace _5chViewer { public partial class Form1 : Form { public Form1() { InitializeComponent(); } 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; 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; } string html = ""; private void listView1_SelectedIndexChanged(object sender, EventArgs e) { if (listView1.SelectedItems.Count <= 0) return; var url = (string)treeView1.SelectedNode.Tag; //https://hayabusa9.5ch.net/newsの形にする url = url.Substring(0, url.Length - ("/subback.html").Length); ////https://hayabusa9.5ch.net/test/read.cgi/news/の形にする url = url.Substring(0, url.LastIndexOf("/")) + "/test/read.cgi" + url.Substring(url.LastIndexOf("/")) + "/"; var sub= (string)listView1.SelectedItems[0].Tag; sub = sub.Substring(0, sub.Length - ("150").Length); url = url + sub; //まずHTMLの取得 try { HttpWebRequest req = WebRequest.CreateHttp(url); 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); } richTextBox1.Text = ""; BackgroundWorker bw = new BackgroundWorker(); bw.DoWork += Bw_DoWork; bw.RunWorkerAsync(); } private void Bw_DoWork(object sender, DoWorkEventArgs e) { Regex numberReg = new Regex("(<span class=\"number\">)(.*?)(</span>)"); Regex nameReg = new Regex("(<span class=\"name\">)(.*?)(</span>)"); Regex dateReg = new Regex("(<span class=\"date\">)(.*?)(</span>)"); Regex uidReg = new Regex("(<span class=\"uid\">)(.*?)(</span>)"); Regex escapedReg = new Regex("(<span class=\"escaped\">)(.*?)(</span>)"); int index = 0; string tmp = ""; while (true) { //numberの取得 var m = numberReg.Match(html, index); if (!m.Success) break; var number = m.Groups[2].Value; index = m.Index; //nameの取得 m = nameReg.Match(html, index); if (!m.Success) break; var name = m.Groups[2].Value; index = m.Index; //dateの取得 m = dateReg.Match(html, index); if (!m.Success) break; var date = m.Groups[2].Value; index = m.Index; //uidの取得 m = uidReg.Match(html, index); if (!m.Success) break; var uid = m.Groups[2].Value; index = m.Index; //escapedの取得 m = escapedReg.Match(html, index); if (!m.Success) break; var escaped = m.Groups[2].Value; escaped = escaped.Replace("<br>", "\r\n"); index = m.Index; //nameとescapedの調整 name = Regex.Replace(name, "<[^>]*?>", ""); escaped = Regex.Replace(escaped, "<[^>]*?>", "").Replace(">", ">").Replace("<", "<").Replace("&", "&"); tmp += number + " " + name + " " + date + " " + uid + "\r\n\r\n" + escaped + "\r\n\r\n\r\n"; } this.Invoke((MethodInvoker)delegate () { richTextBox1.Text = tmp; }); } private void richTextBox1_LinkClicked(object sender, LinkClickedEventArgs e) { System.Diagnostics.Process.Start(e.LinkText); } } }
ポイント
- もっといいコードの書き方があると思われる
- 5chのHTMLはShift-JISで記述されているので、文字コードは「Encoding.GetEncoding(932)」を使用する
- 正規表現はC#の正規表現として、別途覚える(調べる)必要がある
- ListViewをListBoxのような感じで使用したい場合、ListViewのビューを「Details」にし、FullRowSelectを「True」に、HeaderStyleを「None」に、Item追加後に「listView1.Columns[0].Width = -1;」を実行する
- しれっと追加してますが、richTextBox1に「richTextBox1_LinkClicked」イベントを追加しています