怎麼編寫微博爬蟲 圖片爬蟲

背景:實驗室大數據分析須要獲得社交網站的數據,首選固然是新浪。數據包括指定關鍵詞、話題、位置的微博的內容。字段包括:圖片、時間、用戶、位置信息。php

思路分析:要爬新浪的數據主要有2種方法:html

    1.微博開發者平臺提供的微博API,資源包括微博內容、評論、用戶、關係、話題等信息。同時,你也能夠申請高級接口、商業接口得到更多權限,你要去註冊申請成爲開發者得到OAuth2受權以及這個使用參考,審覈大約要1周。也能夠直接使用別人分享的APPKEYpython

     優勢是簡單,有專門的問答社區,代碼更新方便,能夠得到real time的數據,對熱門話題比較開放,返回的文檔主要是json有不少現成的庫類能夠解析。 缺點是,數據受API規定,沒有關鍵詞檢索,對不嗯呢該有訪問次數限制,畢竟人家主要是服務微博的第三方應用,這個缺陷能夠用更換上面的appkey來解決。
git

    2.傳統html爬蟲,即發送http請求返回html源文件,再解析,提取裏面的下級URL或者其餘咱們要的數據。C#.JAVA python都有相關的網頁工具,c#主要是WebClient,或者HttpRequest/HttpResponse,也可使用WebBrowser/page/htmldocument模擬瀏覽器操做,Java可使用htmluint包。python對web處理的強大支持,使得網上的不少爬蟲都是用python寫的。github

實現方法web

  因爲實驗室要採集的量比較大,就先選擇第二種試試看。首先分析一下微博的網頁。首先是按關鍵字搜索微博,可使用高級搜索進行過濾,選着原創和含圖片選項,過濾掉轉發的重複內容和不含圖片的微博。問題是改版後的微博搜索只能顯示第一頁,後面內容須要登錄才能看的。因此咱們的爬蟲程序首要經過登錄這一關。微博的驗證碼能夠手動取消,位置以下圖:正則表達式

1.登錄json

  我知道的登錄的方法有3種。c#

  1,模擬登錄過程。C#地webBrowser封裝了操控瀏覽器的方法,根據ID獲取HTML上的控件填充表單,點擊登錄等,很是的人性化操做,可是須要識別驗證碼的代碼。瀏覽器

  2,模擬登錄POST,要用到抓包工具,好比burpsuite。這可能須要瞭解JS加密方法,以及驗證碼,具體網上找找。

  3。第三種比較簡單,模擬登錄後狀態。到瀏覽器找與s.weibo.com鏈接的heards,提取COOKIE字段加入到代碼做爲heard一塊兒發送request。具體是打開網站-》F12-》NetWor選項卡-》Documents / Scripts-》點個文件看看-》heards的Hosst是否是s.weibo.com,是的話把裏面的cookie複製下來。注意cookie閣段時間會變化。因爲只要登錄一次就能取數據,爲了節省時間我就用了第三種方法。

2.得到html源文件

  要得到源文件首先要向服務器發送訪問請求。新浪微博搜索的URL是:http://s.weibo.com/wb/xiada&xsort=time 。仔細看會發現是像php命令風格的url:

 

    1.其中s.weibo.com是Host。 /wb/是指搜微博,綜合搜索的話是/weibo/,找人的話是/user/,圖片是/pic/。注意這裏的搜的據我觀察好像是用戶使用發微博功能現拍的單圖內容,因此內容少不少。

  2.接着xiada就是搜索的關鍵詞了,若是是中文關鍵詞,這個字段就是經URLEncode編碼的字符串。

    3.Xsort=time 是搜索結果按時間排序,

  4.page=2,就是頁數了,沒有寫就是第一頁啦。能夠經過更換page=多少來實現翻頁。

  5.還有一些其餘命令就不列舉了,這些命令的可選值 均可以按"高級搜索" 選擇你要的篩選條件再看看URL是什麼樣的就知道啦。

你能夠作個界面讓用戶(也就是本身。。)來設置要搜什麼樣的微博。用HttpRequest 加上cookie 訪問你經過上面命令生成的url,返回的就是網頁源代碼了。

 

3.解析html獲取數據

  最關鍵的步驟就是怎麼提取你的文檔,假如你用c#的WebBrowser 那麼恭喜你能夠直接用Document類把html裝進去,經過getItemById()之類的方法,根據標籤頭,直接提取標籤內容,很是之方便。我是用正則表達式去源代碼字符串裏匹配我要的數據(圖片連接,下層連接)。正則表達式C#參考這個,總結的夠清楚。

  分析完這同樣還要找下一頁的超連接,在網頁源碼裏搜"next",會發現只有一個,page next前面的herf="..."就是下一頁的連接,能夠根據這個來判斷是否是最後一頁。更快的方法是直接修改上面的命令page=n. 獲得next page的url以後重複步驟2,3直到完成。

  須要注意的是微博的反爬蟲機制,若是你短期內請求30次左右,無論你登錄與否,會被警告而後讓你輸入驗證。解決方法要麼推送到軟件上手動輸入,要麼用瀏覽器手動輸入,要麼加個驗證碼識別模塊。自動驗證碼識別這個能夠用htmlunit實現,我還在研究中。

 

4.源碼

   這個程序適合入門學習,不少特性(存儲策略,網絡優化,性能)都沒考慮進去,我這裏貼出主要代碼。主要部分實現細節能夠參考http://www.cnblogs.com/Jiajun/archive/2012/06/16/2552103.html 這篇博客,思路大體同樣,註釋很詳細,就是有點繁雜了,我借鑑了一點,很是之感謝。  

  另外有更多需求的同窗能夠去研究一下這個項目,在gihub有不少watch and fork,有API讓你使用,並且有不少在爬到的數據能夠下載,值得深刻學習一下源碼。 

 

 1 namespace IMT.weiboSpider  2 {  3     class Spider  4  {  5         #region  field
 6         public List<WB_Feed> feedList { get; set; }  7         private readonly object _locker = new object();  8         private ThreadManager mThreadManager;//workManager.dowork_handler=
 9         public delegate void ContentsSavedHandler(string name);  10         public delegate void DownloadFinishHandler(int count);  11         public event ContentsSavedHandler ContentsSaved = null;  12         public event DownloadFinishHandler DownloadFinish = null;  13         public string _savePath;  14         public string _url;  15         public string _cookie;  16         public string _next_url;  17         public string _html;  18         public int _count_current_URL;  19         #endregion
 20 
 21 
 22         /// <summary>
 23         /// init  24         /// </summary>
 25          public Spider()  26  {  27             feedList = new System.Collections.Generic.List<WB_Feed>();  28             mThreadManager = new ThreadManager(4);  29             mThreadManager.dowork_handler += new ThreadManager.Do_a_Work_Handler(GetIMGDownload);  30  }  31         /// <summary>
 32         /// start working  33         /// </summary>
 34         /// <param name="cookies"></param>
 35         public void Start(string cookies)  36  {  37             _cookie = cookies;  38             Random rad=new Random();  39             _html=GetHtmlDownload(_url, _cookie);  40             _count_current_URL = GetHtmlPrased(_html);  41             _next_url = GetNextHtmlUrl(_html);  42             ContentsSaved.Invoke("url:[" + _url + "]IMG counts:" + _count_current_URL);  43             while (!string.IsNullOrEmpty(_next_url))  44  {  45                 Thread.Sleep(rad.Next(1000,3000));  46                 _html = GetHtmlDownload(_next_url, _cookie);  47                 _count_current_URL = GetHtmlPrased(_html);  48                 if (_count_current_URL < 2)  49  {  50                     MessageBox.Show("須要手動刷新帳號輸入驗證碼,刷新後點再擊肯定");  51                     continue;  52  }  53                 ContentsSaved.Invoke("In The url:[" + _next_url + "]IMG counts:" + _count_current_URL);  54                 _next_url = GetNextHtmlUrl(_html);  55                 
 56  }  57 
 58 
 59             DialogResult dlresult = MessageBox.Show("fund image:" + feedList.Count, "ALL search result was prased and saveD in ./URLCollection.txt.\nContinue download?", MessageBoxButtons.YesNo);  60             switch (dlresult)  61  {  62                 case DialogResult.Yes:  63  {  64                         SaveURLBuffer(_savePath + "URLCollection.txt");  65  mThreadManager.DispatchWork();  66                         break;  67  }  68                 case DialogResult.No:  69  {  70                         SaveURLBuffer(_savePath + "URLCollection.txt");  71                         break;  72  }  73  }  74             //增長顯示狀態欄
 75  }  76 
 77         /// <summary>
 78         /// stop work  79         /// </summary>
 80         public void Abort()  81  {  82             if (mThreadManager != null)  83  {  84  mThreadManager.StopWorking();  85  }  86  }  87 
 88         /// <summary>
 89         /// save the imgages url to local  90         /// </summary>
 91         /// <param name="filepath"></param>
 92         public void SaveURLBuffer(string filepath)  93  {  94             FileStream fs = new FileStream(filepath, FileMode.Create, FileAccess.ReadWrite);  95             StreamWriter sw = new StreamWriter(fs);  96             foreach (var i in feedList)  97  sw.WriteLine(i.imgSrc);  98  sw.Flush();  99  sw.Close(); 100  fs.Close(); 101  } 102 
103         #region   網頁請求方法
104 
105         /// <summary>
106         /// downloaad image in (index) work thread 107         /// </summary>
108         /// <param name="index"></param>
109         private void GetIMGDownload(int index) 110  { 111             string imgUrl = ""; 112             try
113  { 114                 lock (_locker)// lock feedlist access
115  { 116                     if (feedList.Count <= 0) 117  { 118  mThreadManager.FinishWoking(index); 119                         if (mThreadManager.IsAllFinished()) 120  DownloadFinish(index); 121                         return; 122  } 123                     imgUrl = feedList.First().imgSrc; 124                     feedList.RemoveAt(0); 125  } 126                 string fileName = imgUrl.Substring(imgUrl.LastIndexOf("/") + 1); 127                 WebClient wbc = new WebClient(); 128                 wbc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCallback); 129                 wbc.DownloadFileAsync(new Uri(imgUrl), _savePath + fileName,fileName+"in task:"+index); 130  } 131             catch (WebException we) 132  { 133                 System.Windows.Forms.MessageBox.Show("RequestImageFAIL" + we.Message + imgUrl + we.Status); 134  } 135  } 136 
137         /// <summary>
138         /// call this when the img save finished 139         /// </summary>
140         /// <param name="sender"></param>
141         /// <param name="e"></param>
142         private void DownloadFileCallback(object sender, AsyncCompletedEventArgs e) 143  { 144             if (e.Error != null) 145             { //下載成功 146                // WebClient wbc=sender as WebClient; 147              // wbc.Dispose();
148                 if(ContentsSaved!=null) 149                     ContentsSaved((string)sender); 150                 string msg=sender as string; 151                 mThreadManager.FinishWoking((int)msg[msg.Length-1]); 152  mThreadManager.DispatchWork(); 153  } 154             throw new NotImplementedException();//下載完成回調;
155  } 156 
157         /// <summary>
158         /// get response html string 159         /// </summary>
160         /// <param name="wbUrl"></param>
161         /// <param name="heardCookie"></param>
162         /// <returns></returns>
163         public string GetHtmlDownload(string wbUrl, string heardCookie) 164  { 165             string url = wbUrl; 166             string Cookie = heardCookie; 167             string html = null; 168             WebClient client = new WebClient(); 169             client.Encoding = System.Text.ASCIIEncoding.UTF8; 170             client.Headers.Add("Cookie", Cookie); 171 
172             Stream data = client.OpenRead(url); 173 
174             StreamReader reader = new StreamReader(data); 175             html = reader.ReadToEnd(); 176  client.Dispose(); 177             return html; 178  } 179        
180         /// <summary>
181         /// prase html string return the picture url number in this page 182         /// </summary>
183         /// <param name="html"></param>
184         /// <returns></returns>
185         public int GetHtmlPrased(string html) 186  { 187             string _html = html; 188             string result; 189             int count=0; 190             Regex regex = new Regex(@"(?<=http)[^""]+(?=jpg)"); 191             MatchCollection theMatches = regex.Matches(_html); 192             foreach (Match thematch in theMatches) 193  { 194                 if (thematch.Length != 0) 195  { 196                     result = "http" + thematch.Value.Replace("\\", "") + "jpg"; 197 
198                     //TO DO : 定義匹配規則查找相同的微博。
199 
200                     feedList.Add(new WB_Feed(result)); 201                     count++; 202  } 203  } 204             return count; 205  } 206        
207         /// <summary>
208         /// form the url commond to get next page 209         /// </summary>
210         /// <param name="url"></param>
211         /// <param name="num"></param>
212         /// <returns></returns>
213         public string GetNextHtmlUrlFromPageNUM(string url,int num) 214  { 215             string nextPage; 216             string preUrl = url; 217             int pageIdex = preUrl.IndexOf("page")+5; 218             nextPage=preUrl.Remove(pageIdex, 1); 219             nextPage = nextPage.Insert(pageIdex, "" + num); 220             return nextPage; 221  } 222         /// <summary>
223         /// prase html string to get the next page url 224         /// </summary>
225         /// <param name="html"></param>
226         /// <returns></returns>
227         public string GetNextHtmlUrl(string html) 228  { 229             string nextPage; 230             string s_domain; 231             string _html = html; 232 
233             int nextIndex = _html.LastIndexOf("page next");//find last to be fast
234             if (nextIndex < 0) 235  { 236 
237                 MessageBox.Show("there is not nextpage"); 238                 return null; 239  } 240             //MessageBox.Show("find next in=" + nextIndex);
241             int herfIndex = _html.LastIndexOf("href=", nextIndex); 242             nextPage = _html.Substring(herfIndex + 5 + 2, nextIndex - herfIndex); 243             nextPage= nextPage.Substring(0, nextPage.IndexOf(@"""") - 1); 244             nextPage= nextPage.Replace("\\", ""); 245             //$CONFIG['s_domain'] = 'http://s.weibo.com';
246             int domainIndex=html.IndexOf("'s_domain'"); 247             domainIndex = html.IndexOf('=', domainIndex)+3; 248             int domainLength=html.IndexOf(";",domainIndex)-1-domainIndex; 249             s_domain = html.Substring(domainIndex, domainLength); 250 
251             nextPage = s_domain + nextPage; 252             return nextPage; 253  } 254         #endregion
255 
256 
257 
258         /// <summary>
259         /// work group manageer of downloading images with defult 4 work thread; 260         /// </summary>
261         private class ThreadManager 262  { 263             private bool[] _reqBusy = null; //每一個元素表明一個工做實例是否正在工做
264             private int _reqCount = 4; //工做實例的數量
265             private bool _stop = true; 266             public delegate void Do_a_Work_Handler(int index); 267             public Do_a_Work_Handler dowork_handler; 268             public ThreadManager(int threadCount) 269  { 270                 _reqCount = threadCount; 271                 _reqBusy = new bool[threadCount]; 272                 for (int i=0;i<threadCount;i++) 273  { 274                     _reqBusy[i] = false; 275  } 276                 _stop = false; 277  } 278             public void StartWorking(int index) 279  { 280                 _reqBusy[index] = true; 281                 dowork_handler.Invoke(index);/////invoke requeset resource
282  } 283             public void FinishWoking(int index) 284  { 285                 _reqBusy[index] = false; 286  } 287             public bool IsAllFinished() 288  { 289                 bool done = true; 290                 foreach (var i in _reqBusy) 291                     done = i & done; 292                 return done; 293  } 294             public void WaitALLFinished() 295  { 296                 while (!IsAllFinished()) 297                     Thread.Sleep(1000); 298  } 299             public void StopWorking() 300  { 301                 _stop = true; 302                 for (int i = 0; i < _reqCount; i++) 303                     _reqBusy[i] = false; 304  } 305             public void DispatchWork() 306  { 307                 if (_stop) 308                     return; 309                 for (int i = 0; i < _reqCount; i++)////判斷i編號的工做實例是否空閒
310                     if (!_reqBusy[i]) 311  StartWorking(i); 312  } 313  } 314  } 315 
316     /// <summary>
317     /// the class of weibo data you can add more field for get more detail in codes 318     /// </summary>
319     public class WB_Feed 320  { 321         public WB_Feed(string img) 322  { 323             imgSrc = img; 324  } 325         public string imgSrc { get; set; } 326 
327  } 328     
329 }
主要代碼
相關文章
相關標籤/搜索