摘要:我從年就開始做微信公眾號內容的批量采集,最開始的目的是為了做一個的垃圾內容網站。經過實測的微信客戶端在批量采集過程中崩潰率高于安卓系統。在年年初的時候微信公眾號和微信文章開始使用鏈接。
我從2014年就開始做微信公眾號內容的批量采集,最開始的目的是為了做一個html5的垃圾內容網站。當時垃圾站采集到的微信公眾號的內容很容易在公眾號里面傳播。當時批量采集特別好做,采集入口是公眾號的歷史消息頁。這個入口到現在也是一樣,只不過越來越難采集了。采集的方式也更新換代了好多個版本。后來在2015年html5垃圾站不做了,轉向將采集目標定位在本地新聞資訊類公眾號,前端顯示做成了app。所以就形成了一個可以自動采集公眾號內容的新聞app。曾經我一直擔心有一天微信技術升級之后無法采集內容了,我的新聞app就失效了。但隨著微信不斷的技術升級,采集方法也隨之升級,反而使我越來越有信心。只要公眾號歷史消息頁存在,就能批量采集到內容。所以今天決定將采集方法整理之后寫下來。我的方法來源于許多同行的分享精神,所以我也會延續這個精神,將我的成果分享出來。
本篇文章將持續更新,你所看到的內容將保證在看到的時間是可用的。
首先我們來看一個微信公眾號歷史消息頁面的鏈接地址:
http://mp.weixin.qq.com/mp/getmasssendmsg?__biz=MjM5MzczNjY2NA==&uin=NzM4MTk1ODgx&key=9ed31d4918c154c8e04cb95d0b28d07ae8eda2ba29a25f538d06adfa060e5d7d42a1427e8f9cfb6a4c3ecc0903a1a9ab87d1471e43705a8b04e1a796612405546f901ec1e4ea662122bb9235f4dfea4d&devicetype=android-17&version=26031c34&lang=zh_CN&nettype=WIFI&ascene=3&pass_ticket=iyVknv0cBEc1Z8oR4zVs%2BkLeRwYtW5bbtL4Tj9bm%2FwgjP%2BsobV6en3WohWUOllUU&wx_header=1
這里面有幾個參數:
__biz;uin=;key=;devicetype=;version=;lang=;nettype=;ascene=;pass_ticket=;wx_header=;
其中重要的參數是:__biz;uin=;key=;pass_ticket=;這4個參數。
__biz是公眾號的一個類似id的參數,每個公眾號擁有一個微信的biz,目前極小概率會發生公眾號的biz會變化的事件;
剩下的3個參數是有關用戶的id和令牌票據之類的意思,這3個參數的值只能通過微信的客戶端產生。所以我們想采集公眾號就必須通過一個微信客戶端app。在以前的微信版本中這3個參數還可以獲取一次之后在有效期之內多個公眾號通用。現在的版本已經是每次訪問一個公眾號都會更換參數值。
我現在所使用的方法只需要關注__biz這個參數就可以了。
我的采集系統由以下幾部分組成:
1、一個微信客戶端:可以是一臺手機安裝了微信的app,或者是用電腦中的安卓模擬器。經過實測ios的微信客戶端在批量采集過程中崩潰率高于安卓系統。為了降低成本,我使用的是安卓模擬器。
2、一個微信個人號:為了采集內容不僅需要微信客戶端,還要有一個微信個人號專門用于采集,因為這個微信號就干不了其它事情了。
3、本地代理服務器系統:目前使用的方法是通過Anyproxy代理服務器將公眾號歷史消息頁面中的文章列表發送到自己的服務器上。具體安裝設置方法在后面詳細介紹。
4、文章列表分析與入庫系統:我用的是php語言編寫的,后文將詳細介紹如何分析文章列表和建立采集隊列實現批量采集內容。
步驟
一、安裝模擬器或使用手機安裝微信客戶端app,申請微信個人號并登錄到app上面。這一點就不過多介紹了,大家都會。
二、代理服務器系統安裝
目前我使用的是Anyproxy,AnyProxy 。這個軟件的特點是可以獲取到https鏈接的內容。在2016年年初的時候微信公眾號和微信文章開始使用https鏈接。并且Anyproxy可以通過修改rule配置實現向公眾號的頁面中插入腳本代碼。下面開始介紹安裝與配置過程。
1、安裝 NodeJS
2、在命令行或者終端運行 npm install -g anyproxy,mac系統需要加上sudo;
3、生成RootCA,https需要這個證書:運行命令sudo anyproxy --root(windows可能不需要sudo);
4、啟動anyproxy運行命令:sudo anyproxy -i;參數-i是解析HTTPS的意思;
5、安裝證書,在手機或安卓模擬器中安裝證書:
方法一: 啟動anyproxy,瀏覽器打開 http://localhost:8002/fetchCr... ,能獲取rootCA.crt文件
方法二:啟動anyproxy,http://localhost:8002/qr_root 可以獲取證書路徑的二維碼,移動端安裝時會比較便捷
建議通過二維碼將證書安裝到手機中。
6、設置代理:安卓模擬器的代理服務器地址是wifi鏈接的網關,可以通過吧dhcp設置為靜態后看到網關地址,看完后別忘了再設置為自動。手機中的代理服務器地址就是運行anyproxy的電腦的ip地址。代理服務器默認端口是8001;
現在打開微信,點擊到任意一個公眾號歷史消息或文章中,在終端都可以看到響應的代碼滾動。如果沒有出現,請檢查手機的代理設置是否正確。
現在打開瀏覽器地址http://localhost:8002 可以看到anyproxy的web界面。從微信中點開一個歷史消息頁面,然后再看瀏覽器的web界面,會滾動出現歷史消息頁面的地址。
以/mp/getmasssendmsg開頭的網址就是微信歷史消息頁面。左邊一個小鎖頭表示這個頁面是https加密的。現在我們點擊一下這一行;
右邊如果出現了html的文件內容則表示解密成功。如果沒有內容,請檢查anyproxy的運行模式是否有參數i,是否生成了ca證書,手機是否正確安裝證書。
現在我們的手機中的所有內容都已經可以明文通過代理服務器了。下面我們要修改配置代理服務器,使公眾號內容被獲取到。
一、找到配置文件:
mac系統中配置文件的位置在/usr/local/lib/node_modules/anyproxy/lib/;windows系統請原諒我暫時不知道。應該可以根據類似mac的文件夾地址找到這個目錄。
二、修改文件rule_default.js
找到replaceServerResDataAsync: function(req,res,serverResData,callback) 函數
修改函數內容(請注意詳細閱讀注釋,這里只是介紹原理,理解后根據自己的條件修改內容):
replaceServerResDataAsync: function(req,res,serverResData,callback){ if(/mp/getmasssendmsg/i.test(req.url)){//當鏈接地址為公眾號歷史消息頁面時 try {//防止報錯退出程序 var reg = /msgList = (.*?); /;//定義歷史消息正則匹配規則 var ret = reg.exec(serverResData.toString());//轉換變量為string HttpPost(ret[1],req.url,"getMsgJson.php");//這個函數是后文定義的,將匹配到的歷史消息json發送到自己的服務器 var http = require("http"); http.get("http://xxx.com/getWxHis.php", function(res) {//這個地址是自己服務器上的一個程序,目的是為了獲取到下一個鏈接地址,將地址放在一個js腳本中,將頁面自動跳轉到下一頁。后文將介紹getWxHis.php的原理。 res.on("data", function(chunk){ callback(chunk+serverResData);//將返回的代碼插入到歷史消息頁面中,并返回顯示出來 }) }); }catch(e){//如果上面的正則沒有匹配到,那么這個頁面內容可能是公眾號歷史消息頁面向下翻動的第二頁,因為歷史消息第一頁是html格式的,第二頁就是json格式的。 try { var json = JSON.parse(serverResData.toString()); if (json.general_msg_list != []) { HttpPost(json.general_msg_list,req.url,"getMsgJson.php");//這個函數和上面的一樣是后文定義的,將第二頁歷史消息的json發送到自己的服務器 } }catch(e){ console.log(e);//錯誤捕捉 } callback(serverResData);//直接返回第二頁json內容 } }else if(/mp/getappmsgext/i.test(req.url)){//當鏈接地址為公眾號文章閱讀量和點贊量時 try { HttpPost(serverResData,req.url,"getMsgExt.php");//函數是后文定義的,功能是將文章閱讀量點贊量的json發送到服務器 }catch(e){ } callback(serverResData); }else if(/s?__biz/i.test(req.url) || /mp/rumor/i.test(req.url)){//當鏈接地址為公眾號文章時(rumor這個地址是公眾號文章被辟謠了) try { var http = require("http"); http.get("http://xxx.com/getWxPost.php", function(res) {//這個地址是自己服務器上的另一個程序,目的是為了獲取到下一個鏈接地址,將地址放在一個js腳本中,將頁面自動跳轉到下一頁。后文將介紹getWxPost.php的原理。 res.on("data", function(chunk){ callback(chunk+serverResData); }) }); }catch(e){ callback(serverResData); } }else{ callback(serverResData); } },
上面這段代碼是利用anyproxy可以修改返回頁面內容的功能,向頁面注入腳本,和將頁面內容發送到服務器上。使用這個原理來批量采集公眾號內容和閱讀量。這段腳本中自定義了一個函數,下面詳細介紹:
在rule_default.js文件末尾添加以下代碼:
function HttpPost(str,url,path) {//將json發送到服務器,str為json內容,url為歷史消息頁面地址,path是接收程序的路徑和文件名 var http = require("http"); var data = { str: encodeURIComponent(str), url: encodeURIComponent(url) }; content = require("querystring").stringify(data); var options = { method: "POST", host: "www.xxx.com",//注意沒有http://,這是服務器的域名。 port: 80, path: path,//接收程序的路徑和文件名 headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "Content-Length": content.length } }; var req = http.request(options, function (res) { res.setEncoding("utf8"); res.on("data", function (chunk) { console.log("BODY: " + chunk); }); }); req.on("error", function (e) { console.log("problem with request: " + e.message); }); req.write(content); req.end(); }
上面就是rule規則修改的主要部分,需要將json內容發送到自己的服務器,還要從服務器獲取到下一頁的跳轉地址。這就涉及到了四個php文件:getMsgJson.php、getMsgExt.php、getWxHis.php、getWxPost.php
在詳細介紹這4個php文件之前,為了提高采集系統性能和降低崩潰率,我們還可以進行一些修改:
安卓模擬器經常會訪問一些http://google.com的地址,這樣會導致anyproxy死機,找到函數replaceRequestOption : function(req,option),修改函數內容:
replaceRequestOption : function(req,option){ var newOption = option; if(/google/i.test(newOption.headers.host)){ newOption.hostname = "www.baidu.com"; newOption.port = "80"; } return newOption; },
以上就是針對anyproxy的rule文件的修改配置,配置修改完成之后,重新啟動anyproxy。mac系統里按control+c中斷程序,再輸入命令sudo anyproxy -i啟動;如果啟動報錯,可能是程序沒有退出干凈,端口被占用。這時輸入命令ps -a查看占用的pid,再輸入命令“kill -9 pid”這里將pid替換成查詢到的pid號碼。殺死進程之后就可以啟動anyproxy了。還是那句話windows的命令請原諒我不太熟悉。
接下來詳細介紹服務器上接收程序的設計原理:
(以下代碼并不是直接可以用的,只是介紹原理,其中一部分需要根據自己的服務器數據庫框架進行編寫)
1、getMsgJson.php:這個程序負責接收歷史消息的json并解析后存入數據庫
$str = $_POST["str"]; $url = $_POST["url"];//先獲取到兩個POST變量 //先針對url參數進行操作 parse_str(parse_url(htmlspecialchars_decode(urldecode($url)),PHP_URL_QUERY ),$query);//解析url地址 $biz = $query["__biz"];//得到公眾號的biz //接下來進行以下操作 //從數據庫中查詢biz是否已經存在,如果不存在則插入,這代表著我們新添加了一個采集目標公眾號。 //再解析str變量 $json = json_decode($str,true);//首先進行json_decode if(!$json){ $json = json_decode(htmlspecialchars_decode($result),true);//如果不成功,就增加一步htmlspecialchars_decode } foreach($json["list"] as $k=>$v){ $type = $v["comm_msg_info"]["type"]; if($type==49){//type=49代表是圖文消息 $content_url = str_replace("", "", htmlspecialchars_decode($v["app_msg_ext_info"]["content_url"]));//獲得圖文消息的鏈接地址 $is_multi = $v["app_msg_ext_info"]["is_multi"];//是否是多圖文消息 $datetime = $v["comm_msg_info"]["datetime"];//圖文消息發送時間 //在這里將圖文消息鏈接地址插入到采集隊列庫中(隊列庫將在后文介紹,主要目的是建立一個批量采集隊列,另一個程序將根據隊列安排下一個采集的公眾號或者文章內容) //在這里根據$content_url從數據庫中判斷一下是否重復 if("數據庫中不存在相同的$content_url") { $field_id = $v["app_msg_ext_info"]["fileid"];//一個微信給的id,每條文章唯一不重復 $title = $v["app_msg_ext_info"]["title"];//文章標題 $title_encode = urlencode(str_replace(" ", "", $title));//建議將標題進行編碼,這樣就可以存儲emoji特殊符號了 $digest = $v["app_msg_ext_info"]["digest"];//文章摘要 $source_url = str_replace("", "", htmlspecialchars_decode($v["app_msg_ext_info"]["source_url"]));//閱讀原文的鏈接 $cover = str_replace("", "", htmlspecialchars_decode($v["app_msg_ext_info"]["cover"]));//封面圖片 $is_top = 1;//標記一下是頭條內容 //現在存入數據庫 echo "頭條標題:".$title.$lastId." ";//這個echo可以顯示在anyproxy的終端里 } if($is_multi==1){//如果是多圖文消息 foreach($v["app_msg_ext_info"]["multi_app_msg_item_list"] as $kk=>$vv){//循環后面的圖文消息 $content_url = str_replace("","",htmlspecialchars_decode($vv["content_url"]));//圖文消息鏈接地址 //這里再次根據$content_url判斷一下數據庫中是否重復以免出錯 if("數據庫中不存在相同的$content_url"){ //在這里將圖文消息鏈接地址插入到采集隊列庫中(隊列庫將在后文介紹,主要目的是建立一個批量采集隊列,另一個程序將根據隊列安排下一個采集的公眾號或者文章內容) $title = $vv["title"];//文章標題 $field_id = $vv["fileid"];//一個微信給的id,每條文章唯一不重復 $title_encode = urlencode(str_replace(" ","",$title));//建議將標題進行編碼,這樣就可以存儲emoji特殊符號了 $digest = htmlspecialchars($vv["digest"]);//文章摘要 $source_url = str_replace("","",htmlspecialchars_decode($vv["source_url"]));//閱讀原文的鏈接 //$cover = getCover(str_replace("","",htmlspecialchars_decode($vv["cover"]))); $cover = str_replace("","",htmlspecialchars_decode($vv["cover"]));//封面圖片 //現在存入數據庫 echo "標題:".$title.$lastId." "; } } } } } ?>
再次強調代碼只是原理,其中一部分注視的代碼要自己編寫。
2、getMsgExt.php獲取文章閱讀量和點贊量的程序
$str = $_POST["str"]; $url = $_POST["url"];//先獲取到兩個POST變量 //先針對url參數進行操作 parse_str(parse_url(htmlspecialchars_decode(urldecode($url)),PHP_URL_QUERY ),$query);//解析url地址 $biz = $query["__biz"];//得到公眾號的biz $sn = $query["sn"]; //再解析str變量 $json = json_decode($str,true);//進行json_decode //$sql = "select * from `文章表` where `biz`="".$biz."" and `content_url` like "%".$sn."%"" limit 0,1; //根據biz和sn找到對應的文章 $read_num = $json["appmsgstat"]["read_num"];//閱讀量 $like_num = $json["appmsgstat"]["like_num"];//點贊量 //在這里同樣根據biz和sn在采集隊列表中刪除對應的文章,代表這篇文章可以移出采集隊列了 //然后將閱讀量和點贊量更新到文章表中。 exit(json_encode($msg));//可以顯示在anyproxy的終端里 ?>
3、getWxHis.php、getWxPost.php兩個程序比較類似,一起介紹
//getWxHis.php 當前頁面為公眾號歷史消息時,讀取這個程序 //在采集隊列表中有一個load字段,當值等于1時代表正在被讀取 //首先刪除采集隊列表中load=1的行 //然后從隊列表中任意select一行 if("隊列表為空"){ //隊列表如果空了,就從存儲公眾號biz的表中取得一個biz,這里我在公眾號表中設置了一個采集時間的time字段,按照正序排列之后,就得到時間戳最小的一個公眾號記錄,并取得它的biz $url = "http://mp.weixin.qq.com/mp/getmasssendmsg?__biz=".$biz."#wechat_webview_type=1&wechat_redirect";//拼接公眾號歷史消息url地址 //更新剛才提到的公眾號表中的采集時間time字段為當前時間戳。 }else{ //取得當前這一行的content_url字段 $url = $content_url; //將load字段update為1 } echo "";//將下一個將要跳轉的$url變成js腳本,由anyproxy注入到微信頁面中。 ?> //getWxPost.php 當前頁面為公眾號文章頁面時,讀取這個程序 //首先刪除采集隊列表中load=1的行 //然后從隊列表中按照“order by id asc”選擇多行(注意這一行和上面的程序不一樣) if(!empty("隊列表") && count("隊列表中的行數")>1){//(注意這一行和上面的程序不一樣) //取得第0行的content_url字段 $url = $content_url; //將第0行的load字段update為1 }else{ //隊列表還剩下最后一條時,就從存儲公眾號biz的表中取得一個biz,這里我在公眾號表中設置了一個采集時間的time字段,按照正序排列之后,就得到時間戳最小的一個公眾號記錄,并取得它的biz $url = "http://mp.weixin.qq.com/mp/getmasssendmsg?__biz=".$biz."#wechat_webview_type=1&wechat_redirect";//拼接公眾號歷史消息url地址 //更新剛才提到的公眾號表中的采集時間time字段為當前時間戳。 } echo "";//將下一個將要跳轉的$url變成js腳本,由anyproxy注入到微信頁面中。 ?>
這兩個程序的微小差別是因為當讀取公眾號歷史消息頁面時,anyproxy會同時做兩件事,第一是將歷史消息的json發送到服務器,第二是獲取到下一頁的鏈接地址。但是這兩個操作是存在時間差的,第一次讀取下一頁地址時候本來應該是得到當前這個公眾號文章的第一條鏈接地址,但是這時候歷史消息的json還沒有發送到服務器,所以只能得到第二個公眾號的歷史消息頁面。在讀取第二個公眾號歷史消息頁面之后得到的下一頁地址則是第一個公眾號的第一篇文章的地址。當隊列還剩下一條記錄時,就需要再去取得下一個公眾號的鏈接地址,否則如果當隊列空了再去取得下一個公眾號的鏈接地址,就會循環到上面提到的第一次讀取時的情況,這樣就會出現兩個公眾號歷史消息列表和文章采集穿插進行的情況。
剛才這4個PHP程序提到了幾個數據表,下面再講一下數據表如何設計。這里只介紹一些主要字段,現實應用中還會根據自己程序的不同添加上其它有必要的字段。
1、微信公眾號表
CREATE TABLE `weixin` ( `id` int(11) NOT NULL AUTO_INCREMENT, `biz` varchar(255) DEFAULT "" COMMENT "公眾號唯一標識biz", `collect` int(11) DEFAULT "1" COMMENT "記錄采集時間的時間戳", PRIMARY KEY (`id`) ) ;
2、微信文章表
CREATE TABLE `post` ( `id` int(11) NOT NULL AUTO_INCREMENT, `biz` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT "文章對應的公眾號biz", `field_id` int(11) NOT NULL COMMENT "微信定義的一個id,每條文章唯一", `title` varchar(255) NOT NULL DEFAULT "" COMMENT "文章標題", `title_encode` text CHARACTER SET utf8 NOT NULL COMMENT "文章編碼,防止文章出現emoji", `digest` varchar(500) NOT NULL DEFAULT "" COMMENT "文章摘要", `content_url` varchar(500) CHARACTER SET utf8 NOT NULL COMMENT "文章地址", `source_url` varchar(500) CHARACTER SET utf8 NOT NULL COMMENT "閱讀原文地址", `cover` varchar(500) CHARACTER SET utf8 NOT NULL COMMENT "封面圖片", `is_multi` int(11) NOT NULL COMMENT "是否多圖文", `is_top` int(11) NOT NULL COMMENT "是否頭條", `datetime` int(11) NOT NULL COMMENT "文章時間戳", `readNum` int(11) NOT NULL DEFAULT "1" COMMENT "文章閱讀量", `likeNum` int(11) NOT NULL DEFAULT "0" COMMENT "文章點贊量", PRIMARY KEY (`id`) ) ;
3、采集隊列表
CREATE TABLE `tmplist` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `content_url` varchar(255) DEFAULT NULL COMMENT "文章地址", `load` int(11) DEFAULT "0" COMMENT "讀取中標記", PRIMARY KEY (`id`), UNIQUE KEY `content_url` (`content_url`) ) ;
以上就是由微信客戶端、微信號、anyproxy代理服務器、PHP程序、mysql數據庫共同組成的微信公眾號文章批量自動采集系統。
在接下來的文章中,還會再進一步詳細介紹如何保存文章內容,如何提高采集系統的穩定性,以及其它我的系統運行過程中得到的經驗。
非常希望大家能給予意見和交流,歡迎騷擾微信號cuijin。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/22184.html
摘要:要想實現微信公眾號文章的爬取,需要做兩部分系統處理。詳細實現步驟文章和源碼資源見個人博文微信公眾號文章采集之服務端數據采集 要想實現微信公眾號文章的爬取,需要做兩部分系統處理。 一、公眾號文章的自動化瀏覽處理 一個是移動端的公眾號文章自動瀏覽實現,逐個訪問瀏覽公眾號的歷史文章,在瀏覽公眾號文章的時候會請求公眾號的文章鏈接地址,通過AnyProxy中間人代理解析工具,可以獲取到永久的文章...
摘要:本文轉載自微信公眾號賬號,作者為海航生態科技技術研究院大數據開發工程師高顏。文章介紹了海航生態科技輿情大數據平臺的容器化改造經驗,包括初期技術架構應用容器化架構遷移持續發布與部署。 本文轉載自微信公眾號Docker(賬號:dockerone),作者為海航生態科技技術研究院大數據開發工程師高顏。 文章介紹了海航生態科技輿情大數據平臺的容器化改造經驗,包括初期技術架構、應用容器化、架構遷...
摘要:再如通過處理流數據生成簡單的報告,如五分鐘的窗口聚合數據平均值。復雜的事情還有在流數據中進行數據多維度關聯聚合塞選,從而找到復雜事件中的根因。因為各種需求,也就造就了現在不斷出現實時計算框架,而下文我們將重磅介紹我們推薦的實時計算框架。 前言 先廣而告之,本文摘自本人《大數據重磅炸彈——實時計算框架 Flink》課程第二篇,內容首發自我的知識星球,后面持續在星球里更新,這里做個預告,今...
摘要:擴展庫還包括用于復雜事件處理,機器學習,圖形處理和兼容性的專用代碼庫。事件時間機制使得那些事件無序到達甚至延遲到達的數據流能夠計算出精確的結果。負責接受用戶的程序代碼,然后創建數據流,將數據流提交給以便進一步執行。 showImg(https://segmentfault.com/img/remote/1460000016902812); 前言 Flink 是一種流式計算框架,為什么我...
閱讀 2977·2021-11-23 09:51
閱讀 3608·2021-10-13 09:39
閱讀 2490·2021-09-22 15:06
閱讀 881·2019-08-30 15:55
閱讀 3146·2019-08-30 15:44
閱讀 1778·2019-08-30 14:05
閱讀 3434·2019-08-29 15:24
閱讀 2362·2019-08-29 12:44