github項(xiàng)目地址
錄制的視頻:點(diǎn)擊前往
基本原理
- AnyProxy是一個(gè)阿里開(kāi)源的HTTP代理服務(wù)器,類似fiddler和charles郑兴,但是提供了二次開(kāi)發(fā)能力,可以編寫js代碼改變http/https請(qǐng)求和響應(yīng)
- 為了爬取一個(gè)微信公眾號(hào)的全部文章贝乎,首先就是獲取全部文章情连,然后一篇一篇去打開(kāi)獲取文章標(biāo)題,作者览效,閱讀數(shù)却舀,點(diǎn)贊數(shù)(這兩個(gè)只能在微信瀏覽器獲取)
- 每個(gè)微信公眾號(hào)都提供
查看歷史消息
的功能锤灿,點(diǎn)擊去打開(kāi)這個(gè)網(wǎng)頁(yè)禁筏,不停下滾,可以查到全部發(fā)布文章衡招。在這一步篱昔,基于anyproxy,修改了這個(gè)網(wǎng)頁(yè)html始腾,注入一段讓頁(yè)面不停往下滾動(dòng)的js腳本州刽,當(dāng)滾到底部,就獲取了全部文章列表浪箭。 本質(zhì)上是中間人攻擊穗椅。 -
獲取完全部文章的內(nèi)容(包括url,標(biāo)題奶栖,發(fā)布時(shí)間等等)后匹表,下一步就是循環(huán)通知微信瀏覽器一個(gè)一個(gè)去打開(kāi)這些文章網(wǎng)頁(yè)。每個(gè)文章網(wǎng)頁(yè)也注入js腳本宣鄙,功能是不停的檢查頁(yè)面的點(diǎn)贊數(shù)和閱讀數(shù)袍镀,檢測(cè)到,就往某服務(wù)器發(fā)冻晤,后臺(tái)每成功收到一個(gè)文章的點(diǎn)贊數(shù)和閱讀數(shù)苇羡,就通知微信瀏覽器打開(kāi)下一個(gè)url。這里我使用了socketio鼻弧,實(shí)現(xiàn)微信瀏覽器和自建的koa服務(wù)器之間的通訊设江。
如圖所示:
獲取文章列表演示
如何運(yùn)行
第一步锦茁,一定要安裝成功anyproxy,這一步請(qǐng)?jiān)敿?xì)閱讀anyproxy的官方教程叉存,寫的很詳細(xì)码俩,要保證能成功代理https,能查看到https的body內(nèi)容歼捏。
npm install
npm start
會(huì)自動(dòng)打開(kāi)一個(gè)result.html握玛,實(shí)時(shí)查看爬取文章的內(nèi)容
點(diǎn)擊一個(gè)微信公眾號(hào),點(diǎn)擊查看歷史消息甫菠,之后歷史頁(yè)面會(huì)不停的滾動(dòng)到底,滾動(dòng)完畢冕屯,就開(kāi)始一篇一篇打開(kāi)文章寂诱,爬取內(nèi)容。
具體過(guò)程
1.第一步安聘,要獲取一個(gè)公眾號(hào)的全部歷史文章痰洒。在已經(jīng)設(shè)置好anyproxy代理的真機(jī)上,查看歷史消息浴韭,這時(shí)微信會(huì)打開(kāi)歷史文章網(wǎng)頁(yè)丘喻。
獲取一個(gè)html文檔:
,
var msgList
就是我們需要的歷史文章數(shù)據(jù)念颈,簡(jiǎn)單正則匹配出來(lái)泉粉,替代非法字符,JSON.parse轉(zhuǎn)成我們需要的格式榴芳。 基于anyproxy嗡靡,我們給這個(gè)html文檔注入一段腳本,目的是讓這個(gè)網(wǎng)頁(yè)不停的往下自己滾動(dòng)窟感,觸發(fā)瀏覽器去獲得更多的文章讨彼。
var scrollKey = setInterval(function () {
window.scrollTo(0,document.body.scrollHeight);
},1000);
當(dāng)網(wǎng)頁(yè)滾到底,再次獲取文章柿祈,這個(gè)時(shí)候哈误,同樣的是get請(qǐng)求,但是返回了Content-Type為application/json
的格式躏嚎,這里同樣的方法蜜自,正則匹配找出并格式化成我們需要的格式
同時(shí)當(dāng)
can_msg_continue
為0時(shí),表示已經(jīng)拉到底卢佣,獲取了全部文章袁辈。
至此,獲得了一個(gè)公眾號(hào)的全部文章珠漂,包括文章標(biāo)題晚缩,作者尾膊,url。但是沒(méi)有閱讀數(shù)和點(diǎn)贊數(shù)荞彼,這需要打開(kāi)具體的文章鏈接冈敛,才能看得到。
- 我們還沒(méi)獲得閱讀數(shù)和點(diǎn)贊數(shù)鸣皂,接下來(lái)就是一步一步讓微信瀏覽器不停地打開(kāi)具體文章抓谴,觸發(fā)微信瀏覽器獲取閱讀數(shù)和點(diǎn)贊數(shù)。這里使用了socket.io寞缝,讓文章頁(yè)面連接自定義的服務(wù)器癌压,服務(wù)器主動(dòng)通知瀏覽器下一個(gè)點(diǎn)開(kāi)的文章鏈接,這樣雙向通訊荆陆,一個(gè)循環(huán)就能獲取具體文章的閱讀數(shù)和點(diǎn)贊滩届。
socket.on('url', function (data) {
window.location = data.url;
});
閱讀數(shù)和點(diǎn)贊可以在瀏覽器端,不停檢查dom元素是否渲染出來(lái)然后收集發(fā)往服務(wù)器被啼,也可以直接anyproxy檢查出來(lái)(這里我采用前一種)帜消。
key = setInterval(function () {
var readNum = $('#readNum3').text().trim();
if (!readNum) return;
var likeNum = $('#likeNum3').text().trim();
var postUser = $('#post-user').text().trim();
var postDate = $('#post-date').text().trim() || $('#publish_time').text().trim();
var activityName = $('#activity-name').text().trim();
var js_share_source = $('#js_share_source').attr('href');
socket.emit('crawler', {
readNum: readNum,
likeNum: likeNum,
postUser: postUser,
postDate: postDate,
activityName: activityName,
js_share_source: js_share_source
});
}, 1000);
實(shí)踐過(guò)程的注意點(diǎn)
原理很簡(jiǎn)單,基于真機(jī)的爬蟲浓体,中間人攻擊泡挺,注入javascript腳本,讓瀏覽器模擬人的操作過(guò)程命浴。
- 禁止網(wǎng)頁(yè)的Content-Security-Policy娄猫。CSP 的實(shí)質(zhì)就是白名單制度,開(kāi)發(fā)者明確告訴客戶端生闲,哪些外部資源可以加載和執(zhí)行稚新,等同于提供白名單。如果不禁用跪腹,注入的javascript將無(wú)法執(zhí)行褂删。這里的做法,簡(jiǎn)單粗暴的刪除http響應(yīng)的任何和csp有關(guān)的頭部冲茸。
// 刪除微信網(wǎng)頁(yè)的安全策略
delete header['Content-Security-Policy'];
delete header['Content-Security-Policy-Report-Only'];
- 禁止微信瀏覽器緩存頁(yè)面內(nèi)容屯阀,同樣要修改響應(yīng)頭的和緩存相關(guān)的內(nèi)容。
header['Expires'] = 0;
header['Cache-Control'] = 'no-cache, no-store, must-revalidate';