第一个参数:min_behot_time
以下为今日第一个参数的头条代码
"refresh" === t ? (i = this.list.length > 0 ? this.list[0].behot_time : 0,
this.url += "min_behot_time=" + i) : (i = this.list.length > 0 ? this.list[this.list.length - 1].behot_time : 0,
this.url += "max_behot_time=" + i);
代码整理后逻辑如下
// t = 'refresh'; // 刷新页面时载第一页 t的值
// t = 'loadmore'; // 下拉加载下一页 t 的值
// this.list = [] // 刷新页面时载第一页 this.list 的值
// this.list = [{...},{...},...] // 下拉加载下一页 this.list 的值,里面有16 个元素
var i = 0;
if (t == 'refresh') {
// this.list = [];
if (this.list.length > 0) {
i = this.list[0].behot_time
} else {
i = 0,this.url += "min_behot_time=" + i
// this.url = /api/pc/feed/?min_behot_time=0
}
}
if (t == 'loadmore') {
if (this.list.length > 0) {
// this.list.length = 16
i = this.list[this.list.length - 1].behot_time;
// /api/pc/feed/?max_behot_time=1548149528
} else {
i = 0,this.url += "max_behot_time=" + i;
}
}
逻辑看起来是取返回的数据中,behot_time 字段的值。 this.list 为所有的新闻数据数组,每加载一页就会往这个数组中push.
所以,加载第一页,min_behot_time=0 ; 加载下一页,max_behot_time = this.list[this.list.length - 1].behot_time; 返回数据中的最后一条,behot_time 的值。
先前猜测这个值是直接取的时间戳,调试验证后才知道真相,实践出真理。
第二个参数:category: all
看这个单词很好猜测,分类的意思,加载下一页时没有变动,所以不用修改,可直接使用。
第三个参数:utm_source: toutiao
看起来像来源,toutiao(头条)? 加载下一页时没有变动,所以不用修改,可直接使用。
第四个参数:widen: 1
加载下一页时没有变动,所以不用修改,可直接使用。
第五个参数:tadrequire: false
在调试期间,有时候为false, 有时候为true. 影响不是很大,先不做处理,直接用true 或者false.
第六、七个参数 as、cp
这个比较特殊
as: A1057CA4A68E6CC
cp: 5C469E465C4CAE1
可以将js直接提取出来,然后运行就能获取到。
第八个参数:_signature:
这个参数看起来有点故事。调试看看。
var n = (0, g.sign)(i + "");
(0, a.default)(this.params, {
as: e.as,
cp: e.cp,
_signature: n
})
由上代码可以看出_signature: n; 而 n 是 使用 i 处理后生成的。g.sign 做了什么生出了 n ???
_signature:
经过调试到这..... _signature的值就来自这些字符串,没有思路了,打算采用其他方式爬虫。并考虑到这些字符串可能来自后端生成,然后在进行加密的,如果今日头条不断的进行更新,就会导致今天也许爬虫成功了,过一段时间就不行了。搜索了下,发现有很多这种情况。
Function(function(t) {
return '�e(e,a,r){�(b[e]||(b[e]=t("x,y","�x "+e+" y"�)(r,a)}�a(e,a,r){�(k[r]||(k[r]=t("x,y","�new x[y]("+Array(r+1).join(",x[�y]")�(1)+")"�)(e,a)}�r(e,a,r){�n,t,s={},b=s.d=r?r.d+1:0;for(s["$"+b]=s,t=0;t<b;t�)s[n="$"+t]=r[n];for(t=0,b=s�=a�;t<b;t�)s[t]=a[t];�c(e,0,s)}�c(t,b,k){�u(e){v[x�]=e}�f�{�g=�,t�ing(b�g)}�l�{try{y=c(t,b,k)}catch(e){h=e,y=l}}for(�h,y,d,g,v=[],x=0;;)switch(g=�){case 1:u(!�)�4:�f��5:u(�(e){�a=0,r=e�;���{�c=a<r;�c&&u(e[a�]),c}}(���6:y=�,u(�(y��8:if(g=�,l��g,g=�,y===c)b+=g;else if(y!==l)�y�9:�c�10:u(s(���11:y=�,u(�+y)�12:for(y=f�,d=[],g=0;g<y�;g�)d[g]=y.charCodeAt(g)^g+y�;u(String.fromCharCode.apply(null,d��13:y=�,h=delete �[y]�14:���59:u((g=�)?(y=x,v.slice(x-=g,y�:[])�61:u(�[�])�62:g=�,k[0]=65599*k[0]+k[1].charCodeAt(g)>>>0�65:h=�,y=�,�[y]=h�66:u(e(t[b�],�,���67:y=�,d=�,u((g=�).x===c?r(g.y,y,k):g.apply(d,y��68:u(e((g=t[b�])<"<"?(b--,f�):g+g,�,���70:u(!1)�71:�n�72:�+f��73:u(parseInt(f�,36��75:if(�){b��case 74:g=�<<16>>16�g�76:u(k[�])�77:y=�,u(�[y])�78:g=�,u(a(v,x-=g+1,g��79:g=�,u(k["$"+g])�81:h=�,�[f�]=h�82:u(�[f�])�83:h=�,k[�]=h�84:�!0�85:�void 0�86:u(v[x-1])�88:h=�,y=�,�h,�y�89:u(��{�e�{�r(e.y,arguments,k)}�e.y=f�,e.x=c,e}�)�90:�null�91:�h�93:h=��0:��;default:u((g<<16>>16)-16)}}�n=this,t=n.Function,s=Object.keys||�(e){�a={},r=0;for(�c in e)a[r�]=c;�a�=r,a},b={},k={};�r'.replace(/[�-�]/g, function(e) {
return t[15 & e.charCodeAt(0)]
})
}("v[x++]=�v[--x]�t.charCodeAt(b++)-32�function �return �))�++�.substr�var �.length�()�,b+=�;break;case �;break}".split("�")))()('gr$Daten Иb/s!l y͒yĹg,(lfi~ah`{mv,-n|jqewVxp{rvmmx,&eff�kx[!cs"l".Pq%widthl"@q&heightl"vr*getContextx$"2d[!cs#l#,*;?|u.|uc{uq$fontl#vr(fillTextx$$龘ฑภ경2<[#c}l#2q*shadowBlurl#1q-shadowOffsetXl#$$limeq+shadowColorl#vr#arcx88802[%c}l#vr&strokex[ c}l"v,)}eOmyoZB]mx[ cs!0s$l$Pb<k7l l!r&lengthb%^l$1+s$j�l s#i$1ek1s$gr#tack4)zgr#tac$! +0o![#cj?o ]!l$b%s"o ]!l"l$b*b^0d#>>>s!0s%yA0s"l"l!r&lengthb<k+l"^l"1+s"j�l s&l&z0l!$ +["cs\'(0l#i\'1ps9wxb&s() &{s)/s(gr&Stringr,fromCharCodes)0s*yWl ._b&s o!])l l Jb<k$.aj;l .Tb<k$.gj/l .^b<k&i"-4j!�+& s+yPo!]+s!l!l Hd>&l!l Bd>&+l!l <d>&+l!l 6d>&+l!l &+ s,y=o!o!]/q"13o!l q"10o!],l 2d>& s.{s-yMo!o!]0q"13o!]*Ld<l 4d#>>>b|s!o!l q"10o!],l!& s/yIo!o!].q"13o!],o!]*Jd<l 6d#>>>b|&o!]+l &+ s0l-l!&l-l!i\'1z141z4b/@d<l"b|&+l-l(l!b^&+l-l&zl\'g,)gk}ejo{�cm,)|yn~Lij~em["cl$b%@d<l&zl\'l $ +["cl$b%b|&+l-l%8d<@b|l!b^&+ q$sign ', [Object.defineProperty(e, "__esModule", {
value: !0
})])
最终解决方案
使用 puppeteer
进行爬虫,高级解决方案,可爬取任何网站,不管这个网站如何加密,只要能访问这个网站,就能爬取。
直接上代码: 爬取今日头条首页新闻,滚动加载5 页数据,并将数据转为json,写入到json文件。
const puppeteer = require('puppeteer');
const fs = require('fs');
let news = [];
(async() => {
var pageN = 1;
var sleep = function(time) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, time);
})
};
const browser = await puppeteer.launch();
// const browser = await browser1.createIncognitoBrowserContext();
const page = await browser.newPage();
page.on('response', res => {
let mathStr = res._url.indexOf('www.toutiao.com/api/pc/feed/');
if (mathStr > -1) {
console.log(res._url);
res.json().then(json => {
let data = json.data;
data.forEach(v => {
news.push(v);
})
})
}
})
await page.goto('https://www.toutiao.com/');
console.log('document loaded success');
await sleep(3000);
await page.evaluate(_ => {
window.scrollBy(0, document.body.scrollHeight);
});
console.log('1 one page loaded success' + new Date());
await sleep(3000);
await page.evaluate(_ => {
window.scrollBy(0, document.body.scrollHeight);
});
console.log('2 one page loaded success' + new Date());
await sleep(3000);
await page.evaluate(_ => {
window.scrollBy(0, document.body.scrollHeight);
});
console.log('3 one page loaded success' + new Date());
await sleep(3000);
await page.evaluate(_ => {
window.scrollBy(0, document.body.scrollHeight);
});
console.log('4 one page loaded success' + new Date());
await sleep(3000);
await page.evaluate(_ => {
window.scrollBy(0, document.body.scrollHeight);
});
console.log('5 one page loaded success' + new Date());
await sleep(3000);
let fileDate = new Date();
let fileName = 'news'+fileDate.getFullYear()+fileDate.getDate();
objToJsonFile(news, fileName);
await browser.close();
console.log(' browser close success');
})();
function objToJsonFile(obj, fileName) {
fs.open(__dirname + '/' + fileName + '.json', 'w', (err, fd) => {
if (err) {
throw err;
}
let jsonStr = JSON.stringify(obj);
fs.write(fd, jsonStr, (err, written) => {
if (err) {
throw err;
}
})
fs.close(fd);
});
}
总结
闲话:这次的爬虫,陆陆续续的调试了很多次,也花了很多时间,都是在业余时间进行开发和调试,时间断断续续就会导致思路也是断断续续的,有时候来刚来了点灵感,但又需要去做其他事。但不断的尝试,尝试,还是算给出了解决方案,虽然不是很完美,但只要坚持下去,总会有结果,嗯嗯。
1 这次的爬虫中,有尝试使用 crawler
和 puppeteer
。
crawler 优点:轻量级,速度快。
crawler 缺点:一次只对单个链接进行爬取,无法模拟浏览器操作。
puppeteer 优点:可模拟浏览器操作,可以爬取单页应用,可直接运行浏览器中js代码,只要浏览器可以的操作,使用puppeteer 都可以进行模拟。
puppeteer 缺点:太笨重,每一次运行都需要加载一次chrome 内核,速度慢,内存占用高。
crawler官方网站
puppeteer官方网站
2 这次爬虫的目的是想实现一个私人定制的今日头条,而不是今日头条按照个人兴趣进行AI推荐的内容。(有段时间有类似上瘾的感觉,每次今日头条都很能抓住我的兴趣,导致每天都会花很多时间去刷今日头条,但这样真的好吗,这样是否会导致每个人的兴趣都固定在同一个圈内,而我要的是各个行业的新闻,兴趣应该是广泛的,我想看的是大家的想法、也许我就是乌合之众吧!!!个人的想法)
这次有尝试爬取当天的新闻,也成功爬取了一天中的新闻,并进行了分析,按照之前的想法去构建一个私人定制的今日头条。但按照思路进行了处理,处理后发现结果并不是想象中结果。
爬取了14页左右的今日头条,并按照个人想法进行过滤(按照评论数进行排序,按照关键词标签排除了娱乐新闻、美食、汽车、视频、家居、美文、育儿、健康,这些都不是我想关注。)。
爬取2019/01/31今日头条内容展示地址:http://www.apago.top/example/spiderjrty/test.html
3 处理后发现结果并不是想象中结果,原因大概是,点击爬取中各条新闻就会发现,今日头条首页中的新闻并不都是按照最新的时间进行排序的,有些还是好几天前的新闻。也就是说首页中展示的新闻,都是由小编
推荐的新闻, 而我想看的只是今天或者昨天的新闻,目的是每天都只关注最近的新闻,并达到如果每天都看新闻的话,你会不漏掉所有的新闻,所有的热点,且不会看到重复的新闻。
4 考虑解决方案,这样爬虫下来,并不能达到我想要的目标和结果。
如果考虑只想关注特定的新闻,例如国内政治新闻,最好还是去爬取人民网什么的,还没去了解,搜索下政治新闻排行网站什么就能得到排行。按照这个思路去爬虫,数据会更接近想要的结果,更纯粹。