一、背景 母親喜歡聽評書,跟著廣播每天一集總覺得不過癮,於是2010年給她買了一個帶記憶體,能播放MP3的音箱,從此給她找評書便成了我的責任和義務。 一開始開始還好,單先生說的書多,找起來不困難, 但隨著聽的越多,加上聽慣了單先生的,其他人的母親都不喜歡,即便單先生的,類似白眉
一、背景 母親喜歡聽評書,跟著廣播每天一集總覺得不過癮,於是2010年給她買了一個帶記憶體,能播放MP3的音箱,從此給她找評書便成了我的責任和義務。
一開始開始還好,單先生說的書多,找起來不困難, 但隨著聽的越多,加上聽慣了單先生的,其他人的母親都不喜歡,即便單先生的,類似白眉大俠、童林傳等武俠類的她也不愛聽(本人也不是很喜歡,規律都差不多,自己被欺負了,找兄弟,再不行找師傅,還不行,找師祖,總之一句話你等著,我叫人去),後來實在找不到了,也慢慢的試著聽孫一,張少佐等其他人的了。
電驢被封後,而能打包下載mp3的網站越來越少,想找點評書著實讓人撓頭。 一次偶然的機會,發現聽中國裡面的評書比較全,但是沒法批量下載,於是就有了本文,寫一款打包下載MP3的軟體,軟體下載地址。
隨著智能機的普及,母親已經使用手機聽評書了,所以本文僅供學習交流,請勿用於商業及非法用途。
二、所需技能及工具 想要實現批量下載需要三樣利器。 1、Visual Studio,傳說中的編程界的九陽神功,我現在一般是2010和2015交替使用。
2、正則表達式,童子功,打好基礎開發速度事半功倍。 3、IE10以上、Edge 、Chrome等瀏覽器,相當於慕容世家的絕學鬥轉星移。
三、要實現的功能
1、搜索評書或其他有聲讀物。 2、下載評書。
四、實戰開始
軟體實現搜索功能很簡單,大體上三步即可
1)、模擬瀏覽器向網站提交查詢 ,獲取返回的結果 。 2)、分析結果,寫出對應的正則表達式,生成結果的記錄集 。 3)、將記錄集呈現在窗體上。
1、首先讓我們看看tingChina的查詢頁面什麼樣?
2、選中評書,選擇在演播中搜索,輸入單田芳,點擊搜索資源,結果頁面如下
咱們分析一下鏈接地址,mainlei從名稱看,應該是指有聲小說、評書、相聲、戲曲、兒歌、人文、笑話,查詢界面第一行的搜索分類。我選的是評書,而mainlei=1,那有以此類推一下,有聲讀物對應的是0,相聲對應的是2,具體的試一下就能簡單的驗證。
而lei應該就是查詢界面中的那個下拉框,一共有標題、演播、作者三個選項,而我選的是演播,而lei=1,同樣以此類推,標題應該就是0,作者則是2。
3、分析完鏈接地址,接下來咱們分析頁面的代碼,筆者習慣使用IE瀏覽器,所以直接在查詢結果頁面按F12,然後按圖中的提示操作即可。
按照圖中的提示,能提取一段html代碼,當然,如果你對html非常熟悉,直接查看源代碼找到這段代碼也行。 這時,大家要註意三個問題。 1、有的li元素包含style="background-color:#F3F3F3;"這段代碼,有的不包含。 2、有的a標簽包含style="color:blue;"這段代碼,有的則不含。 3、根據mainlei的不同,結果中鏈接地址也有些區別,比如評書就是<a href="pingshu/disp_1209.htm">,而有聲讀物則是<a href="yousheng/disp_27623.htm">,註意粗體的部分。
這種情況正則表達式怎麼寫呢,對於前兩種情況,這裡我用了一種簡單粗暴的方法,就是把這兩段內容統一替換掉,這樣就能簡化正則表達式了。 最終的正則表達式:<li><a href=""(?<mainlei>[\w]*)/disp_(?<Number>[\d]*).htm""\s*>(?<Title>[\s\S]{1,20}?)</a>\([\d]*\)</li>
4、軟體搜索的最終界面如下:
5、主要代碼如下: C# Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
private void frmSearch_Load(object sender, EventArgs e) { List<DictionaryEntry> list = new List<DictionaryEntry>(); list.Add(new DictionaryEntry("0","標題")); list.Add(new DictionaryEntry("1","演播")); list.Add(new DictionaryEntry("2","作者")); this.cmb_lei.DisplayMember = "Value"; this.cmb_lei.ValueMember = "Key"; this.cmb_lei.DataSource = list; //this.cmb_lei.SelectedValue = "1"; } //http非同步請求的回調函數 public void ResponseCallBack(IAsyncResult result) { string Html = ""; HttpWebRequest req = (HttpWebRequest)result.AsyncState; try { using (HttpWebResponse response = (HttpWebResponse)req.EndGetResponse(result)) { Stream resStream = response.GetResponseStream(); StreamReader sr = new StreamReader(resStream, Encoding.GetEncoding("GB2312")); Html = sr.ReadToEnd(); } } catch(Exception ex) { if (IsDisposed || !this.IsHandleCreated) return; this.Invoke(new Action(() => { MessageBox.Show("查詢時出現異常,原因:" + ex.Message); })); return; } //替換掉干擾代碼 Html = Html.Replace(@" style=""background-color:#F3F3F3;""", "").Replace(@" style=""color:blue;""", ""); //動態生成Label的字體 Font font = new Font("微軟雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); //正則表達式分析網頁,查找查詢結果 MatchCollection ms = Regex.Matches(Html, @"<li><a href=""(?<mainlei>[\w]*)/disp_(?<Number>[\d]*).htm""\s*>(?<Title>[\s\S]{1,20}?)</a>\([\d]*\)</li>", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (ms.Count > 0) { //最大寬度 int MaxWidth = 0; Dictionary<string, string> list = new Dictionary<string, string>(); foreach (Match m in ms) { list.Add(string.Format("http://www.tingchina.com/{0}/disp_{1}.htm", m.Groups["mainlei"].Value, m.Groups["Number"].Value), m.Groups["Title"].Value); //獲取字體的大小,找到最寬的那個條記錄 Size size = TextRenderer.MeasureText(m.Groups["Title"].Value, font); if (size.Width > MaxWidth) { MaxWidth = size.Width; } } if (IsDisposed || !this.IsHandleCreated) return; this.Invoke(new Action(() => { //對結果進行排序 Dictionary<string, string> listAsc = list.OrderBy(o => o.Value).ToDictionary(o => o.Key, p => p.Value); //迴圈動態生成查詢結果. foreach (KeyValuePair<string, string> kvp in listAsc) { Label lbl = new Label(); lbl.Text = kvp.Value; lbl.Tag = kvp.Key; lbl.Cursor = System.Windows.Forms.Cursors.Hand; lbl.Margin = new System.Windows.Forms.Padding(5, 0, 5, 10); lbl.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(192)))), ((int)(((byte)(0))))); lbl.Font = font; lbl.Width = MaxWidth; lbl.Click += new EventHandler(lbl_Click); this.fpnl_Content.Controls.Add(lbl); } })); } else { if (IsDisposed || !this.IsHandleCreated) return; this.Invoke(new Action(() => { MessageBox.Show("沒有查找到數據,請更換關鍵詞。"); })); } } private void btn_Search_Click(object sender, EventArgs e) { if (this.txt_key.Text.Trim() == "") { MessageBox.Show("請輸入查詢關鍵字."); return; } //迴圈獲得分類編號 string mainlei = "0"; foreach (Control c in this.pnl_Search.Controls) { if (c.GetType().Equals(typeof(RadioButton)) && ((RadioButton)c).Checked) { mainlei = c.Name.Replace("rdo_mainlei", ""); break; } } //發送非同步請求,根據關鍵字查詢 string Url = string.Format("http://www.tingchina.com/search1.asp?mainlei={0}&lei={1}&keyword={2}", mainlei, this.cmb_lei.SelectedValue, HttpUtility.UrlEncode(this.txt_key.Text.Trim(),Encoding.GetEncoding("GB2312"))); HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(Url); request.Method = "GET"; request.BeginGetResponse(new AsyncCallback(ResponseCallBack), request); this.fpnl_Content.Controls.Clear(); } private void lbl_Click(object sender, EventArgs e) { //點擊一個有聲讀物,進入其詳細視窗 //待補充 } |
本想一篇文章介紹完,發現內容還真不少(也可能是我寫的啰嗦,見諒),於是改成上下篇吧,會儘快推出下一篇。
未完待續...
作者:相信的勇氣 出處:http://www.newrain.cn/article/detail/15 本文為博主原創文章,歡迎轉載分享但請註明出處及鏈接,否則將其追究法律責! 勤奮的男人和愛笑的女人,運氣一般都不會太差。