最近需要爬取微信公眾號的文章信息。在網上找了找發現微信公眾號爬取的難點在于公眾號文章鏈接在pc端是打不開的,要用微信的自帶瀏覽器(拿到微信客戶端補充的參數,才可以在其它平臺打開),這就給爬蟲程序造成很大困擾。后來在知乎上看到了一位大牛用php寫的微信公眾號爬取程序,就直接按大佬的思路整了整搞成java的了。改造途中遇到蠻多細節問題,拿出來分享一下。
系統的基本思路是在安卓模擬器上運行微信,模擬器設置代理,通過代理服務器攔截微信數據,將得到的數據發送給自己的程序進行處理。
需要準備的環境:nodejs,anyproxy代理,安卓模擬器
nodejs下載地址:http://nodejs.cn/download/,我下載的是windows版的,下好直接安裝就行。安裝好后,直接運行c:program files odejs pm.cmd 會自動配置好環境。
anyproxy安裝:按上一步安裝好nodejs之后,直接在cmd運行 npm install -g anyproxy 就會安裝了
安卓模擬器隨便在網上下一個就好了,一大堆。
首先為代理服務器安裝證書,anyproxy默認不解析https鏈接,安裝證書后就可以解析了,在cmd執行anyproxy --root 就會安裝證書,之后還得在模擬器也下載這個證書。
然后輸入anyproxy -i 命令 打開代理服務。(記得加上參數!)
記住這個ip和端口,之后安卓模擬器的代理就用這個?,F在用瀏覽器打開網頁:http://localhost:8002/ 這是anyproxy的網頁界面,用于顯示http傳輸數據。
點擊上面紅框框里面的菜單,會出一個二維碼,用安卓模擬器掃碼識別,模擬器(手機)就會下載證書了,安裝上就好了。
現在準備為模擬器設置代理,代理方式設置為手動,代理ip為運行anyproxy機器的ip,端口是8001
到這里準備工作基本完成,在模擬器上打開微信隨便打開一個公眾號的文章,就能從你剛打開的web界面中看到anyproxy抓取到的數據:
上面紅框內就是微信文章的鏈接,點擊進去可以看到具體的數據。如果response body里面什么都沒有可能證書安裝有問題。
如果上面都走通了,就可以接著往下走了。
這里我們靠代理服務抓微信數據,但總不能抓取一條數據就自己操作一下微信,那樣還不如直接人工復制。所以我們需要微信客戶端自己跳轉頁面。這時就可以使用anyproxy攔截微信服務器返回的數據,往里面注入頁面跳轉代碼,再把加工的數據返回給模擬器實現微信客戶端自動跳轉。
打開anyproxy中的一個叫rule_default.js的js文件,windows下該文件在:c:usersdministratorppdata oaming pm ode_modulesnyproxylib
在文件里面有個叫replaceserverresdataasync: function(req,res,serverresdata,callback)的方法,這個方法就是負責對anyproxy拿到的數據進行各種操作。一開始應該只有callback(serverresdata);這條語句的意思是直接返回服務器響應數據給客戶端。直接刪掉這條語句,替換成大牛寫的如下代碼。這里的代碼我并沒有做什么改動,里面的注釋也解釋的給非常清楚,直接按邏輯看懂就行,問題不大。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
replaceserverresdataasync: function(req,res,serverresdata,callback){ if (/mp/getmasssendmsg/i.test(req.url)){ //當鏈接地址為公眾號歷史消息頁面時(第一種頁面形式) //console.log("開始第一種頁面爬取"); if (serverresdata.tostring() !== "" ){ 6 try { //防止報錯退出程序 var reg = /msglist = (.*?);/; //定義歷史消息正則匹配規則 var ret = reg.exec(serverresdata.tostring()); //轉換變量為string httppost(ret[ 1 ],req.url, "/internetspider/getdata/showbiz" ); //這個函數是后文定義的,將匹配到的歷史消息json發送到自己的服務器 var http = require( 'http' ); http.get( 'http://xxx/getwxhis' , function(res) {//這個地址是自己服務器上的一個程序,目的是為了獲取到下一個鏈接地址,將地址放在一個js腳本中,將頁面自動跳轉到下一頁。后文將介紹getwxhis.php的原理。 res.on( 'data' , function(chunk){ callback(chunk+serverresdata); //將返回的代碼插入到歷史消息頁面中,并返回顯示出來 }) }); } catch (e){ //如果上面的正則沒有匹配到,那么這個頁面內容可能是公眾號歷史消息頁面向下翻動的第二頁,因為歷史消息第一頁是html格式的,第二頁就是json格式的。 //console.log("開始第一種頁面爬取向下翻形式"); try { var json = json.parse(serverresdata.tostring()); if (json.general_msg_list != []) { httppost(json.general_msg_list,req.url, "/xxx/showbiz" ); //這個函數和上面的一樣是后文定義的,將第二頁歷史消息的json發送到自己的服務器 } } catch (e){ console.log(e); //錯誤捕捉 } callback(serverresdata); //直接返回第二頁json內容 } } //console.log("開始第一種頁面爬取 結束"); } else if (/mp/profile_ext?action=home/i.test(req.url)){ //當鏈接地址為公眾號歷史消息頁面時(第二種頁面形式) try { var reg = /var msglist = '(.*?)';/; //定義歷史消息正則匹配規則(和第一種頁面形式的正則不同) var ret = reg.exec(serverresdata.tostring()); //轉換變量為string httppost(ret[ 1 ],req.url, "/xxx/showbiz" ); //這個函數是后文定義的,將匹配到的歷史消息json發送到自己的服務器 var http = require( 'http' ); http.get( 'xxx/getwxhis' , function(res) { //這個地址是自己服務器上的一個程序,目的是為了獲取到下一個鏈接地址,將地址放在一個js腳本中,將頁面自動跳轉到下一頁。后文將介紹getwxhis.php的原理。 res.on( 'data' , function(chunk){ callback(chunk+serverresdata); //將返回的代碼插入到歷史消息頁面中,并返回顯示出來 }) }); } catch (e){ //console.log(e); callback(serverresdata); } } else if (/mp/profile_ext?action=getmsg/i.test(req.url)){ //第二種頁面表現形式的向下翻頁后的json try { var json = json.parse(serverresdata.tostring()); if (json.general_msg_list != []) { httppost(json.general_msg_list,req.url, "/xxx/showbiz" ); //這個函數和上面的一樣是后文定義的,將第二頁歷史消息的json發送到自己的服務器 } } catch (e){ console.log(e); } callback(serverresdata); } else if (/mp/getappmsgext/i.test(req.url)){ //當鏈接地址為公眾號文章閱讀量和點贊量時 try { httppost(serverresdata,req.url, "/xxx/getmsgext" ); //函數是后文定義的,功能是將文章閱讀量點贊量的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/getwxpost' , function(res) {//這個地址是自己服務器上的另一個程序,目的是為了獲取到下一個鏈接地址,將地址放在一個js腳本中,將頁面自動跳轉到下一頁。后文將介紹getwxpost.php的原理。 res.on( 'data' , function(chunk){ callback(chunk+serverresdata); }) }); } catch (e){ callback(serverresdata); } } else { callback(serverresdata); } //callback(serverresdata); }, |
這里簡單解釋一下,微信公眾號的歷史消息頁鏈接有兩種形式:一種以 mp.weixin.qq.com/mp/getmasssendmsg 開頭,另一種是 mp.weixin.qq.com/mp/profile_ext 開頭。歷史頁是可以向下翻的,如果向下翻將觸發js事件發送請求得到json數據(下一頁內容)。還有公眾號文章鏈接,以及文章的閱讀量和點贊量的鏈接(返回的是json數據),這幾種鏈接的形式是固定的可以通過邏輯判斷來區分。這里有個問題就是歷史頁如果需要全部爬取到該怎么做到。我的思路是通過js去模擬鼠標向下滑動,從而觸發提交加載下一部分列表的請求。或者直接利用anyproxy分析下滑加載的請求,直接向微信服務器發生這個請求。但都有一個問題就是如何判斷已經沒有余下數據了。我是爬取最新數據,暫時沒這個需求,可能以后要。如果有需求的可以嘗試一下。
下圖是上文中的httppost方法內容。
1
|
function httppost(str,url,path) { //將json發送到服務器,str為json內容,url為歷史消息頁面地址,path是接收程序的路徑和文件名<br> console.log("開始執行轉發操作");<br> try{<br> var http = require('http');<br> var data = {<br> str: encodeuricomponent(str),<br> url: encodeuricomponent(url)<br> };<br> data = require('querystring').stringify(data);<br> var options = {<br> method: "post",<br> host: "xxx",//注意沒有http://,這是服務器的域名。<br> port: xxx,<br> path: path,//接收程序的路徑和文件名<br> headers: {<br> 'content-type': 'application/x-www-form-urlencoded; charset=utf-8',<br> "content-length": data.length<br> }<br> };<br> var req = http.request(options, function (res) {<br> res.setencoding('utf8');<br> res.on('data', function (chunk) {<br> console.log('body: ' + chunk);<br> });<br> });<br> req.on('error', function (e) {<br> console.log('problem with request: ' + e.message);<br> });<br> <br> req.write(data);<br> req.end();<br> }catch(e){<br> console.log("錯誤信息:"+e);<br> }<br> console.log("轉發操作結束");<br> } |
做完以上工作,接下來就是按自己業務來完成服務端代碼了,我們的服務用于接收代理服務器發過來的數據進行處理,進行持久化操作,同時向代理服務器發送需要注入到微信的js代碼。針對代理服務器攔截到的幾種不同鏈接發來的數據,我們就需要設計相應的方法來處理這些數據。從anyproxy處理微信數據的js方法replaceserverresdataasync: function(req,res,serverresdata,callback)中,我們可以知道至少需要對公眾號歷史頁數據、公眾號文章頁數據、公眾號文章點贊量和閱讀量數據設計三種方法來處理。同時我們還需要設計一個方法來生成爬取任務,完成公眾號的輪尋爬取。如果需要爬取更多數據,可以從anyproxy抓取到的鏈接中分析出更多需要的數據,然后往replaceserverresdataasync: function(req,res,serverresdata,callback)中添加判定,攔截到需要的數據發送到自己的服務器,相應的在服務端添加方法處理該類數據就行了。
我是用java寫的服務端代碼。
處理公眾號歷史頁數據方法:
- public void getmsgjson(string str ,string url) throws unsupportedencodingexception {
- // todo auto-generated method stub
- string biz = "";
- map<string,string> querystrs = httpurlparser.parseurl(url);
- if(querystrs != null){
- biz = querystrs.get("__biz");
- biz = biz + "==";
- }
- /**
- * 從數據庫中查詢biz是否已經存在,如果不存在則插入,
- * 這代表著我們新添加了一個采集目標公眾號。
- */
- list<weixin> results = weixinmapper.selectbybiz(biz);
- if(results == null || results.size() == 0){
- weixin weixin = new weixin();
- weixin.setbiz(biz);
- weixin.setcollect(system.currenttimemillis());
- weixinmapper.insert(weixin);
- }
- //system.out.println(str);
- //解析str變量
- list<object> lists = jsonpath.read(str, "['list']");
- for(object list : lists){
- object json = list;
- int type = jsonpath.read(json, "['comm_msg_info']['type']");
- if(type == 49){//type=49表示是圖文消息
- string content_url = jsonpath.read(json, "$.app_msg_ext_info.content_url");
- content_url = content_url.replace("", "").replaceall("amp;", "");//獲得圖文消息的鏈接地址
- int is_multi = jsonpath.read(json, "$.app_msg_ext_info.is_multi");//是否是多圖文消息
- integer datetime = jsonpath.read(json, "$.comm_msg_info.datetime");//圖文消息發送時間
- /**
- * 在這里將圖文消息鏈接地址插入到采集隊列庫tmplist中
- * (隊列庫將在后文介紹,主要目的是建立一個批量采集隊列,
- * 另一個程序將根據隊列安排下一個采集的公眾號或者文章內容)
- */
- try{
- if(content_url != null && !"".equals(content_url)){
- tmplist tmplist = new tmplist();
- tmplist.setcontenturl(content_url);
- tmplistmapper.insertselective(tmplist);
- }
- }catch(exception e){
- system.out.println("隊列已存在,不插入!");
- }
- /**
- * 在這里根據$content_url從數據庫post中判斷一下是否重復
- */
- list<post> postlist = postmapper.selectbycontenturl(content_url);
- boolean contenturlexist = false;
- if(postlist != null && postlist.size() != 0){
- contenturlexist = true;
- }
- if(!contenturlexist){//'數據庫post中不存在相同的$content_url'
- integer fileid = jsonpath.read(json, "$.app_msg_ext_info.fileid");//一個微信給的id
- string title = jsonpath.read(json, "$.app_msg_ext_info.title");//文章標題
- string title_encode = urlencoder.encode(title, "utf-8");
- string digest = jsonpath.read(json, "$.app_msg_ext_info.digest");//文章摘要
- string source_url = jsonpath.read(json, "$.app_msg_ext_info.source_url");//閱讀原文的鏈接
- source_url = source_url.replace("", "");
- string cover = jsonpath.read(json, "$.app_msg_ext_info.cover");//封面圖片
- cover = cover.replace("", "");
- /**
- * 存入數據庫
- */
- // system.out.println("頭條標題:"+title);
- // system.out.println("微信id:"+fileid);
- // system.out.println("文章摘要:"+digest);
- // system.out.println("閱讀原文鏈接:"+source_url);
- // system.out.println("封面圖片地址:"+cover);
- post post = new post();
- post.setbiz(biz);
- post.settitle(title);
- post.settitleencode(title_encode);
- post.setfieldid(fileid);
- post.setdigest(digest);
- post.setsourceurl(source_url);
- post.setcover(cover);
- post.setistop(1);//標記一下是頭條內容
- post.setismulti(is_multi);
- post.setdatetime(datetime);
- post.setcontenturl(content_url);
- postmapper.insert(post);
- }
- if(is_multi == 1){//如果是多圖文消息
- list<object> multilists = jsonpath.read(json, "['app_msg_ext_info']['multi_app_msg_item_list']");
- for(object multilist : multilists){
- object multijson = multilist;
- content_url = jsonpath.read(multijson, "['content_url']").tostring().replace("", "").replaceall("amp;", "");//圖文消息鏈接地址
- /**
- * 這里再次根據$content_url判斷一下數據庫中是否重復以免出錯
- */
- contenturlexist = false;
- list<post> posts = postmapper.selectbycontenturl(content_url);
- if(posts != null && posts.size() != 0){
- contenturlexist = true;
- }
- if(!contenturlexist){//'數據庫中不存在相同的$content_url'
- /**
- * 在這里將圖文消息鏈接地址插入到采集隊列庫中
- * (隊列庫將在后文介紹,主要目的是建立一個批量采集隊列,
- * 另一個程序將根據隊列安排下一個采集的公眾號或者文章內容)
- */
- if(content_url != null && !"".equals(content_url)){
- tmplist tmplistt = new tmplist();
- tmplistt.setcontenturl(content_url);
- tmplistmapper.insertselective(tmplistt);
- }
- string title = jsonpath.read(multijson, "$.title");
- string title_encode = urlencoder.encode(title, "utf-8");
- integer fileid = jsonpath.read(multijson, "$.fileid");
- string digest = jsonpath.read(multijson, "$.digest");
- string source_url = jsonpath.read(multijson, "$.source_url");
- source_url = source_url.replace("", "");
- string cover = jsonpath.read(multijson, "$.cover");
- cover = cover.replace("", "");
- // system.out.println("標題:"+title);
- // system.out.println("微信id:"+fileid);
- // system.out.println("文章摘要:"+digest);
- // system.out.println("閱讀原文鏈接:"+source_url);
- // system.out.println("封面圖片地址:"+cover);
- post post = new post();
- post.setbiz(biz);
- post.settitle(title);
- post.settitleencode(title_encode);
- post.setfieldid(fileid);
- post.setdigest(digest);
- post.setsourceurl(source_url);
- post.setcover(cover);
- post.setistop(0);//標記一下不是頭條內容
- post.setismulti(is_multi);
- post.setdatetime(datetime);
- post.setcontenturl(content_url);
- postmapper.insert(post);
- }
- }
- }
- }
- }
- }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public string getwxpost() { // todo auto-generated method stub /** * 當前頁面為公眾號文章頁面時,讀取這個程序 * 首先刪除采集隊列表中load=1的行 * 然后從隊列表中按照“order by id asc”選擇多行(注意這一行和上面的程序不一樣) */ tmplistmapper.deletebyload( 1 ); list<tmplist> queues = tmplistmapper.selectmany( 5 ); string url = "" ; if (queues != null && queues.size() != 0 && queues.size() > 1 ){ tmplist queue = queues.get( 0 ); url = queue.getcontenturl(); queue.setisload( 1 ); int result = tmplistmapper.updatebyprimarykey(queue); system.out.println( "update result:" +result); } else { system.out.println( "getpost queues is null?" +queues== null ? null :queues.size()); weixin weixin = weixinmapper.selectone(); string biz = weixin.getbiz(); if ((math.random()> 0.5 ? 1 : 0 ) == 1 ){ url = "http://mp.weixin.qq.com/mp/getmasssendmsg?__biz=" + biz + "#wechat_webview_type=1&wechat_redirect" ; //拼接公眾號歷史消息url地址(第一種頁面形式) } else { url = "https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=" + biz + "#wechat_redirect" ; //拼接公眾號歷史消息url地址(第二種頁面形式) } url = "https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=" + biz + "#wechat_redirect" ; //拼接公眾號歷史消息url地址(第二種頁面形式) //更新剛才提到的公眾號表中的采集時間time字段為當前時間戳。 weixin.setcollect(system.currenttimemillis()); int result = weixinmapper.updatebyprimarykey(weixin); system.out.println( "getpost weixin updateresult:" +result); } int randomtime = new random().nextint( 3 ) + 3 ; string jscode = "<script>settimeout(function(){window.location.href='" +url+ "';}," +randomtime* 1000 + ");</script>" ; return jscode; } |
處理公眾號點贊量和閱讀量的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
public void getmsgext(string str,string url) { // todo auto-generated method stub string biz = "" ; string sn = "" ; map<string,string> querystrs = httpurlparser.parseurl(url); if (querystrs != null ){ biz = querystrs.get( "__biz" ); biz = biz + "==" ; sn = querystrs.get( "sn" ); sn = "%" + sn + "%" ; } /** * $sql = "select * from `文章表` where `biz`='".$biz."' * and `content_url` like '%".$sn."%'" limit 0,1; * 根據biz和sn找到對應的文章 */ post post = postmapper.selectbybizandsn(biz, sn); if (post == null ){ system.out.println( "biz:" +biz); system.out.println( "sn:" +sn); tmplistmapper.deletebyload( 1 ); return ; } // system.out.println("json數據:"+str); integer read_num; integer like_num; try { read_num = jsonpath.read(str, "['appmsgstat']['read_num']" ); //閱讀量 like_num = jsonpath.read(str, "['appmsgstat']['like_num']" ); //點贊量 } catch (exception e){ read_num = 123 ; //閱讀量 like_num = 321 ; //點贊量 system.out.println( "read_num:" +read_num); system.out.println( "like_num:" +like_num); system.out.println(e.getmessage()); } /** * 在這里同樣根據sn在采集隊列表中刪除對應的文章,代表這篇文章可以移出采集隊列了 * $sql = "delete from `隊列表` where `content_url` like '%".$sn."%'" */ tmplistmapper.deletebysn(sn); //然后將閱讀量和點贊量更新到文章表中。 post.setreadnum(read_num); post.setlikenum(like_num); postmapper.updatebyprimarykey(post); } |
處理跳轉向微信注入js的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public string getwxhis() { string url = "" ; // todo auto-generated method stub /** * 當前頁面為公眾號歷史消息時,讀取這個程序 * 在采集隊列表中有一個load字段,當值等于1時代表正在被讀取 * 首先刪除采集隊列表中load=1的行 * 然后從隊列表中任意select一行 */ tmplistmapper.deletebyload( 1 ); tmplist queue = tmplistmapper.selectrandomone(); system.out.println( "queue is null?" +queue); if (queue == null ){ //隊列表為空 /** * 隊列表如果空了,就從存儲公眾號biz的表中取得一個biz, * 這里我在公眾號表中設置了一個采集時間的time字段,按照正序排列之后, * 就得到時間戳最小的一個公眾號記錄,并取得它的biz */ weixin weixin = weixinmapper.selectone(); string biz = weixin.getbiz(); url = "https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=" + biz + "#wechat_redirect" ; //拼接公眾號歷史消息url地址(第二種頁面形式) //更新剛才提到的公眾號表中的采集時間time字段為當前時間戳。 weixin.setcollect(system.currenttimemillis()); int result = weixinmapper.updatebyprimarykey(weixin); system.out.println( "gethis weixin updateresult:" +result); } else { //取得當前這一行的content_url字段 url = queue.getcontenturl(); //將load字段update為1 tmplistmapper.updatebycontenturl(url); } //將下一個將要跳轉的$url變成js腳本,由anyproxy注入到微信頁面中。 //echo "<script>settimeout(function(){window.location.href='".$url."';},2000);</script>"; int randomtime = new random().nextint( 3 ) + 3 ; string jscode = "<script>settimeout(function(){window.location.href='" +url+ "';}," +randomtime* 1000 + ");</script>" ; return jscode; } |
以上就是對處理代理服務器攔截到的數據進行處理的程序。這里有一個需要注意的問題,程序會對數據庫中的每個收錄的公眾號進行輪循訪問,甚至是已經存儲的文章也會再次訪問,目的是為了一直更新文章的閱讀數和點贊數。如果需要抓取大量的公眾號建議對添加任務隊列的代碼進行修改,添加條件限制,否則公眾號一多 輪循抓取重復數據將十分影響效率。
至此就將微信公眾號的文章鏈接全部爬取到,而且這個鏈接是永久有效而且可以在瀏覽器打開的鏈接,接下來就是寫爬蟲程序從數據庫中拿鏈接爬取文章內容等信息了。
我是用webmagic寫的爬蟲,輕量好用。
- public class spidermodel implements pageprocessor{
- private static postmapper postmapper;
- private static list<post> posts;
- // 抓取網站的相關配置,包括編碼、抓取間隔、重試次數等
- private site site = site.me().setretrytimes(3).setsleeptime(100);
- public site getsite() {
- // todo auto-generated method stub
- return this.site;
- }
- public void process(page page) {
- // todo auto-generated method stub
- post post = posts.remove(0);
- string content = page.gethtml().xpath("//div[@id='js_content']").get();
- //存在和諧文章 此處做判定如果有直接刪除記錄或設置表示位表示文章被和諧
- if(content == null){
- system.out.println("文章已和諧!");
- //postmapper.deletebyprimarykey(post.getid());
- return;
- }
- string contentsnap = content.replaceall("data-src", "src").replaceall("preview.html", "player.html");//快照
- string contenttxt = htmltoword.striphtml(content);//純文本內容
- selectable metacontent = page.gethtml().xpath("//div[@id='meta_content']");
- string pubtime = null;
- string wxname = null;
- string author = null;
- if(metacontent != null){
- pubtime = metacontent.xpath("//em[@id='post-date']").get();
- if(pubtime != null){
- pubtime = htmltoword.striphtml(pubtime);//文章發布時間
- }
- wxname = metacontent.xpath("//a[@id='post-user']").get();
- if(wxname != null){
- wxname = htmltoword.striphtml(wxname);//公眾號名稱
- }
- author = metacontent.xpath("//em[@class='rich_media_meta rich_media_meta_text' and @id!='post-date']").get();
- if(author != null){
- author = htmltoword.striphtml(author);//文章作者
- }
- }
- // system.out.println("發布時間:"+pubtime);
- // system.out.println("公眾號名稱:"+wxname);
- // system.out.println("文章作者:"+author);
- string title = post.gettitle().replaceall(" ", "");//文章標題
- string digest = post.getdigest();//文章摘要
- int likenum = post.getlikenum();//文章點贊數
- int readnum = post.getreadnum();//文章閱讀數
- string contenturl = post.getcontenturl();//文章鏈接
- wechatinfobean wechatbean = new wechatinfobean();
- wechatbean.settitle(title);
- wechatbean.setcontent(contenttxt);//純文本內容
- wechatbean.setsourcecode(contentsnap);//快照
- wechatbean.setlikecount(likenum);
- wechatbean.setviewcount(readnum);
- wechatbean.setabstracttext(digest);//摘要
- wechatbean.seturl(contenturl);
- wechatbean.setpublishtime(pubtime);
- wechatbean.setsitename(wxname);//站點名稱 公眾號名稱
- wechatbean.setauthor(author);
- wechatbean.setmediatype("微信公眾號");//來源媒體類型
- wechatstorage.savewechatinfo(wechatbean);
- //標示文章已經被爬取
- post.setisspider(1);
- postmapper.updatebyprimarykey(post);
- }
- public static void startspider(list<post> inposts,postmapper mypostmapper,string... urls){
- long starttime, endtime;
- starttime = system.currenttimemillis();
- postmapper = mypostmapper;
- posts = inposts;
- httpclientdownloader httpclientdownloader = new httpclientdownloader();
- spidermodel spidermodel = new spidermodel();
- spider myspider = spider.create(spidermodel).addurl(urls);
- myspider.setdownloader(httpclientdownloader);
- try {
- spidermonitor.instance().register(myspider);
- myspider.thread(1).run();
- } catch (jmexception e) {
- e.printstacktrace();
- }
- endtime = system.currenttimemillis();
- system.out.println("爬取時間" + ((endtime - starttime) / 1000) + "秒--");
- }
- }
其它的一些無關邏輯的存儲數據代碼就不貼了,這里我把代理服務器抓取到的數據存在了mysql,把自己的爬蟲程序爬到的數據存儲在了mongodb。
下面是自己爬取到的公眾號號的信息:
原文鏈接:https://www.cnblogs.com/luojiangwen/p/7943696.html