python 爬虫视频网站

一 前言

暑假在家,精神状态一直不是很好,闲来无事,便学起了python。借助这一平台,想谈谈自己在学习中的心得与困惑,也算是记录暑期生活的一种方式。从6月底开始自学python爬虫,到今天断断续续学习了一个多月,期间看网上视频教程,翻各种帖子博客,收获颇多。可能是有点matlab的基础,学习中上手很快,期间爬过视频网站、淘宝、京东、百度图片、QQ好友空间图片、堆糖图片、政府网站获取文本信息等等,也写过小游戏,玩过数据库。后续如果有时间,会将自己写的代码整理好,分享在这里,以供交流学习。


曾在知乎上看到一句话,原话为“师兄说:‘matlab除了不能生孩子,其他什么都能实现’”。额,学了一个多月python后给我的感觉是,python不仅什么都能做,而且能生孩子,而且还能生一堆。。当然这里不是黑matlab,只是说它的适用范围不如python广泛。好了,废话不多说,进入今天的主题python爬虫视频网站。

二 解析网站

所说的视频网站是一部影院,这个网站提供很多vip视频资源,可免费在线观看。该网站的视频源有很多,例如:iqiyi,youku,mgtv,acfun,papapa(一开始这个视频源我也不知道是什么,翻了源码才知道是pptvyun)等等。

分析网站源代码

以电影摔跤吧爸爸为例http://www.yibuyy.com/play/37751-0-0.html打开网站源代码
可见有papapa和mgtv两个资源,后面都跟着字符串。mgtv$$第1集$http://www.mgtv.com/b/314894/4000292.html$mgtv$,其实中间就是芒果TV的播放地址;papapa中间夹的字符串,先不管papapa中间是什么,统一叫url_id吧,后面方便称呼。
图片1

审查元素抓包分析

打开审查元素,进行抓包分析,可在other中找到一个get请求,Request Url是:
http://sapi.goudaitv.com/youkuyun/url.php?xml=http://www.mgtv.com/b/314894/4000292.html&type=mgtv&hd=cq&wap=0&siteuser=123&lg=
此Request Url是可以拼凑出来:“网站名(据经验来看,网站名经常变动,任意一个都行,比如我在程序中使用的是http://api.goudaitv.com/youkuyun/url.php?xml,但是会影响其Preview的网页布局,使用正则时需要改变参数) + url_id &type=视频源”*,后面的参数可以省略。
这里写图片描述
这里写图片描述
然后,查看此get请求的Preview,在video里发现一个https链接:
https://disp.titan.mgtv.com/vod.do?fmt=4&pno=1121&fid=D37570B18EB50AF1EDC92A3A24A07248&file=/c1/2017/07/04_0/D37570B18EB50AF1EDC92A3A24A07248_20170704_1_1_776.mp4,这便就是视频的绝对地址。好了,只要把视频的真实地址找到,就可以放心大胆的在python中用urllib.request.urlretrieve去下载了。问题在于,直接访问这个get请求,会出错,也就是说访问缺少参数,所以将Referer和User-Agent作为头部信息提交。

mgtv视频解析

看到这儿,我们做一个大胆的假设,既然芒果视频在一部影院中的url_id是其本身的播放链接,是不是所有芒果视频的链接(包含vip)都可以这种方法解析?答案是可以的。下面动手来操作,首先进入mgtv随便找到一个视频链接,以异星觉醒为例,以下提供代码:

import requests
import re

#mgtv的异星觉醒链接
mgtv_link = 'http://www.mgtv.com/b/311566/4010142.html'
#拼凑出get请求的url
url = 'http://api.goudaitv.com/youkuyun/url.php?xml=%s&type=mgtv'%(mgtv_link)

#添加头部信息
headers = {'Referer':'http://api.goudaitv.com/youkuyun/ckplayer/ckplayer.swf',
 'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}

#访问get请求
r = requests.get(url, timeout = 30, headers = headers)
print(r.text)

#正则匹配视频的绝对地址
parse = re.findall(r'CDATA(.*?)]', r.text)
print('\n视频的绝对地址是:%s'%parse[1][1:])

程序运行结果:
这里写图片描述
看!视频的绝对地址解析出来了,只要在程序中换掉mgtv的视频链接,就可以想解析啥就解析啥了。

结语

至此,初步解析怎么获得视频的绝对地址。但是没有考虑视频分段的情况,本文编写的程序只提供mgtv和papapa资源的下载,其它资源大多分段,暂时只提供观看。这里吐槽一下,一部影院的papapa资源源代码设计混乱,网页源码存在问题。下面提供python代码,剖析视频爬虫结合GUI展现出来。


最近找到一个可以获得视频绝对地址的解析网站,支持各大视频网站,我用PhantomJS尝试去爬取,结果成功了,下篇准备将其结合tkinter进行编程。

三 程序源代码的实现

整体思路剖析(请结合源代码):

1.设置整体tkinter,然后通过Button(Bone)按钮传递到body函数;
2.body函数执行getlinks,通过entry输入的关键字,从一部影院进行资源搜索;然后通过Button按钮传递至middle函数;
3.middle函数执行getinfo,通过my_entry输入的视频资源编号,进行子资源进行爬取;然后通过Button按钮传递至player函数;
4.player函数通过my_entry(这里使用同一个Entry)输入的子视频的播放\下载源编号,然后根据播放源选择观看、下载,即通过Button按钮传递至choice_down(选择下载剧集)函数或者browser(选择观看剧集)函数;
5.choice_down函数通过my_entry输入的视频剧集,到网站爬取分集下载链接;通过Button按钮传递至start_down(开启下载线程)函数;
6.start_down函数指定一个进程(设置了两个下载进程),通过Button按钮传递至download函数;
7.download函数根据线程,设置下载进度以及相关设置。

python源码:

import requests
import re
from bs4 import BeautifulSoup
import urllib
import webbrowser
from tkinter import *
from tkinter import messagebox
from tkinter.scrolledtext import ScrolledText
import time 
import threading
import os 

#在一部影院中从关键字获取所有资源链接
def getlinks():
    global links 
    global keyword
    keyword = var2.get()
    url = 'http://www.yibuyy.com/search.php?searchword='+keyword 
    r = requests.get(url, timeout = 30, headers = headers)  
    top = r.text.find('<!-- 头部结束 -->')
    bottom = r.text.find('<!-- 尾部开始 -->')
    link_all = r.text[top:bottom]
    req = re.compile(r'a href="(.*?)"[\s\S]*?"sTit">(.*?)<')
    links = re.findall(req, link_all) #找到所有的资源名和资源链接
    return links

#根据gui里选择的资源,访问该资源
def getinfo(links, origin):
    try:
        r = requests.get('http://www.yibuyy.com'+links[origin-1][0], timeout = 30)
        r.encoding = 'utf-8'
    except:
        print('没有找到分页链接或访问超时')
    req = re.compile(r'span id=.*?<a>(.*?)</a>')
    play_root = re.findall(req, r.text)
    soup = BeautifulSoup(r.text, 'lxml')
    group = soup.find_all('div', class_="downlist mb clearfix")
    req_subset = re.compile(r'href="(.*?)".*?title="(.*?)"')
    url_total = []
    for each in group:
        links_subset = re.findall(req_subset, str(each))
        url_total.append(links_subset)
    info = list(zip(play_root, url_total)) #找到相关资源的播放源和分集信息
    return info

#选择下载资源
def choice_down(event = None): 
    global flag
    global download_info
    flag = 1   #流动字幕传入参数,控制字幕break
    if len(info[play_origin-1][1]) == 1: #当视频只有一集时,设置匹配字符
        num = 1
        subset = info[play_origin-1][1][0][1]
        reg = re.compile(r'%s\$\$%s\$(.*?)\$%s'%(info[play_origin-1][0], subset, info[play_origin-1][0]))
    else:#当视频有多集时,设置匹配字符
        num = int(var5.get())
        subset =info[play_origin-1][1][num-1][1]
        reg = re.compile(r'%s.*?%s\$(.*?)\$'%(info[play_origin-1][0], subset))    
    try:
        r = requests.get('http://www.yibuyy.com'+info[play_origin-1][1][num-1][0],timeout = 30)#访问视频分集源代码
        r.encoding = 'utf-8'
        video_url = re.findall(reg, r.text) #找到url_id,用于拼凑最后的get请求链接
        if info[play_origin-1][0] == 'mgtv':
            res = requests.get('http://api.goudaitv.com/youkuyun/url.php?xml=%s&type=%s'%(video_url[0],'mgtv'), timeout = 30, headers = head)#访问get请求,提交头部信息
        else:
            res = requests.get('http://api.goudaitv.com/youkuyun/url.php?xml=%s&type=%s'%(video_url[0],'pptvyun'), timeout = 30,headers = head)#访问get请求,提交头部信息
        parse = re.findall(r'CDATA(.*?)]', res.text) #从res中找到视频的真实地址
        download_info = list(zip([subset],[parse[1]]))#下载集数和视频真实地址
        text.insert(END,'\n%s %s下载链接:\n%s'%(keyword, download_info[0][0],download_info[0][1][1:]))#将信息插入在window中的text里
        print(download_info)
        label = Label(window, font=('楷体',10), fg = 'red',textvariable=var6).grid(row=7, column=0, columnspan=3)
        var6.set('……全力加速,准备下载……')
        var0.set('---点击开始---')
        button = Button(window, text='开始', width=7, height=3, bg='blue', fg='white',font=('微软雅黑',18), command = start_down)#转到下载
        button.focus_force()
        button.bind("<KeyPress-Return>", start_down)
        button.grid(row=4, column=2)
        return download_info
    except:
        var3.set('访问下载链接失败或访问超时,请重试或切换播放\下载源') 

def start_down(event = None):
    global thread
    thread += 1
    th = threading.Thread(target = download)#定义多线程下载视频
    th.start()


def openfile(event = None):
    os.system('explorer '+os.getcwd()+'\download')  #打开文件夹

#设置下载进度1   
def process1(a, b, c):
    per = 100.0 * a * b / c
    if per > 100:
        per = 100
    var6.set('%s  下载进度:%.2f%%  %.2fM/%.2fM   用时: %02d:%02d' % (t1,per, a*b/1024/1024, c/1024/1024, int(time.clock()-s1)//60 ,int(time.clock()-s1)- 60*(int(time.clock()-s1)//60)))
    if per == 100:
        var6.set('……%s 下载完毕,用时: %02d:%02d……'%(t1,int(time.clock()-s1)//60 ,int(time.clock()-s1)- 60*(int(time.clock()-s1)//60)))

#设置下载进度1  
def process2(a, b, c):
    per = 100.0 * a * b / c
    if per > 100:
        per = 100
    var7.set('%s  下载进度:%.2f%%  %.2fM/%.2fM   用时: %02d:%02d' % (t2,per, a*b/1024/1024, c/1024/1024, int(time.clock()-s2)//60 ,int(time.clock()-s2)- 60*(int(time.clock()-s2)//60)))
    if per == 100:
        var7.set('……%s 下载完毕,用时: %02d:%02d……'%(t2,int(time.clock()-s2)//60 ,int(time.clock()-s2)- 60*(int(time.clock()-s2)//60)))

#进行下载     
def download(event = None):
    global s1  #线程1 时间
    global s2  #线程2 时间
    global t1  #线程1 下载信息
    global t2  #线程2 下载信息
    global thread
    time.sleep(1)
    var3.set('<---下载过程中请勿关闭本窗口--->')
    var0.set('---打开下载文件夹---')
    button = Button(window, text='打开', width=7, height=3, bg='blue', fg='white',font=('微软雅黑',18), command = openfile)
    button.focus_force()
    button.bind("<KeyPress-Return>", openfile)
    button.grid(row=4, column=2)

    if not os.path.exists('download'):
        os.mkdir('download')

    if thread == 1:  #如果是一条下载线程
        label = Label(window, font=('楷体',10), fg = 'red',textvariable=eval('var%d'%(thread+6))).grid(row=thread+7, column=0, columnspan=3)
        eval('var%d'%(thread+6)).set('……可继续添加下载……')
        t1 = '%s %s'%(keyword,download_info[0][0])
        s1 = time.clock()
    if thread == 2:  #如果是两条下载线程
        label = Label(window, font=('楷体',10), fg = 'red',textvariable=eval('var%d'%(thread+6))).grid(row=thread+7, column=0, columnspan=3)
        eval('var%d'%(thread+6)).set('╮(╯▽╰)╭只设置了两条下载线程╮(╯▽╰)╭')
        t2 = '%s %s'%(keyword,download_info[0][0])
        s2 = time.clock()
    print(thread)
    urllib.request.urlretrieve(download_info[0][1][1:], "download\%s.mp4" %eval('t%d'%thread), eval('process%d'%thread))
    thread -= 1  #下载完毕,腾出线程

#打开浏览器进行观看
def browser(event = None):
    num = int(var5.get())
    var3.set('已在浏览器中打开,请移步浏览器进行观看')
    webbrowser.open('http://www.yibuyy.com'+info[play_origin-1][1][num-1][0],0) 

#定义button
def button(function):
    global _button
    _button = Button(window, text='确定',width=8, bg='blue', fg='white', font=('微软雅黑', 14), command = function, activebackground= 'red')
    _button.grid(row=6, column=2) 
    _button.bind("<KeyPress-Return>", function)
    return _button

#定义lab
def lab(picture):
    label = Label(window, image = picture).grid(row=4, column=2)

#
def player(event = None):
    global play_origin
    try:
        play_origin = int(var5.get())
        print(play_origin)
        if play_origin in down_num:
            lab(img)
            var0.set('---扫一扫,加关注---')  
            var3.set('<---下载链接为视频真实地址,选中Crtl+c可在浏览器进行观看\下载--->')
            var4.set('请选择下载的集数:')
            my_entry.delete(0, END)
            _button.grid_forget()
            button(choice_down)         
        else:
            if len(info[play_origin-1][1]) == 1:
                var3.set('<---已在浏览器中打开,请移步浏览器进行观看--->')
                webbrowser.open('http://www.yibuyy.com'+info[play_origin-1][1][0][0],0)    
            else:
                my_entry.delete(0, END) 
                var4.set('请输入观看的集数:')
                my_entry.focus_force()
                _button.grid_forget()
                button(browser)     
    except:
        var3.set('<---您输入有误,请重新输入--->')
        my_entry.delete(0, END)

def middle(event = None):
    global info
    global down_num
    try:
        origin = int(var5.get())
        info = getinfo(links, origin)
        var3.set('<---访问papapa资源下载速度较慢,建议选择mgtv--->')
        text.delete(0.0, END)
        down_num = []
        for i in range(len(info)):
            if len(info[i][1]) == 1:
                if info[i][0] == 'papapa' or info[i][0] == 'mgtv':
                    text.insert(END, '%s  %s(下载):%s = %s'%(links[origin-1][1], info[i][0], info[i][1][0][1], str(i+1))+'\n')
                    down_num.append(i+1)
                else:
                    text.insert(END, '%s  %s(观看):%s = %s'%(links[origin-1][1], info[i][0], info[i][1][0][1], str(i+1))+'\n')
            else:
                if info[i][0] == 'papapa' or info[i][0] == 'mgtv':
                    text.insert(END, '%s  %s(下载):共%s集 = %s'%(links[origin-1][1], info[i][0], len(info[i][1]), str(i+1))+'\n')
                    down_num.append(i+1)
                else:
                    text.insert(END, '%s  %s(观看):共%s集 = %s'%(links[origin-1][1], info[i][0], len(info[i][1]), str(i+1))+'\n')
        my_entry.delete(0, END)    
        lab(godie_qr)
        var0.set('---微信公众号---')  
        var4.set('请选择播放\下载源:')
        my_entry.focus_force()
        _button.grid_forget()
        button(player)
    except:
        var3.set('<---您输入有误,请重新输入--->')
        my_entry.delete(0, END)

def body(event = None):
    global my_entry
    global button
    links = getlinks()
    text.delete(0.0, END)
    if len(links) != 0:
        for i in range(len(links)):
            var3.set('<---为您找到以上资源--->')
            var7 = 'author:tianwen'
            lab(godie)
            var0.set('---狗带TV---')
            text.insert(END,links[i][1]+' = '+str(i+1)+'\n')
            label = Label(window, font=('楷体',14), textvariable=var4).grid(row=6, column=0)
            var4.set('请选择资源:')
            my_entry = Entry(window, textvariable=var5, font=('微软雅黑',14),relief = 'ridge')
            my_entry.focus_force()
            my_entry.icursor(10) 
            my_entry.grid(row=6, column=1)
            button(middle)      
    else:
        var3.set('没有找到该资源,请重新输入关键词')
        entry.delete(0, END)

def callback():  
    if messagebox.askokcancel('Quit?', 'Do you really want to quit?'):  
        window.destroy()       


headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}
head = {'Referer':'http://api.goudaitv.com/youkuyun/ckplayer/ckplayer.swf',
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}

if __name__ == '__main__': 
    global thread
    thread = 0
    #Start GUI
    window = Tk()
    window.title('一部影院')
    window.geometry('+450+210')
    window.resizable(False, False)
    window.iconbitmap('material\i.ico')
    photo = PhotoImage(file= 'material\ic.png')
    godie = PhotoImage(file= 'material\godie.png')
    godie_qr = PhotoImage(file= 'material\godie_qr.png')
    img = PhotoImage(file= 'material\ma_qr.png')

    var0 = StringVar()
    var1 = StringVar()
    var3 = StringVar()
    var2 = StringVar()
    var4 = StringVar()
    var5 = StringVar()
    var6 = StringVar()
    var7 = StringVar()
    var8 = StringVar()    

    label = Label(window, font=('楷体',14), textvariable=var1).grid(row=1, column=0)
    var1.set('请输入影视信息:')
    entry = Entry(window, textvariable=var2, font=('微软雅黑',14), relief = 'ridge')
    entry.focus_force()
    entry.grid(row=1, column=1)
    Bone = Button(window, text='搜索', width=8,bg='blue', fg='white', font=('微软雅黑', 14), command = body,activebackground= 'red')
    Bone.grid(row=1, column=2)
    Bone.bind("<KeyPress-Return>", body)
    lab(photo)
    #label = Label(window, image = photo).grid(row=4, column=2)
    label = Label(window, textvariable=var0, font=('微软雅黑',8)).grid(row=3, column=2)
    var0.set('---信息来源---')
    text = ScrolledText(window, font=('楷体',13), fg='blue',height=9,width=47)
    text.grid(row=3, column=0, rowspan=2, columnspan=2)
    text.insert(1.0,'此软件仅用于交流学习,请勿用于商业用途')
    label = Label(window,font=('楷体',9),fg='red',textvariable=var3).grid(row=5, column=0, columnspan=2)
    var3.set('<---程序准备就绪--->')
    window.protocol("WM_DELETE_WINDOW", callback) #询问退出 
    canvas = Canvas(window, height=11, width=530)
    canvas.grid(row=0, column=0, columnspan=3)
    flag = 0
    while True:#流动字幕
        string = '交流学习使用,请勿用于商业用途(Author:Tian Wen)'
        c=canvas.create_text(-150,8,text=string,fill='red', font=('楷体',9))  
        for x in range(0,300):
            canvas.move(c,3,0)  
            window.update()         
            time.sleep(0.05) 
            if flag:
                canvas.move(c,1000,0)
                break
        if flag:
            break
    window.mainloop()




存在的问题:

1.流动字幕和开启的下载进程不能很好的融合,无奈之下,只能将while True的流动字幕break掉;
2.本文设置了Button的回车执行事件,但是好像不符合用户的习惯,下次尝试在Entry中加入回车执行事件;
3.只支持两个下载进程,并且在进度显示上存在问题:两个进程同时下载,第一个线程下载完毕后,继续添加下载,会将原第二个线程下载进度遮住;
4.tkinter布局稍显混乱,也没用根据具体电脑分辨率做相应调整;
5.初学python,程序存在很多bug,还有代码写的实在乱,万望见谅。

程序打包

最后使用pyinstaller将程序打包成exe文件,可在windows上运行,软件截图:这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

软件下载地址:

此软件交流学习使用,请勿用做商业用途。
百度云下载链接:http://pan.baidu.com/s/1o7XlRbc 密码:w7qf

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值