这几天闲来无事,想爬取视频,上午爬取b站(很简单),下午爬取头条上的小视频,爬取还是比较麻烦的,我是通过selenium获取的网页源代码,因为requests没有get到,而且selenium直接获取通过ajax加载的信息,但就是非常的慢,而且必须使用有头浏览器进行get链接,我在尝试使用无头浏览器时,发现他不能获取链接的代码,所以直接使用selenium获取源代码,虽然慢,但是好用。
环境:pycharm+selenium+谷歌浏览器驱动+re+execjs(python调用js代码,如果你厉害的话可以将js转化为python代码,不行的话就直接调用js代码)+webstorm+nodejs
1.总体思路: 爬取关注的人的主页,随便进入一个个人界面,首先爬取这个人主界面的所有视频信息,然后再进行具体分析。
2 获取视频主页的url
打开chrome的开发者模式,不停向下滑动网页发现内容是动态加载,毫无疑问是ajax加载,选择xhr选项,
查看请求的内容,正式自己所需要的这些东西。
这个api是,每次变化的值是as,cp,_signature,max_behot_time(as,cp这两个参数可要可无,不影响,这个max_behot_time参数可以设置为0,后续会讲为啥子),我们就开始搞定这个加密,全局搜索寻找_signature参数
https://www.toutiao.com/c/user/article/?page_type=1&user_id=6358222830&max_behot_time=1533612102&count=20&as=A1D5AB37BB3D96E&cp=5B7B4DE9C62E4E1&_signature=D1LeQxAYVC6lfZ7DNAHvJA9S3l
发现加密参数在这个js文件里面,保存这个js文件到本地进行分析,使用webstorm打开js文件寻找_signature参数
显然signature参数是e,e就是调用TAC.sign(一个参数)函数,然后我们去寻找TAC
找到了这么个玩意,头皮发麻,直接调用这个js代码,在我之前的文章就破解过这个头条的,详情看上次的文章。上次爬取的是新闻标题,函数的调用还是有差别的。这次的是一个参数,我们要想知道这个参数的值,就利用fiddler进行本地js的调换,让http请求本地的js代码,然后我们在js本地文件中加上这几句标识。
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=,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) {
return t[15 & i.charCodeAt(0)]
})
}("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 = {}]);
我们想知道这几个值分别是啥,合起来是啥,然后清除浏览器,重新刷新网页,在console中查看结果
function o() {
var t, i = ascp.getHoney(), e = "";
window.console.log(userInfo.id);
window.console.log(c.params.max_behot_time);
window.console.log(userInfo.id + "" + c.params.max_behot_time);
window.console.log("xxx")
e = TAC.sign(userInfo.id + "" + c.params.max_behot_time)
window.console.log('hellow')
window.console.log(e)
return window.TAC && (e = TAC.sign(userInfo.id + "" + c.params.max_behot_time)),
t = _.extend({}, c.params, {
as: i.as,
cp: i.cp,
_signature: e,
})
}
我们先确定是不是这样的,打开network,找到请求产生的网页
6358222830 这是 userInfo.id
0 这是 c.params.max_behot_time
63582228300 这是 userInfo.id + "" + c.params.max_behot_time
8GUlpBAbqylaSmUkO4I1WvBlJb 这个就是_signature参数
_signature和我们请求的一样,这个userId是你查看个人界面的id,max_behot_time就设为0,现在模拟调用js代码
之前的文章有详细的说明,这就可以得到——signature参数,as和cp的简单,可以自行查看我的之前头条爬取文章。
function a() {
global.navigator = {};
global.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36";
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=,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) {
return t[15 & i.charCodeAt(0)]
})
}("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 = {}]);
var data = TAC.sign('6358222830'+'0');
console.log(data)
return data
}
a()
2 分析详细播放页面的网页,寻找连接
随便点入一个页面,
发现了一个可怕的连接,点击
发现正是直接播放的链接,开始请求。我开始的时候使用requests获取网页源代码,这个是动态加载无法获取,可以通过js获取,但是要麻烦,我就直接使用selenium请求这个网页
def getSeleniumHtml(url='http://www.365yg.com/i6580854257686154499/#mid=1574175703010317',way=1):
prefs= {
"profile.managed_default_content_settings.images":1,
"profile.content_settings.plugin_whitelist.adobe-flash-player":1,
"profile.content_settings.exceptions.plugins.*,*.per_resource.adobe-flash-player":1,
}
chromeOpitons.add_experimental_option('prefs', prefs) 这之前的都是设置自动播放flash,因为我觉得只有自动播放这个动态加载的才会出现,我也忘记有没有验证这个对不对,感兴趣的可以自己取消这几句
driver = webdriver.Chrome( chrome_options=chromeOpitons)
driver.get(url)
time.sleep(2) 必须设计等待时间,不然无法加载出那个连接
t=driver.page_source
driver.close()
基本上已经分析完成,下面是详细代码
import re
import execjs
import requests
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
from requests.packages.urllib3.exceptions import InsecureRequestWarning
# 禁用安全请求警告
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from pyquery import PyQuery as pq
from selenium.webdriver.chrome.options import Options
chromeOpitons = Options()
# chromeOpitons.set_headless()
def download(url): #下载视频
ht=getHtml(url)
def changeUrl(url): #将视频进行修改格式,不然不会得到MP4的八个连接
ll=url.split('/')
print(ll)
finad_url='http://www.365yg.com/a'+ll[4]+'/#mid='
print(finad_url)
return finad_url
def getSeleniumHtml(url='http://www.365yg.com/i6580854257686154499/#mid=1574175703010317',way=1): #使用selenium进行请求网页
prefs= {
"profile.managed_default_content_settings.images":1,
"profile.content_settings.plugin_whitelist.adobe-flash-player":1,
"profile.content_settings.exceptions.plugins.*,*.per_resource.adobe-flash-player":1,
}
chromeOpitons.add_experimental_option('prefs', prefs)
driver = webdriver.Chrome( chrome_options=chromeOpitons)
driver.get(url)
time.sleep(2)
t=driver.page_source
driver.close()
if way==1:
getUrlList(t) #这个是获取每个视频的页面连接
else:
parseHtml(t) #这个是获取视频的mp4连接
def getHtml(url): #下载视频时,通过它请求
headers={
'Accept': 'application/json, text/javascript',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Content-Type':' application/x-www-form-urlencoded',
'Host': 'www.toutiao.com',
'Referer': 'https://www.toutiao.com/c/user/6358222830/',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36',
'X-Requested-With':'XMLHttpRequest',
'Cookie': 'csrftoken=a68e0f76c25884df3136e00e6d4422a0; tt_webid=6592057817641289220; uuid="w:532efcf428344539a8c578ec20eb0881"; UM_distinctid=1655b2e1328455-0298d8d65bd909-5e4b2519-144000-1655b2e1329c1b; __tasessionId=2yunymud11534834916847; CNZZDATA1259612802=520931702-1534828445-https%253A%252F%252Fwww.toutiao.com%252F%7C1534833845; Hm_lvt_dde6ba2851f3db0ddc415ce0f895822e=1534833077,1534835549,1534836367,1534836730; Hm_lpvt_dde6ba2851f3db0ddc415ce0f895822e=1534836730',
}
html=requests.get(url,headers,verify=False)
with open("1.mp4",'wb') as f:
f.write(html.content)
print("下载完成")
def parseHtml(txt): #正则表达式寻找播放链接
pattern=re.compile(r'<video.*?src="(.*?)"></video>',re.S)
url=re.findall(pattern,txt)
title=pq(txt).find('body > div > div.bui-box.container.wide > div.bui-left.index-content > div.abstract > h2').text()
print(title,url)
return title,url
def get_cp_as(): #这是获取as参数
f1 = open("t.js", 'r')
js=f1.read()
ctx = execjs.compile(js)
return ctx.call('a')
def get_signature(userId): #这是获取signature参数
f2 = open("tt.js", 'r')
js=f2.read()
ctx = execjs.compile(js)
return ctx.call('a',userId)
def getUrlList(txt): # #这个是获取每个视频的页面连接
pattern=re.compile(r'<pre.*?>(.*?)</pre>',re.S)
content=re.findall(pattern,txt)
content=content[0]
content=content.replace('false','False').replace('true','True')
dic=eval(content)
# print(dic['data'])
list=dic['data']
for i in list:
url=i['display_url']
print(url)
url=changeUrl(url)
getSeleniumHtml(url,2)
if __name__ == '__main__':
dic = get_cp_as();
as1 = dic['as']
cp = dic['cp']
key = get_signature(6358222830)
time.sleep(1)
url='https://www.toutiao.com/c/user/article/?page_type=1&user_id=6358222830&max_behot_time=0&count=20&as={as1}&cp={cp}&_signature={signature}'
finalUrl=url.format(as1=as1,cp=cp,signature=key)
print(finalUrl)
t=getSeleniumHtml(finalUrl)
# download('http://172.31.189.8:8081/IXC38e2f08c354d82e771a81379bdf209c7/26a241d020b15fdd46ad48c4bc28bbf2/5b7bda42/video/m/2201ae932258b9d4bb097fff020a4b6708d115a82f600004eef6849f900/')
# changeUrl('//toutiao.com/group/6588422865471671556/')
# t=getSeleniumHtml('http://www.365yg.com/a6586813826820211207/#mid=',2)
# start=time.time()
# t=getHtml('https://www.toutiao.com/c/user/article/?page_type=1&user_id=64385420255&max_behot_time=0&count=20&as=A1256B77FB2A66D&cp=5B7B6AD6D6DD3E1&_signature=rC4nTRAQ92QGAWfNbEp.dawuJ1')
# end=time.time()
# print("执行时间",end-start)
t.js内容
function a() {
var md5=require('md5-node');
var t = Math.floor((new Date).getTime() / 1e3), e = t.toString(16).toUpperCase(),
i = md5(t).toString().toUpperCase();
console.log(t)
if (8 != e.length) return {as: "479BB4B7254C150", cp: "7E0AC8874BB0985"};
for (var n = i.slice(0, 5), a = i.slice(-5), s = "", o = 0; 5 > o; o++) s += n[o] + e[o];
for (var r = "", c = 0; 5 > c; c++) r += e[c + 3] + a[c];
return {as: "A1" + s + e.slice(-3), cp: e.slice(0, 3) + r + "E1"}
}
tt.js
function a(id) {
global.navigator = {};
global.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36";
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=,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) {
return t[15 & i.charCodeAt(0)]
})
}("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 = {}]);
var data = TAC.sign(id+"0");
console.log(data)
return data
}