首先表示抱歉,年底大家都懂的,又涉及SupportYun系統V1.0上線。故而第四篇文章來的有點晚了些~~~對關注的朋友說聲sorry!
SupportYun系統當前一覽:
首先說一下,文章的進度一直是延后於系統開發進度的。
當前系統V1.0 已經正式上線服役了,這就給大家欣賞幾個主要界面~~
哈哈~這是系統主頁,極簡風格。主體框架使用的是 B-JUI ,偶然間看到的一個開源框架,相信它的作者會把它做得越來越好!
這是數據列表的功能頁面,大家對這個table應該非常熟悉哈,我使用的是easyUI的datagrid,沒辦法誰叫他簡單便捷呢~~
再給大家秀一秀V1.1版本正在開發的一個界面:【搜一搜】
嘿嘿~有木有一種百度搜索結果的既視感...沒辦法,誰叫俺是個程序員呢,也設計不出來啥好樣式,只有照着百度自己拼湊了一個!
技術只是解決問題的選擇,而不是解決問題的根本。
爬取微信公眾號方案:
不知道為什么,這個系統做着做着我身上就又多了一個使命:爬取微信文章。
誰叫咱是拿錢干活,而不是拿錢吩咐人干活的呢!哎~還非得作為緊急任務添加到了即將上線的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×tamp=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
原創文章,代碼都是從自己項目里貼出來的。轉載請注明出處哦,親~~~