鱼C论坛

 找回密码
 立即注册
查看: 5935|回复: 70

[作品展示] 女朋友说想看电影,所以我用python写了一个电影爬取工具

[复制链接]
发表于 2021-1-29 09:06:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
搜索是百度搜索,找到 爱奇艺、腾讯、优酷、PPTV、芒果 这些网站的链接
解析主要是通过在线解析网址,然后对解析后的网页分析,获得m3u8视频的url,解密下载
其中如果网页请求失败,我就不断的重新发起请求,所以下载最后阶段会有点慢
主要用的是'https://jx.618g.com/?url=',这个解析地址的电影视频都可以下载
但有时候会跳转到'https://jx.147g.cc/?url=',这个地址有robots协议就只能在线观看

Python版本 -> 3.8.7

废话不多说,直接上代码
import os
import requests
import base64
from lxml import etree
from Crypto.Cipher import AES
import asyncio
import aiohttp
import shutil
import time


class FilmDownloader:
    def __init__(self):
        '''初始化'''
        
        # 爱奇艺、腾讯、优酷、PPTV、芒果

        '''查找电影的url'''
        self.searchUrl = 'https://www.baidu.com/s?wd='
        # 解析url
        self.parseUrl = 'https://jx.618g.com/?url='
        self.parseUrl_147 = 'https://jx.147g.cc/?url='
        # 下载地址头
        self.downloadHead = 'https://video.dious.cc'
        # User-Agent
        self.userAgent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36'
        # 下载临时文件目录
        self.downDir = os.getcwd() + '\\temp\\'
        self.webList = ['爱奇艺', '腾讯视频', '优酷', 'PP视频', '芒果TV']
        # 电影名称
        self.name = ''
        # 在线观看url
        self.onlineUrl = ''
        # 完整的电影播放网址
        self.finalUrl = ''
        # 搜索结果列表
        self.searchResultList = []
        # 电影所有的url的地址
        self.allListUrl = ''
        # 解密视频所需要的秘钥的URL
        self.keyUrl = ''
        # 解密视频的秘钥
        self.key = ''
        # 电影所有url列表
        self.allList = []
        # AES解密 - 初始化加密器
        self.aes = AES.new(b'0000000000000000', AES.MODE_CBC)
        # 临时文件总数
        self.total = 0
        # 已经下载的文件数量
        self.cur = 0

        self.indexUrl = ''

        # 下载目录不存在,则创建目录
        if not os.path.exists(self.downDir) or not os.path.isdir(self.downDir):
            os.mkdir(self.downDir)
        # 清空下载目录
        if len(os.listdir(self.downDir)) != 0:
            shutil.rmtree(self.downDir)
            time.sleep(0.2)
            os.mkdir(self.downDir)

    def SearchFilm(self, name: str) -> bool:
        '''电影搜索'''
        if len(name) == 0:
            print('电影名不能为空...')
            return False

        self.name = name
        head = {
            'Host': 'www.baidu.com',
            'User-Agent': self.userAgent
        }

        print('正在搜索 {} 资源...'.format(name))
        time.sleep(0.2)
        res = requests.get(self.searchUrl + name, headers=head)
        res.encoding = 'utf-8'
        html = etree.HTML(res.text)

        condition = '//a[@target="_blank"][@data-visited="off"][@class="dis-line-block c-gap-right dis-no-line c-blocka"]'
        self.searchResultList = html.xpath(condition)
        # 爱奇艺、腾讯、优酷、PPTV、芒果
        for item in self.searchResultList:
            flag = item.text == '爱奇艺' or item.text == '腾讯视频' or item.text == '优酷'  or item.text == 'PP视频' or item.text == '芒果TV'
            if flag:
                self.finalUrl = item.attrib['href']
                break

        if len(self.finalUrl) == 0:
            self.searchResultList = html.xpath('//div//h3//a')
            tempList = []
            isExist = False
            for Item in self.searchResultList:
                child = Item.getchildren()
                for each in child:
                    tempList.append(each.tail)
                    tempList.append(each.text)
                for each in self.webList:
                    if each in str(tempList):
                        isExist = True
                        break
                if isExist:
                    self.finalUrl = Item.attrib['href']
                    break
        
        if len(self.finalUrl) == 0:
            return False

        return True

    def ParseFilmAndGetURL(self) -> bool:
        '''解析视频,并获得下载地址'''
        if len(self.finalUrl) == 0:
            return False

        head = {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
            'User-Agent': self.userAgent
        }
        time.sleep(0.2)
        res = requests.get(self.finalUrl, headers=head)
        res.encoding = 'utf-8'
        self.finalUrl = self.parseUrl + res.url
        self.onlineUrl = self.finalUrl

        return True

    def getIndexUrl(self) -> bool:
        '''获取视频所有片段的url'''
        if len(self.finalUrl) == 0:
            return False

        head = {
            'User-Agent': self.userAgent
        }

        time.sleep(0.2)
        response = requests.get(self.finalUrl, headers=head)
        response.encoding = 'utf-8'
        html = etree.HTML(response.text)
        nodes = html.xpath('//iframe[@id="player"]')
        if nodes is None or len(nodes) == 0:
            return False

        for item in nodes:
            self.indexUrl = item.attrib['src']
            if self.indexUrl.find('m3u8') != -1:
                index = self.indexUrl.find('url=') + 4
                self.indexUrl = self.indexUrl[index:]
            elif self.indexUrl.find('jx.147g.cc') != -1:
                print('由于robots协议,本视频无法下载...')
                return True
            if len(self.indexUrl) != 0:
                # print('url: ' + indexUrl)
                break

        time.sleep(0.2)
        response = requests.get(self.indexUrl, headers=head)
        response.encoding = 'urf-8'
        
        result = response.text.split('\n')
        for line in result:
            if line.find('#') == -1:
                self.allListUrl = self.downloadHead + line
                break

        try:
            response = requests.get(self.allListUrl, headers=head)
        except Exception:
            print('{} 没有下载资源...'.format(self.name))
            return False
        
        response.encoding = 'utf-8'
        tempList = response.text.split('\n')
        n = 0
        for line in tempList:
            if line.find('KEY') != -1 and line.find('URI') != -1:
                self.keyUrl = line[line.find('"') + 1:line.rfind('"')]
                keyRes = requests.get(self.keyUrl, headers=head)
                keyRes.encoding = 'utf-8'
                self.key = keyRes.text
                self.aes = AES.new(self.key.encode('utf-8'), AES.MODE_CBC)
            elif line.find('http') != -1:
                self.allList.append({
                    'index': n,
                    'url': line
                })
                n += 1
        
        self.total = len(self.allList)
        return True

    async def crawler(self, index, url):
        head = {
            'Connection': 'keep-alive',
            'Host': 'ts1.lslkkyj.com',
            'User-Agent': self.userAgent
        }

        content = b''
        
        try:
            async with aiohttp.ClientSession() as session:
                await asyncio.sleep(5)
                async with session.get(url, headers=head) as response:
                    text = await response.read()
                    await asyncio.sleep(1)
                    
                    content = self.aes.decrypt(text)                                  # 解密

                    filename = self.downDir + '{:0>5d}.mp4'.format(index)
                    file = open(filename, 'wb')
                    file.write(content)
                    file.close()
                    self.cur += 1
                    percent = float(self.cur) / float(self.total) * 100
                    print('\r{} 下载中... {:.2f} %   {:d} / {:d}'.format(self.name, percent, self.cur, self.total), end='')
        except Exception:
            # print('{:d} 下载错误;url:{}'.format(index, url))
            await asyncio.sleep(5)
            await self.crawler(index, url)

    def DownloadFilm(self) -> bool:
        '''协程'''
        if len(self.allList) == 0:
            return False

        print('搜索完成!正在下载...')
        loop = asyncio.get_event_loop()
        tasks = [self.crawler(item['index'], item['url']) for item in self.allList]
        loop.run_until_complete(asyncio.gather(*tasks))
        loop.close()

        print('\n下载完成,正在合并文件...')
        command = 'copy /b ' + self.downDir + '* ' + os.getcwd() + '\\' + self.name + '.mp4'
        os.system(command)
        shutil.rmtree(self.downDir)
        time.sleep(0.2)
        os.mkdir(self.downDir)
        print('视频下载完成...')

        return True

    def FindFilmAndDownload(self, name: str) -> bool:
        '''查找视频并下载或在线观看'''
        if not self.SearchFilm(name):
            print('没有搜索到 {} 资源...'.format(name))
            return False
        elif not self.ParseFilmAndGetURL():
            print('{} 资源解析失败...'.format(name))
            return False
        elif not self.getIndexUrl():
            print('获取 {} 下载资源失败...'.format(name))
            return False
        chioce = input('是否在线观看?在线观看则不下载视频!y/n\n无法下载的视频不选择在线观看则退出程序...\n')
        if chioce == 'y':
            os.system('start ' + self.onlineUrl)
            return True
        if len(self.allList) == 0:
            return False
        if not self.DownloadFilm():
            print('{} 下载失败...'.format(name))

        
if __name__ == "__main__":
    
    print('-' * 50)
    # film = '唐伯虎点秋香'
    film = input('请输入电影名...\n')

    task = FilmDownloader()
    task.FindFilmAndDownload(film)

评分

参与人数 3荣誉 +8 鱼币 +8 贡献 +3 收起 理由
小伤口 + 2 + 2 无条件支持楼主!
nahongyan1997 + 1 + 1 感谢楼主无私奉献!
Xiaoyiwhite + 5 + 5 + 3 感谢楼主无私奉献!

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2021-2-6 21:34:57 | 显示全部楼层
思来想去还是把问题集中一下,整理一下问题吧...
目前看来有不少童鞋都出现了问题,我就整理一下
后面的童鞋可以现在这里找找能不能解决,这里没有再开贴问吧

1.这个脚本是通过视频解析网站解析主流视频网站上的视频,然后分析解析之后的视频再进行下载,所以是不需要会员的
但是也正因为是通过解析网站,所以视频画质什么的不能保证(毕竟主流视频网站也没那么容易爬取,我这里都没有用到什么IP代理那种麻烦的东西...)

2.不需要有女朋友也可以用

3.报错:   ModuleNotFoundError: No module named 'xxxxxx'
             这个错误的原因是没有安装xxxxxx模块
             在控制台利用命令pip install xxxxx来安装模块
             (代码开头部分所有import的模块都要安装!)

4.运行没反应
          确保你的电脑上有Python环境,运行脚本最好是弄个批处理或者在vscode、pycharm上运行(双击.py文件有时候真的不大好使)

5.报错:   requests.exceptions.SSLError
          报错出现上面这句话就说明是SSL认证失败,这时候可以关闭SSL认证试一试
          关闭SSL认证的方法是把所有的requests.get函数调用的地方 把verify参数设置为False
        
ressponse = requests.get(url, headers=head, verify=False)

6.已经安装了crypto模块却依然报错:    ModuleNotFoundError: No module named 'Crypto'
          crypto模块本身有些小问题
          找到你crypto模块的路径( {你的Python目录}/Lib/site-packages/下crypto文件夹 )
          把这个文件夹第一个字母改成大写

7.报错:   ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
          可能是因为爬虫速度过快所以被网站暂时禁止连接了
          可以把代码里requests.get前面加上延时(time.sleep({这里参数单位为 秒 }))
          时间设置长一点....0.0

8.代码我自己在Python3.8.7和Python3.9.1上都测试通过的

祝大家使用愉快咯
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-29 19:06:48 | 显示全部楼层
有这个是不是就不要会员了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-1-29 19:14:48 | 显示全部楼层
nahongyan1997 发表于 2021-1-29 19:06
有这个是不是就不要会员了

是的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-1-29 19:21:09 | 显示全部楼层
早上9点发的帖,晚上下班回家审核通过,大家支持一下吧
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2021-1-30 10:22:51 | 显示全部楼层
测试,成功虽然每个都有会员,但是运行成功的感觉真好
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-30 10:30:34 | 显示全部楼层
膜拜大佬!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-30 21:19:47 | 显示全部楼层
这个清晰度如何
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-30 23:00:49 | 显示全部楼层
我运行了没反应是怎么回事?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-30 23:18:26 | 显示全部楼层
然后女朋友不会用,转身包了一个会员
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2021-1-30 23:53:09 | 显示全部楼层
Daniel_Zhang 发表于 2021-1-30 23:18
然后女朋友不会用,转身包了一个会员

其实是嫌弃没有弹幕,转身开了个会员
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-30 23:58:29 | 显示全部楼层
yuxijian2020 发表于 2021-1-30 23:53
其实是嫌弃没有弹幕,转身开了个会员

有被笑到
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-31 09:57:41 | 显示全部楼层
划重点了  啊     这些代码其实都不重要   重要的是你得有个女朋友
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2021-1-31 11:31:32 | 显示全部楼层
解析网站能生存多长时间不可预测
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-1-31 12:50:15 | 显示全部楼层
zhiweng07 发表于 2021-1-31 11:31
解析网站能生存多长时间不可预测

其实我最开始是准备从我看了很多年的一个dao版视频网站上爬的,之前一个版本是在那个网站上搜索视频然后再解密下载。分析了半天结果那个网站是用的视频解析,然后我就直接百度搜,顺便测试了下,这个解析网站能支持解析不少主流视频网址....   而且本来爬虫生存时间就比较短,且行且珍惜吧
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-1-31 12:56:14 | 显示全部楼层
26913 发表于 2021-1-30 23:00
我运行了没反应是怎么回事?

首先,我的Python版本是3.8.7,我家里电脑Python版本3.9.1 都测试通过
然后你要看看我上面import的模块,你是不是全都pip下来了
最后有时候双击.py文件可能没法运行,你可以弄到你的vscode或者pycharm里面运行
或者你可以写一个批处理
比如你把上面的代码保存为 -> FileDownloader.py
然后新建批处理文件(电影搜索下载.bat)
批处理内容 -> start FileDownloader.py
然后运行批处理文件
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2021-1-31 14:27:32 | 显示全部楼层
点个赞
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-31 14:30:03 | 显示全部楼层
免会员的吗
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-1-31 18:30:33 | 显示全部楼层

是的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-1 11:59:27 | 显示全部楼层
大神,膜拜
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-1 12:45:28 | 显示全部楼层
准备学习Python 像卤煮学习
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-1-16 12:54

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表