記一次企業級爬蟲系統升級改造(四):爬取微信公衆號文章(經過搜狗與新榜等第三方平臺)

首先表示抱歉,年末你們都懂的,又涉及SupportYun系統V1.0上線。故而第四篇文章來的有點晚了些~~~對關注的朋友說聲sorry!

SupportYun系統當前一覽:html

  首先說一下,文章的進度一直是延後於系統開發進度的。程序員

  當前系統V1.0 已經正式上線服役了,這就給你們欣賞幾個主要界面~~web

  

  哈哈~這是系統主頁,極簡風格。主體框架使用的是 B-JUI ,偶然間看到的一個開源框架,相信它的做者會把它作得愈來愈好!ajax

  

  這是數據列表的功能頁面,你們對這個table應該很是熟悉哈,我使用的是easyUI的datagrid,沒辦法誰叫他簡單便捷呢~~json

  再給你們秀一秀V1.1版本正在開發的一個界面:【搜一搜】windows

  

  嘿嘿~有木有一種百度搜索結果的既視感...沒辦法,誰叫俺是個程序員呢,也設計不出來啥好樣式,只有照着百度本身拼湊了一個!  api

 技術只是解決問題的選擇,而不是解決問題的根本。瀏覽器

 

爬取微信公衆號方案:微信

  不知道爲何,這個系統作着作着我身上就又多了一個使命:爬取微信文章。app

  誰叫咱是拿錢幹活,而不是拿錢吩咐人幹活的呢!哎~還非得做爲緊急任務添加到了即將上線的V1.0版本,哭>>>>>

  「工欲善其事,必先利其器!」

  「說人話!」

  「我先百度一下怎麼作~~~」

  因此啊,程序員最離不開的仍是萬能的搜索引擎。藉助幾個小時在搜索引擎看的相關帖子、博客、文章等等內容,我也大體總結了一下網友們提到的能夠實現的方案:

    1.微信API獲取   ——想一想都是醉了,腫麼可能...

    2.抓取微信歷史文章頁面   ——聽說每一個微信公衆號都有一個固定地址的歷史文章頁面,嗯,我看行...

    3.經過搜狗搜索微信,抓取結果列表   ——啊哦,這不就成了抓網頁了麼,個人系統有現成的方案啊...

    4.使用相似新榜這樣的內容網站平臺   ——本質和搜狗應該想差不大...

    5.能搜到不少各式各樣的抓取軟件,看介紹是有相似功能的,不過不是要錢就是要各類幣,故我也沒作嘗試...

  嗯,刀磨鋒利了,該選幾棵樹試試效果了。

  以博主這幾個小時攝取的知識來看,第3/4兩條,經過搜狗、新榜來爬取它們的網頁比較靠譜,也和SupportYun當前系統相符(原本咱就是一個單純的網站爬取系統)。至於第二條,爬歷史文章,這個因爲BOSS的關注點更在於能抓取到最新的文章,而不是大量的歷史文章,故而放棄嘗試。

我到底該學什麼?------別問,學就對了; 

我到底該怎麼作?------別問,作就對了。

 

初嘗搜狗搜索,抓取微信公衆號文章:

  打開搜狗搜索,咱們能夠看到,搜索微信文章分兩種模式:搜文章、搜公衆號

  先來看搜文章:

  

  根據搜索結果,咱們發現,這至關於內容搜索,結果不侷限於任何公衆號。因爲博主的BOSS要求的是爬取指定的一堆微信公衆號的最新文章...因此,放棄搜文章。不過你們要是有其餘需求,仍是能夠經過這個方案來構建url抓取數據的。

  咱們再來看搜公衆號是個什麼鬼:

  

  OK,咱們點擊某一個公衆號進去看看。額~~~竟然讓輸入驗證碼。好萬惡!

  進去後咱們發現是直接到了微信的列表頁,觀察url,有過期的風險。以小道消息的url爲例:

  http://mp.weixin.qq.com/profile?src=3&timestamp=1484034436&ver=1&signature=5ANAj3eXwUD5KImAqpqhfnnzIx49V9*lzIc-MKxq21VwMoq51PCrd2NxcOqQPbt35Zg5SrRXDB418Rj48HCV5Q==

  在url中看見timestamp=1484034436這種帶有時間戳的參數,通常都會存在過時問題,博主屢次嘗試對該url的時間戳進行替換,發現signature參數的值應該是與時間戳有所關聯,故而很難本身從新構建url。

  假如咱們先忽略掉驗證碼與列表url過時問題,畢竟咱們還有搜索文章那一條路,是不存在這兩個問題的。那麼咱們看到的就是一個標準的列表頁面:

  

  抓取這種頁面是SupportYun系統的強項,具體代碼你們能夠看第二篇文章:

  記一次企業級爬蟲系統升級改造(二):基於AngleSharp實現的抓取服務

    注:此處詳情頁的url也是會過期的,通過博主試驗,咱們只須要在url後面統一加上&devicetype=Windows-QQBrowser&version=61030004&pass_ticket=qMx7ntinAtmqhVn+C23mCuwc9ZRyUp20kIusGgbFLi0=&uin=MTc1MDA1NjU1&ascene=1 ,這樣,瀏覽器訪問時就會自動改變url地址爲該文章固定地址。

  因此,利用搜狗搜索來爬取微信公衆號文章,比較可取的方案是經過關鍵詞搜索文章列表,而後爬取列表。切記詳情頁url過期處理。

當你試圖解決一個你不理解的問題時,複雜化就產成了。 

 

基於新榜API爬取微信公衆號文章:

  咱們首先在新榜平臺搜索想要抓取的公衆號,例:互聯網扒皮哥

  

  點擊進入該公衆號頁面,咱們看到此時url:http://www.newrank.cn/public/info/detail.html?account=hlwbpg

  經屢次測試,該格式固定爲參數account值是公衆號中文拼音(或全拼或首字母縮寫),OK,省略掉url過期的憂傷。

  

  咱們看到新榜一共爲該公衆號展現20篇文章,10篇最新,10篇7天最熱...這不正是博主千辛萬苦想要的數據麼~~

  Ok~咱們F12查看,該20條數據爲ajax異步請求api渲染,該api狀況以下:

  地址:http://www.newrank.cn/xdnphb/detail/getAccountArticle

  請求方式:POST

  參數:

  經屢次試驗,咱們會發現每次請求,flag的值永遠爲true,uuid的值每一個公衆號固定不變,而nonce與xyz的值刷新一次變化一次。

  咱們能夠大膽的猜想uuid的值是該公衆號在新榜庫的惟一標識,而nonce與xyz的值變化不影響結果輸出...

  而後使用在線http模擬工具一測試,果真如咱們所料...

  

  這就很清晰明瞭了,針對不一樣的微信公衆號,咱們只須要找到它在新榜平臺的惟一標識uuid,就能夠經過定時輪詢掃描該API來爬取最新的微信文章。

  咱們新建一個基於api的抓取服務:BasedOnAPIGrabService

  核心抓取方法:  

 1         public void OprGrab(Guid ruleId)
 2         {
 3             using (var context = new SupportYunDBContext())
 4             {
 5                 var ruleInfo = context.CollectionRule.Find(ruleId);
 6                 if (ruleInfo == null)
 7                 {
 8                     throw new Exception("抓取規則已不存在!");
 9                 }
10 
11                 var listUrl = ruleInfo.ListUrlRule.Split(new[] { ',' });
12                 // 獲取列表
13                 //var client = new HttpClient();
14                 //HttpContent content = new StringContent("flag=" + listUrl[0] + "&uuid=" + listUrl[1],Encoding.UTF8);
15                 //var responseJson = client.PostAsync(ruleInfo.WebListUrl, content).Result.Content.ReadAsStringAsync().Result;
16                 var responseJson = new HttpHelper().PushToWeb(ruleInfo.WebListUrl, "flag=" + listUrl[0] + "&uuid=" + listUrl[1],
17                     Encoding.UTF8);
18                 var responseModel = new JavaScriptSerializer().Deserialize<ResponseModel>(responseJson);
19 
20                 if (responseModel.success == "True" && responseModel.value != null &&
21                     (responseModel.value.lastestArticle.Any() || responseModel.value.topArticle.Any()))
22                 {
23                     var newList = new List<DataModel>();
24                     newList.AddRange(responseModel.value.lastestArticle);
25                     newList.AddRange(responseModel.value.topArticle);
26                     newList = newList.Distinct(i => i.title, StringComparer.CurrentCultureIgnoreCase).ToList();
27                     foreach (var list in newList)
28                     {
29                         angleSharpGrabService.GrabDetail(ruleInfo, list.url, list.title, context);
30                     }
31                 }
32             }
33         }

  其中第18行,是講json字符串序列化爲對應的object。須要引用System.Web.Extensions

  註釋掉的幾行是準備使用HttpClient來實現Post請求,但該接口使用該方法一直報錯,沒法獲取參數。故而本身寫了一個PushToWeb方法來實現Post請求:  

 1         public string PushToWeb(string weburl, string data, Encoding encode)
 2         {
 3             var byteArray = encode.GetBytes(data);
 4 
 5             var webRequest = (HttpWebRequest)WebRequest.Create(new Uri(weburl));
 6             webRequest.Method = "POST";
 7             webRequest.ContentType = "application/x-www-form-urlencoded";
 8             webRequest.ContentLength = byteArray.Length;
 9             var newStream = webRequest.GetRequestStream();
10             newStream.Write(byteArray, 0, byteArray.Length);
11             newStream.Close();
12 
13             //接收返回信息:
14             var response = (HttpWebResponse)webRequest.GetResponse();
15             var aspx = new StreamReader(response.GetResponseStream(), encode);
16             return aspx.ReadToEnd();
17         }

  抓取詳情頁的方法GrabDetail,是重構AngleSharpGrabService服務的OprGrab方法提取出來的,具體的AngleSharpGrabService服務源碼及思路請參考第二篇文章:

  記一次企業級爬蟲系統升級改造(二):基於AngleSharp實現的抓取服務

  

 1 public void GrabDetail(CollectionRule ruleInfo, string realUrl,string title,SupportYunDBContext context)
 2         {
 3             if (!IsRepeatedGrab(realUrl, ruleInfo.Id, title))
 4             {
 5                 string contentDetail = GetHtml(realUrl, ruleInfo.GetCharset());
 6                 var detailModel = DetailAnalyse(contentDetail, title, ruleInfo);
 7 
 8                 if (!string.IsNullOrEmpty(detailModel.FullContent))
 9                 {
10                     var ruleModel = context.CollectionRule.Find(ruleInfo.Id);
11                     ruleModel.LastGrabTime = DateTime.Now;
12                     var newData = new CollectionInitialData()
13                     {
14                         CollectionRule = ruleModel,
15                         CollectionType = ruleModel.CollectionType,
16                         Title = detailModel.Title,
17                         FullContent = detailModel.FullContent,
18                         Url = realUrl,
19                         ProcessingProgress = ProcessingProgress.未處理
20                     };
21                     if (!IsRepeatedGrab(realUrl, ruleInfo.Id, newData.Title))
22                     {
23                         context.CollectionInitialData.Add(newData);
24                         context.SaveChanges();
25                     }
26                 }
27             }
28         }

   該服務我關聯到了每日查詢6次的group下面,windows服務就會自動每日掃描配置了該api規則的公衆號6次,以知足儘快抓取公衆號最新文章的需求。

若是你交給某人一個程序,你將折磨他一成天;若是你教給某人如何編寫程序, 你將折磨他一生!

  哈哈,我想到了從小聽到大的至理名言:授人以魚,不如授人以漁!

 

  過程當中參考了不少資料,這裏列出博主認爲收穫最多的一個連接,以供你們參考:

   https://www.zhihu.com/question/31285583

 

原創文章,代碼都是從本身項目裏貼出來的。轉載請註明出處哦,親~~~

相關文章
相關標籤/搜索