抓取分析網頁批量下載評書(1)之搜索有聲小說


 
一、背景
      母親喜歡聽評書,跟着廣播每天一集總覺得不過癮,於是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)
{
    
//點擊一個有聲讀物,進入其詳細窗口
     //待補充
}

本想一篇文章介紹完,發現內容還真不少(也可能是我寫的啰嗦,見諒),於是改成三篇吧,會盡快推出下一篇。
未完待續...
 
作者: 相信的勇氣
本文為博主原創文章,歡迎轉載分享但請注明出處及鏈接,否則將其追究法律責!
勤奮的男人和愛笑的女人,運氣一般都不會太差。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM