node 爬虫今日头条

第一个参数: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直接提取出来,然后运行就能获取到。

今日头条as,cp 获取测试代码

第八个参数:_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 这次的爬虫中,有尝试使用 crawlerpuppeteer
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 考虑解决方案,这样爬虫下来,并不能达到我想要的目标和结果。
如果考虑只想关注特定的新闻,例如国内政治新闻,最好还是去爬取人民网什么的,还没去了解,搜索下政治新闻排行网站什么就能得到排行。按照这个思路去爬虫,数据会更接近想要的结果,更纯粹。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容