鱼C论坛

 找回密码
 立即注册
查看: 6520|回复: 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

废话不多说,直接上代码
  1. import os
  2. import requests
  3. import base64
  4. from lxml import etree
  5. from Crypto.Cipher import AES
  6. import asyncio
  7. import aiohttp
  8. import shutil
  9. import time


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

  15.         '''查找电影的url'''
  16.         self.searchUrl = 'https://www.baidu.com/s?wd='
  17.         # 解析url
  18.         self.parseUrl = 'https://jx.618g.com/?url='
  19.         self.parseUrl_147 = 'https://jx.147g.cc/?url='
  20.         # 下载地址头
  21.         self.downloadHead = 'https://video.dious.cc'
  22.         # User-Agent
  23.         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'
  24.         # 下载临时文件目录
  25.         self.downDir = os.getcwd() + '\\temp\\'
  26.         self.webList = ['爱奇艺', '腾讯视频', '优酷', 'PP视频', '芒果TV']
  27.         # 电影名称
  28.         self.name = ''
  29.         # 在线观看url
  30.         self.onlineUrl = ''
  31.         # 完整的电影播放网址
  32.         self.finalUrl = ''
  33.         # 搜索结果列表
  34.         self.searchResultList = []
  35.         # 电影所有的url的地址
  36.         self.allListUrl = ''
  37.         # 解密视频所需要的秘钥的URL
  38.         self.keyUrl = ''
  39.         # 解密视频的秘钥
  40.         self.key = ''
  41.         # 电影所有url列表
  42.         self.allList = []
  43.         # AES解密 - 初始化加密器
  44.         self.aes = AES.new(b'0000000000000000', AES.MODE_CBC)
  45.         # 临时文件总数
  46.         self.total = 0
  47.         # 已经下载的文件数量
  48.         self.cur = 0

  49.         self.indexUrl = ''

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

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

  63.         self.name = name
  64.         head = {
  65.             'Host': 'www.baidu.com',
  66.             'User-Agent': self.userAgent
  67.         }

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

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

  81.         if len(self.finalUrl) == 0:
  82.             self.searchResultList = html.xpath('//div//h3//a')
  83.             tempList = []
  84.             isExist = False
  85.             for Item in self.searchResultList:
  86.                 child = Item.getchildren()
  87.                 for each in child:
  88.                     tempList.append(each.tail)
  89.                     tempList.append(each.text)
  90.                 for each in self.webList:
  91.                     if each in str(tempList):
  92.                         isExist = True
  93.                         break
  94.                 if isExist:
  95.                     self.finalUrl = Item.attrib['href']
  96.                     break
  97.         
  98.         if len(self.finalUrl) == 0:
  99.             return False

  100.         return True

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

  105.         head = {
  106.             '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',
  107.             'User-Agent': self.userAgent
  108.         }
  109.         time.sleep(0.2)
  110.         res = requests.get(self.finalUrl, headers=head)
  111.         res.encoding = 'utf-8'
  112.         self.finalUrl = self.parseUrl + res.url
  113.         self.onlineUrl = self.finalUrl

  114.         return True

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

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

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

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

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

  149.         try:
  150.             response = requests.get(self.allListUrl, headers=head)
  151.         except Exception:
  152.             print('{} 没有下载资源...'.format(self.name))
  153.             return False
  154.         
  155.         response.encoding = 'utf-8'
  156.         tempList = response.text.split('\n')
  157.         n = 0
  158.         for line in tempList:
  159.             if line.find('KEY') != -1 and line.find('URI') != -1:
  160.                 self.keyUrl = line[line.find('"') + 1:line.rfind('"')]
  161.                 keyRes = requests.get(self.keyUrl, headers=head)
  162.                 keyRes.encoding = 'utf-8'
  163.                 self.key = keyRes.text
  164.                 self.aes = AES.new(self.key.encode('utf-8'), AES.MODE_CBC)
  165.             elif line.find('http') != -1:
  166.                 self.allList.append({
  167.                     'index': n,
  168.                     'url': line
  169.                 })
  170.                 n += 1
  171.         
  172.         self.total = len(self.allList)
  173.         return True

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

  180.         content = b''
  181.         
  182.         try:
  183.             async with aiohttp.ClientSession() as session:
  184.                 await asyncio.sleep(5)
  185.                 async with session.get(url, headers=head) as response:
  186.                     text = await response.read()
  187.                     await asyncio.sleep(1)
  188.                     
  189.                     content = self.aes.decrypt(text)                                  # 解密

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

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

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

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

  217.         return True

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

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

  243.     task = FilmDownloader()
  244.     task.FindFilmAndDownload(film)
复制代码

评分

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

查看全部评分

小甲鱼最新课程 -> https://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
         
  1. 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上都测试通过的

祝大家使用愉快咯
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-29 19:06:48 | 显示全部楼层
有这个是不是就不要会员了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

是的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-1-29 19:21:09 | 显示全部楼层
早上9点发的帖,晚上下班回家审核通过,大家支持一下吧
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2021-1-30 10:22:51 | 显示全部楼层
测试,成功虽然每个都有会员,但是运行成功的感觉真好
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-30 10:30:34 | 显示全部楼层
膜拜大佬!!!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-30 21:19:47 | 显示全部楼层
这个清晰度如何
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-30 23:00:49 | 显示全部楼层
我运行了没反应是怎么回事?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-30 23:18:26 | 显示全部楼层
然后女朋友不会用,转身包了一个会员
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

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

其实是嫌弃没有弹幕,转身开了个会员
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

有被笑到
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-31 09:57:41 | 显示全部楼层
划重点了  啊     这些代码其实都不重要   重要的是你得有个女朋友
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2021-1-31 11:31:32 | 显示全部楼层
解析网站能生存多长时间不可预测
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

其实我最开始是准备从我看了很多年的一个dao版视频网站上爬的,之前一个版本是在那个网站上搜索视频然后再解密下载。分析了半天结果那个网站是用的视频解析,然后我就直接百度搜,顺便测试了下,这个解析网站能支持解析不少主流视频网址....   而且本来爬虫生存时间就比较短,且行且珍惜吧
小甲鱼最新课程 -> https://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
然后运行批处理文件
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2021-1-31 14:27:32 | 显示全部楼层
点个赞
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-1-31 14:30:03 | 显示全部楼层
免会员的吗
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

是的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-1 11:59:27 | 显示全部楼层
大神,膜拜
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-1 12:45:28 | 显示全部楼层
准备学习Python 像卤煮学习
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-5-8 11:45

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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