為了拓展公司業務,客服部需要大量相關行業的客戶資料。由於公司規模不大,從成本上考慮購買數據不太現實,所以我們就開發了一套方案,從行業相關的商業網站上去抓取客戶資料。
鑒於不同的網站在數據排版、字符編碼、獲取數據方式(get或post)不同。如要抓到自己想要的數據,針對每個網站都需要進行網頁的分析,有效數據的提取。在這里主要分享關鍵代碼,順便把最近寫的一個程序源代碼貼出來供園友們下載研究,里面有抓取三個網站數據的例子(深市A股、三星手機、當當網微軟技術叢書,抓取不同的網站需要修改運行入口)。掌握其中一個的技巧,便可以在所有類似網站上獲取你想要的數據,比方你在炒股,你可以抓取股票網站數據到本地,做個報表分析,操作的好,能讓你賺的盆滿缽滿;假若你自己有個淘寶網店,需要商品圖片,copy別人店鋪的圖片多慢啊,抓取便是;如果你有博客或新聞網站,沒有更新的數據怎么辦,看中哪個網站抓取便是。說了這么多,回正題……
下面我以抓取其中一個網站為例來介紹這個小程序,首先抓取任務如下:
http://detail.zol.com.cn/cell_phone_index/subcate57_98_list_1.html
只要圖片,手機名稱,價格,網絡模式,網絡類型,其它信息不要
手機圖片要下載下來
1.程序界面設計如下,只為達到抓取數據目的,界面比較鄙陋筒靴們將就看:-)。

2.用時信息通過Timer控件的tick事件來顯示抓取用的時間,代碼如下:
1 #region //時間處理 2 int duration = 0; 3 private void timer1_Tick(object sender, EventArgs e) 4 { 5 duration++; 6 lbTime.Text = "用時:" + GetTimeBySecount(int.Parse(duration.ToString().Trim())); 7 } 8 /// <summary> 9 /// 通過秒返回時:分:秒格式的時間 10 /// </summary> 11 /// <returns></returns> 12 private string GetTimeBySecount(int s) 13 { 14 s = s + 1; 15 TimeSpan timeSpan = new TimeSpan(0, 0, s); 16 string hour = timeSpan.Hours < 10 ? timeSpan.Hours.ToString().Trim().PadLeft(2, '0') : timeSpan.Hours.ToString().Trim(); 17 string minute = timeSpan.Minutes < 10 ? timeSpan.Minutes.ToString().Trim().PadLeft(2, '0') : timeSpan.Minutes.ToString().Trim(); 18 string second = timeSpan.Seconds < 10 ? timeSpan.Seconds.ToString().Trim().PadLeft(2, '0') : timeSpan.Seconds.ToString().Trim(); 19 return string.Format("{0}:{1}:{2}", hour, minute, second); 20 } 21 #endregion
3.進度信息用的是委托顯示抓取詳情,代碼如下:
1 #region //委托顯示用戶提示信息 2 /// <summary> 3 /// 為了進程之間通信,定義一個委托 4 /// </summary> 5 /// <param name="displayString">實時處理的信息</param> 6 /// <param name="lbl">接收實時處理信息的 Label 對象</param> 7 /// <param name="ui">承載 Label 控件的窗體對象</param> 8 delegate void SetText(string displayString, Label lbl, Form ui); 9 /// <summary> 10 /// 利用委托操作窗體線程上的 Lable 控件 11 /// </summary> 12 /// <param name="str">實時處理的信息</param> 13 /// <param name="lb">接收實時處理信息的 Label 對象</param> 14 /// <param name="fui">承載 Label 控件的窗體對象</param> 15 public static void SetLableText(string str, Label lb, Form fui) 16 { 17 if (lb.InvokeRequired) 18 { 19 SetText st = new SetText(SetLableText); 20 fui.Invoke(st, str, lb, fui); 21 } 22 else 23 { 24 lb.Text = str; 25 } 26 } 27 #endregion
4.拖放一個backgroundWorker控件,啟動DoWork事件和RunWorkerCompleted事件。
- DoWork代碼如下(代碼中標紅的方法是幾個關鍵的方法
Common.Get_HttpAll:根據網頁地址,和字符編碼獲取網頁源代碼返回stringCommon.ResolverAndOutput:從網頁內容中獲取關鍵信息,通常用於獲取列表數據,table里面的tr數據。
參數1傳入要分析的網頁內容;
參數2傳入要分析的網頁內容的起始點,為空則表示從網頁內容起始位置開始;
參數3傳入要分析的網頁內容的終結點,為空則表示截止到網頁內容的最后位置;
參數4傳入過濾規則,例如:<tr>(?<content>.+?)</tr>,表示獲取參數2和參數3之間的字符串中所有<tr>開始,</tr>結束的所有字符串,這些字符串用$~拼接;
參數5默認傳入1;
參數6默認傳入false;Common.JieQuString:截取字符串中A和B之間的數據。參數1傳入帶分析的字符串;參數2傳入A(A在字符串中唯一);參數3傳入B(B在字符串中唯一)):
1 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 2 { 3 try 4 { 5 DataSet ds = new DataSet(); 6 DataTable dtContact = new DataTable("tbl"); 7 dtContact.Columns.Add("name"); 8 dtContact.Columns.Add("price"); 9 dtContact.Columns.Add("fileName"); 10 dtContact.Columns.Add("model"); 11 dtContact.Columns.Add("type"); 12 DataRow dr; 13 int i = 1; 14 bool flag = true; 15 do 16 { 17 string path = "http://detail.zol.com.cn/cell_phone_index/subcate57_98_list_" + i.ToString() + ".html"; 18 string getHtml = Common.Get_HttpAll(path, "GB2312"); 19 if (!getHtml.Contains("wrong")) 20 { 21 string useHtml = Common.ResolverAndOutput(getHtml, "", "", "<div class=\"list-item clearfix\">(?<content>.+?)target=\"_blank\">查詢底價</a>", 1, false); 22 string[] trItem = useHtml.Replace("~", "").Split('$'); 23 flag = trItem.Length > 2; 24 for (int j = 0; j < trItem.Length - 1; j++) 25 { 26 SetLableText("進度:當前正在分析第" + i + " 頁,第"+(j+1)+"條數據", lbJD, this); 27 dr = dtContact.NewRow(); 28 string imgUrl = Common.JieQuString(trItem[j], "<div class=\"pic-box SP\" data-rel=\"", "\">"); 29 int splitIndex=imgUrl.LastIndexOf('/'); 30 if (splitIndex < 0) { 31 break; 32 } 33 string fileName= imgUrl.Substring(splitIndex, imgUrl.Length - splitIndex).Replace("/",""); 34 //下載當前產品圖片 35 string filepath = "E:\\抓數據\\中關村三星手機圖片\\"+fileName; 36 WebClient mywebclient = new WebClient(); 37 mywebclient.DownloadFile(imgUrl, filepath); 38 //填充行值 39 dr["name"] = Common.JieQuString(Common.JieQuString(trItem[j], "<h3>", "</h3>"), ">", "</a>"); 40 dr["price"] = Common.JieQuString(trItem[j],"<span class=\"price\">¥<b class=''>","</b></span>"); 41 dr["fileName"] = fileName; 42 dr["model"] = Common.JieQuString(trItem[j], "<span>網絡模式:</span>", "</li>"); 43 dr["type"] = Common.JieQuString(trItem[j], "<span>網絡類型:</span>", "</li>"); 44 dtContact.Rows.Add(dr); 45 } 46 ds.Tables.Add(dtContact.Copy()); 47 string savePath = "E:\\抓數據\\中關村三星手機.xml"; 48 ds.WriteXml(savePath); 49 ds.Tables.Clear(); 50 } 51 else 52 { 53 continue; 54 } 55 i++; 56 } while (flag); 57 } 58 catch (Exception ex) 59 { 60 MessageBox.Show("代碼異常:" + ex); 61 } 62 }
-
RunWorkerCompleted代碼如下:
1 private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 2 { 3 timer1.Stop(); 4 timer1.Enabled = false; 5 MessageBox.Show("ok,見 E:\\抓數據\\中關村三星手機.xml"); 6 }
5.點擊窗體抓取數據啟動Click事件,代碼如下:
1 private void btnGetData_Click(object sender, EventArgs e) 2 { 3 timer1.Enabled = true; 4 timer1.Start(); 5 backgroundWorker1.RunWorkerAsync(); 6 }
6.大功告成。啟動程序效果如下:

7.運行完成后查看抓取的數據信息和手機圖片如下(把Xml文件拖到Excel中顯示):


總結:此例只是針對Get請求數據的情況,而且當前抓取的網站都是不需要登錄的。而在實際生產中肯定有Post請求數據的,也有需要登錄的,源碼里面的Common類提供了各種場景需要的方法,有興趣的可以深入研究。
