爬取今日头条短视频

   这几天闲来无事,想爬取视频,上午爬取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
}

猜你喜欢

转载自blog.csdn.net/weixin_40444270/article/details/81910186