当前位置:   article > 正文

python爬取今日头条评论,python3爬今日头条新闻_python爬虫今日头条

python爬虫今日头条

这篇文章主要介绍了python抓取今日头条中的广告,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获,下面让小编带着大家一起了解一下。

前言

任何爬虫工程师在爬取网站数据之前都会对网站进行分析,并且进行逆向(js)破解(加密),所以我们在爬取今日头条的文章和视频数据之前,我们也需要先分析一下今日头条的反爬虫机制以及进行逆向(js)破解(加密)。

QQ群聊

855262907

最新版本

Python3爬取今日头条文章视频数据,完美解决as、cp、_signature的加密方法(2020-6-29版)
Python3爬取今日头条文章视频数据,完美解决_signature的加密方法(2020-10-16版)

分析今日头条

今日头条某用户的链接:https://www.toutiao.com/c/user/3410443345/#mid=3413306633
今日头条某用户链接
我们将对今日头条链接进行详细的分析,以下为分析步骤:

1.分析网站信息是否是静态数据

按F12弹出开发者界面,选择Network区域,你会发现有很多的请求链接,鼠标划到最上面,然后看到有一个跟网站URL一样的链接,点击进去用python画小猫
演示图
演示图
从这里得出今日头条网站的数据并不是静态数据,所以我们要进行下一步分析。

2.分析网站信息是否为AJAX请求的

AJAX请求是网站通过js脚本发起的请求连接,目的就是为了在不刷新网页的情况下就能实时的更新网页内容,从而在感觉上你是觉得他和网页源码拼接在一起。

我们再从众多的链接中进行筛选,由于AJAX的类型是XHR的,所以在开发者模式里面我们选择XHR,就能看到只剩下几个链接了,然后从这几个链接里面找出与网站内容极其相符的链接就可以了。
演示图
这样分析出来了网站的内容主要靠AJAX请求的数据来显示的。

3.分析请求链接

我们知道这个链接后就会要去请求这个链接,从而在里面获取到自己想要的数据,这样我们就获得了数据。

我们来看一下开发者工具中的Network区域的Headers区域,经过两次刷新网页会发现两次请求中的参数是有变化的,以及Request Headers的参数也是有变化的,看图。
第一次请求:
第一次请求
第一次请求
第二次请求:
第二次请求
第二次请求
赖加载请求:

解释(百度百科):在Web应用程序中,系统的瓶颈常在于系统的响应速度。如果系统响应速度过慢,用户就会出现埋怨情绪,系统的价值也因此会大打折扣。因此,提高系统响应速度,是非常重要的。
Web应用程序做的最多就是和后台数据库交互,而查询数据库是种非常耗时的过程。当数据库里记录过多时,查询优化更显得尤为重要。为了解决这种问题,有人提出了缓存的概念。缓存就是将用户频繁使用的数据放在内存中以便快速访问。在用户执行一次查询操作后,查询的记录会放在缓存中。当用户再次查询时,系统会首先从缓存中读取,如果缓存中没有,再查询数据库。缓存技术在一定程度上提升了系统性能,但是当数据量过大时,缓存就不太合适了。因为内存容量有限,把过多的数据放在内存中,会影响电脑性能。而另一种技术,懒加载可以解决这种问题。
赖加载请求
赖加载请求
从上面图片来看,我们就能发现链接的参数和请求的headers的参数是有变化的。
变化参数的作用:

参数作用加密
page_type获取数据的类型为文章还是视频,视频为0,文章为1
user_id获取那个用户的数据,在用户链接中获取user_id:https://www.toutiao.com/c/user/3410443345/#mid=3413306633
max_behot_time连续获取用户的数据,用于赖加载上,第一次值默认为0,后续的值为上一次返回数据中的max_behot_time的值
count单次返回最大的数据量,默认值为20
as每一次请求的链接是否是最近的时间
cp与上同理
_signature用于判断user_id和max_behot_time的值跟链接和headers的参数的值是否相等

需要注意的Request Headers中cookie的参数(解决方法:手动获取这些参数的值):

参数作用变化加密
tt_webid未知根据我的观察是一天变化一次
WEATHER_CITY标记城市不变化是(urlencode)
tt_webid未知同上tt_webid一样
csrftoken用于csrf同上

这样就分析完整个网站了,下面开始实战。

爬取今日头条

知道这些加密参数后就开始对加密参数进行破解

1.破解as和cp

首先我们已经知道了每次的访问参数都在变化,所以我们要先找出这些参数的加密代码,也就是js代码。

搜索js文件中带有ascp_signature字符的文件,然后再分析代码是怎么构成的。

搜索方法:
搜索方法
进入后再按Ctrl+F再次搜索_signature,就可以看到参数的加密方法了。
搜索
这样就能找到这串加密代码了。

  1. function n() {
  2. var e, i = ascp.getHoney(), t = "";
  3. return window.TAC && (t = TAC.sign(userInfo.id + "" + d.params.max_behot_time)),
  4. e = _.extend({}, d.params, {
  5. as: i.as,
  6. cp: i.cp,
  7. _signature: t
  8. })
  9. }

其中i.as和i.cp分别对应as和cp,而i是由ascp.getHoney()这代码返回的,所以我们继续找ascp.getHoney,继续搜索getHoney函数。
搜索

  1. i.getHoney = function() {
  2. var e = Math.floor((new Date).getTime() / 1e3)
  3. , i = e.toString(16).toUpperCase()
  4. , t = md5(e).toString().toUpperCase();
  5. if (8 != i.length)
  6. return {
  7. as: "479BB4B7254C150",
  8. cp: "7E0AC8874BB0985"
  9. };
  10. for (var o = t.slice(0, 5), n = t.slice(-5), a = "", s = 0; 5 > s; s++)
  11. a += o[s] + i[s];
  12. for (var r = "", l = 0; 5 > l; l++)
  13. r += i[l + 3] + n[l];
  14. return {
  15. as: "A1" + a + i.slice(-3),
  16. cp: i.slice(0, 3) + r + "E1"
  17. }
  18. }

根据这串JS代码将他转换为Python代码就可以得出ascp的值了,下面给出Python代码:

  1. import math,time,hashlib
  2. def getHoney():
  3. e = math.floor(int(str(time.time() * 1000).split('.')[0]) / 1e3) #获取13位毫秒数然后除于1e3再向下取整
  4. i = str('%X' % e) #转换e为16进制
  5. m5 = hashlib.md5()
  6. m5.update(str(e).encode('utf-8'))
  7. t = str(m5.hexdigest()).upper() #进行md5加密
  8. if 8 != len(i):
  9. return {'as':'479BB4B7254C150','cp':'7E0AC8874BB0985'}
  10. o = t[0:5]
  11. n = t[-5:]
  12. a = ""
  13. r = ""
  14. for x in range(5): #交换字符串
  15. a += o[x] + i[x]
  16. r += i[x + 3] + n[x]
  17. return {'as':"A1" + a + i[-3:],'cp':i[0:3] + r + "E1"}
  18. if __name__ == '__main__':
  19. print(getHoney())

输出结果

2.破解_signature

根据上面破解as和cp时候的截图,我们可以看见生成_signature的值是t,而生成t的方法是TAC.sign(userInfo.id + "" + d.params.max_behot_time),所以我们要搜索TAC.sign()方法。
搜索
搜索完成之后发现是很大一串混淆加密后的方法实现的TAC,想要破解需要花很多时间去看代码,所以为了方便,我就用execjs直接执行这串代码就可以了。
在这里插入图片描述
js源代码:

  1. Function(function(e) {
  2. 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=,ting(bg)}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:f5:u((e){a=0,r=e;{c=a<r;c&&u(e[a]),c}}(6:y=,u((y8:if(g=,lg,g=,y===c)b+=g;else if(y!==l)y9:c10: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,d13: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)>>>065:h=,y=,[y]=h66:u(e(t[b],,67:y=,d=,u((g=).x===c?r(g.y,y,k):g.apply(d,y68:u(e((g=t[b])<"<"?(b--,f):g+g,,70:u(!1)71:n72:+f73:u(parseInt(f,3675:if(){bcase 74:g=<<16>>16g76:u(k[])77:y=,u([y])78:g=,u(a(v,x-=g+1,g79:g=,u(k["$"+g])81:h=,[f]=h82:u([f])83:h=,k[]=h84:!085:void 086:u(v[x-1])88:h=,y=,h,y89:u({e{r(e.y,arguments,k)}e.y=f,e.x=c,e})90:null91:h93: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(i) {
  3. return e[15 & i.charCodeAt(0)]
  4. })
  5. }("v[x++]=v[--x]t.charCodeAt(b++)-32function return ))++.substrvar .length(),b+=;break;case ;break}".split("")))()('gr$Daten Иb/s!l y͒yĹg,(lfi~ah`{mv,-n|jqewVxp{rvmmx,&effkx[!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$jl 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"jl 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 ', [TAC = {}]);

不加userAgent:

  1. function tac(userid) {
  2. Function(function (e) {
  3. 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=,ting(bg)}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:f5:u((e){a=0,r=e;{c=a<r;c&&u(e[a]),c}}(6:y=,u((y8:if(g=,lg,g=,y===c)b+=g;else if(y!==l)y9:c10: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,d13: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)>>>065:h=,y=,[y]=h66:u(e(t[b],,67:y=,d=,u((g=).x===c?r(g.y,y,k):g.apply(d,y68:u(e((g=t[b])<"<"?(b--,f):g+g,,70:u(!1)71:n72:+f73:u(parseInt(f,3675:if(){bcase 74:g=<<16>>16g76:u(k[])77:y=,u([y])78:g=,u(a(v,x-=g+1,g79:g=,u(k["$"+g])81:h=,[f]=h82:u([f])83:h=,k[]=h84:!085:void 086:u(v[x-1])88:h=,y=,h,y89:u({e{r(e.y,arguments,k)}e.y=f,e.x=c,e})90:null91:h93: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 (i) {
  4. return e[15 & i.charCodeAt(0)]
  5. })
  6. }("v[x++]=v[--x]t.charCodeAt(b++)-32function return ))++.substrvar .length(),b+=;break;case ;break}".split("")))()('gr$Daten Иb/s!l y͒yĹg,(lfi~ah`{mv,-n|jqewVxp{rvmmx,&effkx[!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$jl 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"jl 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 ', [TAC = {}]);
  7. var signs = TAC.sign(userid);
  8. return signs;
  9. }

在这里插入图片描述
由于python没有浏览器的userAgent,所以我们得对代码进行一点改变,让他来适应我们python代码。

  1. function tac(userid){
  2. Function(function(e) {
  3. global.navigator={}; //声明一个浏览器得navigator
  4. global.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36"; //给navigator添加一个userAgent,让下面混淆能够正常读取userAgent信息,此userAgent必须和python代码中的headers里的userAgent一模一样。
  5. 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=,ting(bg)}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:f5:u((e){a=0,r=e;{c=a<r;c&&u(e[a]),c}}(6:y=,u((y8:if(g=,lg,g=,y===c)b+=g;else if(y!==l)y9:c10: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,d13: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)>>>065:h=,y=,[y]=h66:u(e(t[b],,67:y=,d=,u((g=).x===c?r(g.y,y,k):g.apply(d,y68:u(e((g=t[b])<"<"?(b--,f):g+g,,70:u(!1)71:n72:+f73:u(parseInt(f,3675:if(){bcase 74:g=<<16>>16g76:u(k[])77:y=,u([y])78:g=,u(a(v,x-=g+1,g79:g=,u(k["$"+g])81:h=,[f]=h82:u([f])83:h=,k[]=h84:!085:void 086:u(v[x-1])88:h=,y=,h,y89:u({e{r(e.y,arguments,k)}e.y=f,e.x=c,e})90:null91:h93: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(i) {
  6. return e[15 & i.charCodeAt(0)]
  7. })
  8. }("v[x++]=v[--x]t.charCodeAt(b++)-32function return ))++.substrvar .length(),b+=;break;case ;break}".split("")))()('gr$Daten Иb/s!l y͒yĹg,(lfi~ah`{mv,-n|jqewVxp{rvmmx,&effkx[!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$jl 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"jl 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 ', [TAC = {}]);
  9. var signs = TAC.sign(userid);
  10. return signs
  11. }

安装execjs命令:pip install PyExecJS
在执行上面这串js代码前请先更换一个js环境,默认execjs使用的是windows系统的JScript,这个js环境不适合这串代码,所以安装一个nodejs环境,然后重启一下pycharm就可以了。
把这串js代码保存为js文件,然后用python来进行执行,下面转换为python代码。

  1. import execjs
  2. def get_signature(user_id,max_behot_time):
  3. with open('signature.js') as f:
  4. jsData = f.read()
  5. execjs.get()
  6. ctx = execjs.compile(jsData).call('tac',str(user_id) + str(max_behot_time)) #复原TAC.sign(userInfo.id + "" + i.param.max_behot_time)
  7. return ctx
  8. if __name__ == '__main__':
  9. user_id = 'https://www.toutiao.com/c/user/3410443345/#mid=3413306633'.split('/')[-2] #获取user_id
  10. print(get_signature(user_id,0))

在这里插入图片描述
这三个加密参数破解完了,那么就大功告成了。

3.获取数据

上面已经介绍完了所有的过程,所以这里就不多说废话了,直接上代码了。

  1. import requests,math,time,hashlib,execjs,json
  2. #获取_signature参数
  3. def get_signature(user_id,max_behot_time):
  4. with open('newsign.js') as f:
  5. jsData = f.read()
  6. execjs.get()
  7. ctx = execjs.compile(jsData).call('tac',str(user_id) + str(max_behot_time)) #复原TAC.sign(userInfo.id + "" + i.param.max_behot_time)
  8. return ctx
  9. #获取as和cp参数
  10. def getHoney():
  11. e = math.floor(int(str(time.time() * 1000).split('.')[0]) / 1e3)
  12. i = str('%X' % e)
  13. m1 = hashlib.md5()
  14. m1.update(str(e).encode('utf-8'))
  15. t = str(m1.hexdigest()).upper()
  16. if 8 != len(i):
  17. return {
  18. 'as':'479BB4B7254C150',
  19. 'cp':'7E0AC8874BB0985'
  20. }
  21. o = t[0:5]
  22. n = t[-5:]
  23. a = ""
  24. r = ""
  25. for x in range(5):
  26. a += o[x] + i[x]
  27. r += i[x + 3] + n[x]
  28. return {'as':"A1" + a + i[-3:],'cp':i[0:3] + r + "E1"}
  29. #获取网站api的json数据
  30. def get_json(user_id,max_behot_time,as_cp,signature,page_type):
  31. #page_type为0是爬取视频内容,为1是爬取文章内容
  32. headers = {
  33. 'authority': 'www.toutiao.com',
  34. 'method': 'GET',
  35. 'path': '/c/user/article/?page_type={page_type}&user_id={user_id}&max_behot_time={max_behot_time}&count=20&as={as}&cp={cp}&_signature={signature}'.format(page_type=page_type,user_id=user_id,max_behot_time=max_behot_time,signature=signature, **as_cp),
  36. 'scheme': 'https',
  37. 'accept': 'application/json, text/java',
  38. 'accept-encoding': 'gzip, deflate, br',
  39. 'accept-language': 'zh-CN,zh;q=0.9',
  40. 'content-type': 'application/x-www-form-urlencoded',
  41. 'cookie': 'tt_webid=6753859293584737805; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6753859293584737805; csrftoken=60ed6e0a8271872b7d0ab6b864bd6611; __tasessionId=eqzqwzzav1572574450156',
  42. 'referer': 'https://www.toutiao.com/c/user/3410443345/',
  43. 'sec-fetch-mode': 'cors',
  44. 'sec-fetch-site': 'same-origin',
  45. 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
  46. 'x-requested-with': 'XMLHttpRequest'
  47. }
  48. url = 'https://www.toutiao.com/c/user/article/?page_type={page_type}&user_id={user_id}&max_behot_time={max_behot_time}&count=20&as={as}&cp={cp}&_signature={signature}'.format(page_type=page_type,user_id=user_id,max_behot_time=max_behot_time,signature=signature, **as_cp)
  49. response = requests.get(url, headers=headers)
  50. resp_json = json.loads(response.text)
  51. return resp_json
  52. if __name__ == '__main__':
  53. url = 'https://www.toutiao.com/c/user/3410443345/#mid=3413306633' # url为用户文章和视频的源
  54. user_id = url.split('/')[-2] # 获取用户user_id,等同于js中的userInfo.id
  55. max_behot_time = 0 # _signature参数生成需要,并且赖加载时也需要此参数
  56. page_type = 1 #0为文章,1为视频
  57. as_cp = getHoney() #获取as和cp值
  58. signature = get_signature(user_id,max_behot_time) #获取_signature值
  59. toutiao_json = get_json(user_id,max_behot_time,as_cp,signature,page_type) #获取今日头条文章数据
  60. print(json.dumps(toutiao_json,ensure_ascii=False,indent=4,sort_keys=True))

演示图

4.获取赖加载数据(完整版)

在这里插入图片描述
上图就是获取到空数据,所以为了解决这个问题加了try来进行控制。
在这里插入图片描述
完整代码:

  1. import requests,math,time,hashlib,execjs,json
  2. #获取_signature参数
  3. def get_signature(user_id,max_behot_time):
  4. with open('newsign.js') as f:
  5. jsData = f.read()
  6. execjs.get()
  7. ctx = execjs.compile(jsData).call('tac',str(user_id) + str(max_behot_time)) #复原TAC.sign(userInfo.id + "" + i.param.max_behot_time)
  8. return ctx
  9. #获取as和cp参数
  10. def getHoney():
  11. e = math.floor(int(str(time.time() * 1000).split('.')[0]) / 1e3)
  12. i = str('%X' % e)
  13. m1 = hashlib.md5()
  14. m1.update(str(e).encode('utf-8'))
  15. t = str(m1.hexdigest()).upper()
  16. if 8 != len(i):
  17. return {
  18. 'as':'479BB4B7254C150',
  19. 'cp':'7E0AC8874BB0985'
  20. }
  21. o = t[0:5]
  22. n = t[-5:]
  23. a = ""
  24. r = ""
  25. for x in range(5):
  26. a += o[x] + i[x]
  27. r += i[x + 3] + n[x]
  28. return {'as':"A1" + a + i[-3:],'cp':i[0:3] + r + "E1"}
  29. #获取网站api的json数据
  30. def get_json(user_id,max_behot_time,as_cp,signature,page_type):
  31. #page_type为0是爬取视频内容,为1是爬取文章内容
  32. headers = {
  33. 'authority': 'www.toutiao.com',
  34. 'method': 'GET',
  35. 'path': '/c/user/article/?page_type={page_type}&user_id={user_id}&max_behot_time={max_behot_time}&count=20&as={as}&cp={cp}&_signature={signature}'.format(page_type=page_type,user_id=user_id,max_behot_time=max_behot_time,signature=signature, **as_cp),
  36. 'scheme': 'https',
  37. 'accept': 'application/json, text/java',
  38. 'accept-encoding': 'gzip, deflate, br',
  39. 'accept-language': 'zh-CN,zh;q=0.9',
  40. 'content-type': 'application/x-www-form-urlencoded',
  41. 'cookie': 'tt_webid=6753859293584737805; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6753859293584737805; csrftoken=60ed6e0a8271872b7d0ab6b864bd6611; __tasessionId=eqzqwzzav1572574450156',
  42. 'referer': 'https://www.toutiao.com/c/user/3410443345/',
  43. 'sec-fetch-mode': 'cors',
  44. 'sec-fetch-site': 'same-origin',
  45. 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
  46. 'x-requested-with': 'XMLHttpRequest'
  47. }
  48. url = 'https://www.toutiao.com/c/user/article/?page_type={page_type}&user_id={user_id}&max_behot_time={max_behot_time}&count=20&as={as}&cp={cp}&_signature={signature}'.format(page_type=page_type,user_id=user_id,max_behot_time=max_behot_time,signature=signature, **as_cp)
  49. response = requests.get(url, headers=headers)
  50. resp_json = json.loads(response.text)
  51. return resp_json
  52. if __name__ == '__main__':
  53. url = 'https://www.toutiao.com/c/user/3410443345/#mid=3413306633' # url为用户文章和视频的源
  54. user_id = url.split('/')[-2] # 获取用户user_id,等同于js中的userInfo.id
  55. max_behot_time = 0 # _signature参数生成需要,并且赖加载时也需要此参数
  56. isDown = True # 一直获取数据
  57. page_type = 1 #0为视频,1为文章
  58. while isDown: #解决赖加载问题
  59. as_cp = getHoney()
  60. signature = get_signature(user_id, max_behot_time)
  61. toutiao_json = get_json(user_id, max_behot_time, as_cp, signature,page_type)
  62. # 由于今日头条的反爬虫机制,有时会获取到空的数据,所以需要用try来控制,一般比例是3:1所以是访问3次有一次获得数据
  63. if page_type:
  64. try:
  65. max_behot_time = toutiao_json['next']['max_behot_time'] #数据为空就没有这个选项从而引发try
  66. print('文章数据:%s' % str(toutiao_json))
  67. has_more = toutiao_json['has_more'] #获取是否还有下一页数据
  68. if not has_more: #用来判断是否没有下一页了
  69. isDown = False
  70. break
  71. except Exception as e:
  72. continue
  73. else:
  74. try:
  75. max_behot_time = toutiao_json['next']['max_behot_time'] # 数据为空就没有这个选项从而引发try
  76. print('视频数据:%s' % str(toutiao_json))
  77. has_more = toutiao_json['has_more'] #获取是否还有下一页数据
  78. if not has_more: #用来判断是否没有下一页了
  79. isDown = False
  80. break
  81. except Exception as e:
  82. continue
github项目地址:https://github.com/wangluozhe/toutiao
zip文件下载:https://codeload.github.com/wangluozhe/toutiao/zip/master

声明:本文仅供学习交流使用,请勿用于商业用途,违者后果自负。

文章知识点与官方知识档案匹配,可进一步学习相关知识
Python入门技能树首页概览422916 人正在系统学习中
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/665144
推荐阅读
相关标签
  

闽ICP备14008679号