前言:

  本人是个新人博主,本篇博客或有点长,还望各位大佬耐心看完,如果是爬虫小白相信您能有所收货,并完成这个案例运用到自己的生活当中,本项目较为综合,适合爬虫小白在学完协程之后作为一个综合练手项目。

  本文涉及的主要爬虫要点如下,如果没有相关知识可以自行先学习一下在看代码,尤其是协程相关内容,建议先补相关的基础再阅读本篇博客作为练手,效果更佳。
    1. python基础
    2. 请求获取数据的方式
    3. xpath提取数据
    4. 正则表达式提取数据
    5. 协程爬取数据

一、准备工作

各种包的安装

  下面是展示本案例所需的各种包的安装,供各位大佬查漏补缺,记得删除#之后注释的内容,不建议大家在pycharm的settings中下载如下的包,并且有条件更建议大家下载anaconda,将自己的环境切换成anaconda的,在anaconda中安装,这样就可以一次吃饱,新建工程后只要环境还是anaconda的,就可以不用再重复装包了。

pip install requests        # 安装requests请求的包
pip install lxml            # 主要用里面的etree模块,用于xpath解析
pip install asyncio         # 使用协程所需要的包
pip install aiohttp         # 协程发送请求的包
pip install aiofiles        # 协程中保存文件的包
pip install pycrytodome     # 用于对数据进行解密(部分电影需要解密)导包的时候导入Crypto# 如果安装出现错误,请先尝试使用国内镜像源,如用清华源下载requests模块就需要使用如下指令
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

导包

所需要的各种包在上面均已告知下载方式,下面是具体的导包

import requests                         # 发送请求的requests模块
import re                                # 使用正则
import asyncio                           # 使用协程
import aiohttp                           # 协程中发送请求
import aiofiles                          # 协程中的文件流
import os                                # 使用一些命令行的功能
from lxml import etree                # xpath解析所需
from urllib.parse import urljoin       # 拼接url,使得url完整
from Crypto.Cipher import AES           # 解密模块,此处因为是AES加密,所以导入AES模块

爬取目标

   具体地址本博客不放(狗头保命),网站简称wbdy。

视频获取过程

  1. 请求到m3u8文件
  2. 加载ts文件
  3. 正常播放视频资源

m3u8文件简介

  m3u8是一种视频播放标准,编码采用UTF-8,其格式如下:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:2.667,
https://hey05.cjkypo.com/20220731/5TKw4kYs/1100kb/hls/F9HUFq1M.ts
#EXTINF:1.667,
https://hey05.cjkypo.com/20220731/5TKw4kYs/1100kb/hls/zHp9UZuq.ts
#EXTINF:1.667,
https://hey05.cjkypo.com/20220731/5TKw4kYs/1100kb/hls/fglNVIm5.ts
#EXTINF:3.333,
https://hey05.cjkypo.com/20220731/5TKw4kYs/1100kb/hls/1dLHM64b.ts
#EXTINF:1.633,
https://hey05.cjkypo.com/20220731/5TKw4kYs/1100kb/hls/cNwwlEiN.ts
.....
#EXT-X-ENDLIST

  由此我们可以看到m3u8其实是将一个电影分解成一个个片段,记录每个片段的数据即上面所示的结尾是xxx.ts的url,记录时长的是#EXTINF后面的参数,至于文件头可以暂时不用管。就普遍理性而言,一个ts正常只占几秒钟,因此我们将所有的ts文件获取到,再进行拼接就可以获得一部完整的电影。

  m3u8格式的好处在于在加载初期就可以直接先加载十几个或者几十个ts,先播放着,后续再边播放边加载后面的ts,是现在视频播放的一种很重要的格式。

网页分析

  1. 拿到视频页面的源代码
  2. 从源代码中找到iframe,提取iframe中的src(如果抓包中可以找到m3u8的url可以直接跳过1,2步,直接请求url获取结果)
  3. 请求到src对应的页面源代码,解析出m3u8文件地址
  4. 获取m3u8中的ts的url并获取到ts文件
  5. 如果数据进行了加密的话需要对数据进行解密
  7. 对ts文件进行合并,还原成mp4文件

网页请求

  这里我们直接将requests请求获取数据的过程写成一个函数,这样之后涉及到请求网页的时候直接调用该函数即可,该函数如下:

# 获取url对应的内容
# param: url——所要请求网页的url
#        pageContent——请求返回的网页内容
def Get_url_content(url):resp = requests.get(url, header)pageContent = resp.textresp.close()return pageContent

二、获取m3u8文件

简陋版:直接请求m3u8文件地址

  我们直接按下f12的开发者工具,来到network选项,打开preview并点击左侧的包中查看请求返回给浏览器的结果,直到找到preview中显示是一个视频的m3u8文件格式的包,如下图所示:

  之后我们在这个包的headers中寻找request的url,请求方式以及各种请求头(主要是UA)


  之后就是将获取的结果保存到本地的txt中,进行后面的ts文件url提取部分
  使用该方法具体获取m3u8文件过程代码如下:

import os.path
import requestsurl = "https://vod1.bdzybf7.com/20220805/Fm13ClIH/1700kb/hls/index.m3u8"headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
}resp = requests.get(url=url, headers=headers, verify=False)filename = url.split("/")[-1]    # m3u8文件名称
with open(filename, mode="w", encoding="utf-8") as f:  # 写入文件f.write(resp.text)resp.close()

  当然这只是你只想爬一部电影的情况下,你可以直接这么搞,甚至为了省事可以直接将preview中的结果直接ctrl+a全选,然后ctrl+cv到本地新建的txt文件中,一步到位。但以普遍理性而言,这种下载方式是不符合批量下载的要求的,而且这样做就太不爬虫了,因此下面从源代码出发,我们看看是否可以寻找一种方法进行批量下载电影。

正式版:从源码出发

  在网页源码中,我们右击查看网页源代码,直接ctrl+f搜索m3u8即可在源代码中找到关于m3u8文件的url。至于为什么需要右击查看网页源代码,是因为在调试工具中的elements选项卡中是实时的html代码,其中有的代码是经过js渲染后的,而我们requests获取的只能是网页最初的源代码,因此我们需要网页源代码中的代码才是requests返回的源码。查找结果如下,可以看到源代码中有且只有一个地方有m3u8文件的url,

  使用xpath解析就可以获取到该url。由于我们发现在源代码中包含url的标签iframe的上一层div标签的class值为唯一的,因此写xpath的时候就可以直接根据该div的class属性值向下找到iframe,进而找到其中的src属性值,提取到url,具体的xpath如下所指示:

"//div[@class='pbbox embed-responsive embed-responsive-16by9']/iframe/@src"

  但是这个url不全,因此我们需要对其进行url补全,即在其最前端添加外层网页的url:https://v.wbdy.tv,这里直接使用urljoin函数就可以解决。之后我们对该url发送请求,获取该url的相应结果,相应的代码如下所示,为了防止一次请求失败,我在这里会重试10次。

def Get_m3u8_url(url):print("开始获取m3u8地址")for i in range(10):try:page_content = Get_url_content(url)page_html = etree.HTML(page_content)m3u8_url = page_html.xpath("//div[@class='pbbox embed-responsive embed-responsive-16by9']/iframe/@src")[0]m3u8_url = urljoin(url, m3u8_url)print("成功获取到m3u8地址")return m3u8_urlexcept:if i < 9:print(f"获取m3u8地址失败,正在进行第{i}次重试")else:print("获取m3u8失败,请稍后重试")return

  获取之后发现返回的结果是一个网页,可以播放电影,因此这还不是我们要找的m3u8文件。

  接着我们打开开发者工具中的Elements选项卡后搜索m3u8,可以发现有一个xxx.m3u8藏在js代码中,我们简单通过request请求该网页url后看到返回结果中是有该m3u8的链接的,如下图所示。

  由于其在js内,不再适合“靓汤”或者xpath了,但其归根结底还是个文本,因此我们确定可以直接使用正则表达式提取该js中隐藏的真m3u8文件。具体的代码如下,为了防止出错,依旧是访问了10次:

# 获取m3u8url的内容
# param: m3u8_url——m3u8的url地址
#        m3u8_first_url——m3u8文件的url地址
def Get_first_m3u8(m3u8_url):print("正在获取第一层m3u8_url")for i in range(10):try:page_content = Get_url_content(m3u8_url)obj = re.compile('url: "(?P<url>.*?)"')      # 正则m3u8_first_url = obj.search(page_content)["url"]print("获取第一层m3u8_url成功")return m3u8_first_urlexcept:if i < 9:print(f"获取第一层m3u8_url失败,正在进行第{i}次重试")else:print("获取第一层m3u8_url失败,请稍后重试")return

  我们对该url发起请求后获得其响应,可以看到其中的m3u8文件的内容如下所示,里面摆放的又是一个m3u8地址,证明我们还需要再次对该m3u8的url发起请求获取数据,并且这个url还是不全的,需要继续使用urljoin函数将其与当前页面的url进行拼接。

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1700000,RESOLUTION=1280x720
/20220805/Fm13ClIH/1700kb/hls/index.m3u8

  请求该url后,我们终于获取到了真正的m3u8文件,具体代码如下所示:

# 下载m3u8文件
# param:m3u8_first_url——上一步从m3u8文件汇总获取的电影m3u8文件的url
def Download_m3u8(m3u8_first_url):print("开始获取第二层m3u8_url")for i in range(10):try:m3u8_url_content = Get_url_content(m3u8_first_url).split()[-1]m3u8_url_content = urljoin(m3u8_first_url, m3u8_url_content)print("获取第二层m3u8成功")breakexcept:if i < 9:print(f"获取第二层m3u8_url失败,正在进行第{i}次重试")else:print("获取第二层m3u8_url失败,请稍后重试")returnprint("开始获取m3u8文件内容")for i in range(10):try:m3u8_content = Get_url_content(m3u8_url_content)except:if i < 9:print(f"获取m3u8文件内容失败,正在进行第{i}次重试")else:print("获取m3u8文件内容失败,请稍后重试")with open("data/ColdJoke2.txt", 'w', encoding='utf-8') as f:     # 此处在当前目录下有个data文件夹,需要创建一下,也可以如捡漏版代码一样用os创建文件夹,此处博主就不再修改f.write(m3u8_content)print("获取m3u8文件内容成功")

  文件结果显示,我们获得了真正的包含电影的m3u8文件,因为其中包括了每个ts的url和每个ts的持续时间,且一共有几千个ts,如果不放心的话,各位可以将其中的时间信息提取出来做个加法,看看是否与电影时长相吻合。m3u8文件的部分如下所示:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:7
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:3.753,
/20220805/Fm13ClIH/1700kb/hls/OV8t58Ar.ts
#EXTINF:3.628,
/20220805/Fm13ClIH/1700kb/hls/0MVXaUOj.ts
#EXTINF:3.753,
/20220805/Fm13ClIH/1700kb/hls/NEMOGtlR.ts
#EXTINF:3.753,
/20220805/Fm13ClIH/1700kb/hls/adjnxylX.ts
#EXTINF:3.753,
/20220805/Fm13ClIH/1700kb/hls/jpl7HLkG.ts
#EXTINF:0.625,
/20220805/Fm13ClIH/1700kb/hls/hFIHWOjx.ts
...
#EXT-X-ENDLIST

  至此我们经历各种折腾,终于是获取到了完整的m3u8文件,将该文件保存下来作为我们下一步工作的输入,即提取所有的ts文件并将其合并成一部完整的电影。

三、获取一系列ts文件

无加密版

  由于我们获取了装有ts文件url的m3u8文件,并将该文件保存到了本地,因此我们可以直接通过读取本地的文件,并将其中的每个ts的url提取出来,通过requests发送请求获取ts文件,由于文件数量多,请求时间长,因此我们可以将所有的ts保存进一个本地的文件夹,方便后续的拼接工作。

  此外,由于文件数量众多,下载过程均为一致的,可以说是非常适合采用协程的方式进行爬取。至于文件顺序可以不用担心,我们可以稍微做个小处理。由于我们发现m3u8文件中是按顺序记录的ts文件的url地址,并且url最后的xxx.ts是没有重复的,因此我们可以考虑在将ts文件保存到本地的时候将xxx.ts作为文件的文件名,这样我们就可以在之后通过m3u8文件将正确的ts文件顺序拼接起来。

  理论成立,来看实际操作。既然是协程,那我们就自然而然想到需要先写一个下载单个ts的函数。方法依旧是请求url,然后获取响应,将响应内容以二进制方式写入文件中。但是注意的是都要讲写法写成协程的方式,并且每次遇到读写都需要加上await。未学习协程的可以考虑先去补下基础,因为不使用协程的话爬取会很慢很慢此外还有一个注意点是需要加上semaphore,这个参数的意义在于限制并发量,不然超过并发量的话程序会直接报错,这是来自操作系统的限制。代码如下所示:

semaphore = asyncio.Semaphore(200)                              # 一次最多200个文件,个人体验是win下最好不要超过400,最多是500# 下载一个ts文件
# param:url——ts文件的url
#       semaphore——最大并发量
async def Download_one_ts(url, semaphore):name = url.split("/")[-1].strip()for i in range(10):try:async with semaphore:                                # 限制并发量async with aiohttp.ClientSession() as session:async with session.get(url) as resp:ts_content = await resp.content.read()async with aiofiles.open("ts解码前/" + name, 'wb') as f:   # 此处也需要现在路径下新建一个“ts解码前”这个文件夹await f.write(ts_content)print(f"{url}获取成功")except:if i < 9:print(f"获取失败{url}")else:print(f"获取失败{url},请稍后重试")

  常规电影中下载最难的地方解决了,下面就是写一个函数将下载ts文件串起来,当然也得是协程。此时我们面临一个最原始的问题,那就是我们如何从m3u8文件中获得url呢?这其实也很简单,python和m3u8文件的格式都给我们安排的明明白白的。那就是利用m3u8文件格式的特殊性,使用starwith函数检测每行是否是以#开头,如果是则跳过,不是则认为其实一个url,并使用urljoin函数将其与当前页面的url进行拼接。最后我们将其传入上面写完的下载ts的函数,并将其打包成任务放进任务列表里,具体实现如下:

semaphore = asyncio.Semaphore(200)# 下载所有的ts
# param:semaphore——最大并行量
async def Download_all_ts(semaphore):tasks = []                    # 任务列表with open("data/ColdJoke2.txt", 'r', encoding='utf-8') as f:for line in f:if line.startswith("#"):continuetasks.append(asyncio.create_task(Download_one_ts(line.strip(), semaphore)))   # 将其放进任务列表await asyncio.wait(tasks)      # 将任务挂起loop = asyncio.get_event_loop()          # 启动协程
loop.run_until_complete(Download_all_ts(semaphore))
loop.close()

加密版

  网吧电影中有的电影的数据是被加密过的,此处纯粹是我在找《十万个冷笑话2》过程中的偶然发现,居然有数据是加密过的。虽然里面也直接放了ts,但是ts文件的内容是加密过的,如果还是按照上面的方法的话,我们最后拼出来的文件是无法播放的。
  我们一起来研究一下,首先前面的过程和上面的无加密版是如出一辙的,直到我们找到存放电影ts文件的m3u8中有所不同。我们一起来看一下加密后的m3u8文件:

  如上图所示,加密后的m3u8相较于未加密的文件多了红框中的一行。我们来简单看一下其中的内容,首先一个头部#EXT-X-KEY就告知我们里面加密了,并且有key,之后的信息解读如下:

METHOD=AES-128      # 说明其加密方式是AES-128
URI=“https://hey07.cjkypo.com/20220517/DezCCUqs/1100kb/hls/key.key”   # 这句话表明加密的秘钥藏在这个url中

  解读出来了我们就目标很明确了,直接先对这个url申请,看看里面藏着什么,大概率是我们加密解密需要用到的秘钥,提取的方法同时是正则,因为这是一个文本而不是html,不适合用“靓汤”或者xpath,获取过程如下,要注意的是需要对获取结果的秘钥重新encode成字节类型,因为加密的时候需要其为字节类型。

# 获取秘钥
# param:key——解密的秘钥
def getKey():obj = re.compile('URI="(?P<key>.*?)"')key_url = ""with open("data/ColdJoke2.txt", "r", encoding='utf-8') as f:key_url = obj.search(f.read())["key"]key_str = Get_url_content(key_url)key = key_str.encode("utf-8")           # 变成字节return key

  现在离解密已经是唾手可得了,我们直接就新建一个AE对象(自己取的),秘钥是上面获取的秘钥,模式为CBC,iv就直接给b"0000000000000000",注意iv和key都得是字节类型,因此iv直接在前面加个b表示转为字节类型。

# 解密ts文件并保存
# param:filename——需要解密的文件名
#       key——秘钥
#       semaphore——最大并行量
async def Decode_ts_one(filename, key, semaphore):print("开始解码" + filename)aes = AES.new(key=key, IV=b"0000000000000000", mode=AES.MODE_CBC)async with semaphore:async with aiofiles.open("ts解码前/" + filename, 'rb') as fRead, aiofiles.open("ts解码后/" + filename, 'wb') as fWrite:content = await fRead.read()content_decode = aes.decrypt(content)await fWrite.write(content_decode)print(filename + "解码完成")

  最后这里就和未加密版的最后是一样的,写一个函数将上面解密一个文件的包装成任务并添加进任务列表。

# 解密并保存所有ts文件
# param:key——秘钥
#       semaphore——最大并行量
async def Decode_ts(semaphore, key):tasks = []key = getKey()with open("data/ColdJoke2.txt", "r", encoding='utf-8') as f:for line in f:if line.startswith("#"):continueelse:name = line.split("/")[-1].strip()tasks.append(asyncio.create_task(Decode_ts_one(name, key, semaphore)))await asyncio.wait(tasks)loop = asyncio.get_event_loop()           # 启动协程
loop.run_until_complete(Decode_ts(semaphore, key))
loop.close()

四、合并ts文件

  在上一大步骤中,我们已经将电影中的所有ts文件下载下来并保存进了文件夹中。因此我们现在的目标明确,任务简单,只需要将这些个ts文件按顺序拼接起来就能得到一部完整的电影啦。在windows中,拼接视频所用的命令如下:

copy /b 输入文件 输出文件

  其中,输入文件之间用“+”连接。于是乎,我们只需要从我们的m3u8文件中,按顺序再读一次文件,并使用“+”将这些文件串联起来,最后再指定输出文件就大功告成了。这里再使用分治的思想,即将一个大任务分解成一个个小任务,这里由于有几千个ts文件,因此我们一条指令拼接100个ts文件,并按顺序给合并文件的名字命名为1.ts,2.ts,3.ts······,这样做是方便最后将这些拼接的ts按顺序合并成完整的视频。

  值得注意的是,并不是ts数目可以被100整除,因此在循环过后会有残余的ts文件留下来,因此在训练后还需要再讲剩下所有的ts文件也合并为一个ts。

  最后一部就是将所有的1.ts,2.ts,3.ts······合并成一部完整的电影,具体的代码如下所示:

# 合并ts文件
# param:name_list——需要合并的文件列表
def merge(name_list):temp = []n = 1for i in range(len(name_list)):name = name_list[i]temp.append(name)if i !=0 and i%100 == 0:cmd_fileName = "+".join(temp)os.system(f"copy /b{cmd_fileName}{n}.ts")temp = []n += 1# 处理能被100整除的剩下的ts文件cmd_fileName = "+".join(temp)os.system(f"copy /b{cmd_fileName}{n}.ts")temp = []n += 1for i in range(n):temp.append(f"{i+1}.ts")cmd_fileName = "+".join(temp)os.system(f"copy /b{cmd_fileName}ColdJoke2.mp4")

  需要注意的是拼接工作需要将路径转换至ts路径下,因此在进行上一步之前需要先将路径切换,在合并完之后还得切回来。至此爬取一部电影就大功告成

# 合并所有ts文件
# param:code_ornot——是否加密,用于选择ts所在的文件夹
def merge_ts(code_ornot):name_list = []with open("data/ColdJoke2.txt", "r", encoding='utf-8') as f:for line in f:if line.startswith("#"):continuename_list.append(line.split("/")[-1].strip())    # 按顺序获取ts文件名称并加进名字列表中nowDir = os.getcwd()       # 获取当前路径,方便后面切回if code_ornot == 1:            # 切换进ts文件夹os.chdir("ts解码后")else:os.chdir("ts解码前")merge(name_list)os.chdir(nowDir)         # 切回项目路径print("下载完成")

五、总结

  本项目综合而言涉及的点较多,较适合学完协程的做一个综合性训练。全部的完整代码可以从下面获取,但是因为涉及到解密的原因,代码有点混乱,各位大佬可自行调整源码,在下就先不肝了,实在肝不动了。

import requests
import re
import asyncio
import aiohttp
import aiofiles
import os
from lxml import etree
from urllib.parse import urljoin
from Crypto.Cipher import AESheader = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}# 获取url对应的内容
def Get_url_content(url):resp = requests.get(url, header)pageContent = resp.textresp.close()return pageContent# 获得m3u8地址
def Get_m3u8_url(url):print("开始获取m3u8地址")for i in range(10):try:page_content = Get_url_content(url)page_html = etree.HTML(page_content)m3u8_url = page_html.xpath("//div[@class='pbbox embed-responsive embed-responsive-16by9']/iframe/@src")[0]m3u8_url = urljoin(url, m3u8_url)print("成功获取到m3u8地址")return m3u8_urlexcept:if i < 9:print(f"获取m3u8地址失败,正在进行第{i}次重试")else:print("获取m3u8失败,请稍后重试")return# 获取第一层m3u8地址
def Get_first_m3u8(m3u8_url):print("正在获取第一层m3u8_url")for i in range(10):try:page_content = Get_url_content(m3u8_url)obj = re.compile('url: "(?P<url>.*?)"')m3u8_first_url = obj.search(page_content)["url"]print("获取第一层m3u8_url成功")return m3u8_first_urlexcept:if i < 9:print(f"获取第一层m3u8_url失败,正在进行第{i}次重试")else:print("获取第一层m3u8_url失败,请稍后重试")return# 获取第二层m3u8地址并下载m3u8文件
def Download_m3u8(m3u8_first_url):print("开始获取第二层m3u8_url")for i in range(10):try:m3u8_url_content = Get_url_content(m3u8_first_url).split()[-1]m3u8_url_content = urljoin(m3u8_first_url, m3u8_url_content)print("获取第二层m3u8成功")breakexcept:if i < 9:print(f"获取第二层m3u8_url失败,正在进行第{i}次重试")else:print("获取第二层m3u8_url失败,请稍后重试")returnprint("开始获取m3u8文件内容")for i in range(10):try:m3u8_content = Get_url_content(m3u8_url_content)except:if i < 9:print(f"获取m3u8文件内容失败,正在进行第{i}次重试")else:print("获取m3u8文件内容失败,请稍后重试")with open("data/ColdJoke2.txt", 'w', encoding='utf-8') as f:f.write(m3u8_content)print("获取m3u8文件内容成功")# 协程下载ts文件
async def Download_one_ts(url, semaphore):name = url.split("/")[-1].strip()for i in range(10):try:async with semaphore:async with aiohttp.ClientSession() as session:async with session.get(url) as resp:ts_content = await resp.content.read()async with aiofiles.open("ts解码前/" + name, 'wb') as f:await f.write(ts_content)print(f"{url}获取成功")except:if i < 9:print(f"获取失败{url}")else:print(f"获取失败{url},请稍后重试")async def Download_all_ts(semaphore):tasks = []with open("data/ColdJoke2.txt", 'r', encoding='utf-8') as f:for line in f:if line.startswith("#"):continuetasks.append(asyncio.create_task(Download_one_ts(line.strip(), semaphore)))await asyncio.wait(tasks)# 合并ts文件
def merge(name_list):temp = []n = 1for i in range(len(name_list)):name = name_list[i]temp.append(name)if i !=0 and i%100 == 0:cmd_fileName = "+".join(temp)os.system(f"copy /b{cmd_fileName}{n}.ts")temp = []n += 1# 处理能被100整除的剩下的ts文件cmd_fileName = "+".join(temp)os.system(f"copy /b{cmd_fileName}{n}.ts")temp = []n += 1for i in range(n):temp.append(f"{i+1}.ts")cmd_fileName = "+".join(temp)os.system(f"copy /b{cmd_fileName}ColdJoke2.mp4")def merge_ts(code_ornot):name_list = []with open("data/ColdJoke2.txt", "r", encoding='utf-8') as f:for line in f:if line.startswith("#"):continuename_list.append(line.split("/")[-1].strip())nowDir = os.getcwd()if code_ornot == 1:os.chdir("ts解码后")else:os.chdir("ts解码前")merge(name_list)os.chdir(nowDir)print("下载完成")def getKey():obj = re.compile('URI="(?P<key>.*?)"')key_url = ""with open("data/ColdJoke2.txt", "r", encoding='utf-8') as f:key_url = obj.search(f.read())["key"]key_str = Get_url_content(key_url)key = key_str.encode("utf-8")           # 变成字节return keyasync def Decode_ts_one(filename, key, semaphore):print("开始解码" + filename)aes = AES.new(key=key, IV=b"0000000000000000", mode=AES.MODE_CBC)async with semaphore:async with aiofiles.open("ts解码前/" + filename, 'rb') as fRead, aiofiles.open("ts解码后/" + filename, 'wb') as fWrite:content = await fRead.read()content_decode = aes.decrypt(content)await fWrite.write(content_decode)print(filename + "解码完成")# ts文件解码
async def Decode_ts(semaphore, key):tasks = []key = getKey()with open("data/ColdJoke2.txt", "r", encoding='utf-8') as f:for line in f:if line.startswith("#"):continueelse:name = line.split("/")[-1].strip()tasks.append(asyncio.create_task(Decode_ts_one(name, key, semaphore)))await asyncio.wait(tasks)def main():semaphore = asyncio.Semaphore(200)# 获取电影的m3u8文件# url = 'https://www.wbdy.tv/play/44801_2_1.html'# m3u8_url = Get_m3u8_url(url)# m3u8_first_url = Get_first_m3u8(m3u8_url)# Download_m3u8(m3u8_first_url)# 下载ts# loop = asyncio.get_event_loop()# loop.run_until_complete(Download_all_ts(semaphore))# loop.close()# 解码ts# key = getKey()# if len(key) != 0:#     loop = asyncio.get_event_loop()#     loop.run_until_complete(Decode_ts(semaphore, key))#     loop.close()#     code_ornot = 1# else:#     code_ornot = 0code_ornot = 0# 合并tsmerge_ts(code_ornot)if __name__ == '__main__':main()

爬虫项目:获取movie相关推荐

  1. post获取重定向的链接 python_欧美音乐网站Python爬虫项目实战

    爬虫项目实战 0x01 目标分析 最近发现一个比较好的欧美音乐下载网站,可以下载大部分高质量欧美音乐.该爬虫项目要实现自动化批量获取用户想要下载的音乐.本文从网站分析.爬虫设计.代码实现三个方面出发, ...

  2. python爬虫下载链接_【Python项目】简单爬虫批量获取资源网站的下载链接

    简单爬虫批量获取资源网站的下载链接 1 由来 自己在收集剧集资源的时候,这些网站的下载链接还要手动一个一个复制到百度云离线下载里,稍微懂了一点编程就不想做这种无意义的劳动了.于是就写了一个这样的一个小 ...

  3. 【python教程入门学习】Python零基础入门爬虫项目

    Python入门爬虫项目 这是我的第一个python项目,分享给大家. 需求 我们目前正在开发一款产品其功能大致是:用户收到短信如:购买了电影票或者火车票机票之类的事件.然后app读取短信,解析短信, ...

  4. python爬虫项目-32个Python爬虫实战项目,满足你的项目慌

    原标题:32个Python爬虫实战项目,满足你的项目慌 爬虫项目名称及简介 一些项目名称涉及企业名词,小编用拼写代替 1.[WechatSogou]- weixin公众号爬虫.基于weixin公众号爬 ...

  5. python爬虫项目-33个Python爬虫项目实战(推荐)

    今天为大家整理了32个Python爬虫项目. 整理的原因是,爬虫入门简单快速,也非常适合新入门的小伙伴培养信心.所有链接指向GitHub,祝大家玩的愉快~O(∩_∩)O WechatSogou [1] ...

  6. python爬虫项目-32个Python爬虫项目让你一次吃到撑

    今天为大家整理了32个Python爬虫项目.整理的原因是,爬虫入门简单快速,也非常适合新入门的小伙伴培养信心.所有链接指向GitHub,祝大家玩的愉快~O(∩_∩)O WechatSogou [1]- ...

  7. python爬虫项目-23个Python爬虫开源项目代码

    今天为大家整理了23个Python爬虫项目.整理的原因是,爬虫入门简单快速,也非常适合新入门的小伙伴培养信心.所有链接指向GitHub,祝大家玩的愉快 1.WechatSogou [1]– 微信公众号 ...

  8. python爬虫新手项目-33个Python爬虫项目实战(推荐)

    今天为大家整理了32个Python爬虫项目. 整理的原因是,爬虫入门简单快速,也非常适合新入门的小伙伴培养信心.所有链接指向GitHub,祝大家玩的愉快~O(∩_∩)O WechatSogou [1] ...

  9. python爬虫教程推荐-33个Python爬虫项目实战(推荐)

    今天为大家整理了32个Python爬虫项目. 整理的原因是,爬虫入门简单快速,也非常适合新入门的小伙伴培养信心.所有链接指向GitHub,祝大家玩的愉快~O(∩_∩)O WechatSogou [1] ...

  10. C++爬虫项目爬取图片

    C++爬虫项目爬取图片, 值得注意的是有些网站的图片爬不来的,有反爬机制,所以一般人爬不下来. 主要代码文件 main.cpp文件里面的代码 #include "CHttp.h" ...

最新文章

  1. SQL获取所有数据库名、表名、储存过程以及参数列表
  2. SpringBoot RabbitMQ 延迟队列代码实现
  3. $sanitize和$sce服务的使用方法
  4. Silverlight Training
  5. ucos iii学习笔记——为什么选择ucos iii
  6. 使用OData服务创建SAP C4C的Lead数据,必须指定Account字段
  7. .NET Core全新的配置管理[共9篇]
  8. 前端学习(3021):vue+element今日头条管理--创建组件和配置路由
  9. Oracle实例之间的心跳机制,为何而心跳-Oracle Heartbeat研究之二
  10. word修订模式怎么彻底关闭_标书制作靠它准没错!那些你不知道的Word技巧大全...
  11. 不显示参数名_非参数检验 之 非参数卡方检验
  12. 使用PYTHON列表生成式过滤数据
  13. 电力电气自动计算excel表格大全【共46份】
  14. 计算机win7设置用户密码,win7怎么设置开机密码 win7设置开机密码步骤盘点【详解】...
  15. python根据日期算星期几_python根据日期返回星期几的方法
  16. 2022-2028年中国航空货运产业发展动态及竞争战略分析报告
  17. Kotlin的协程:挂起函数
  18. Efficientnet笔记:各个框架最适合的图像尺寸
  19. @Cacheable失效
  20. 纯净版ISO镜像下载大全(Windows、Linux、mac)

热门文章

  1. Android 跳过开机界面 直接软件自启动 Android做自己的桌面 替代原生桌面 Android开发自己桌面
  2. 百度网盘视频倍速播放
  3. ecshop 简单修改 可做淘宝客网站
  4. 百度NLP工具LAC初体验:分词,词性标注,命名实体识别
  5. 论文阅读 | NIPS‘20 | Beta Embeddings for Multi-Hop Logical Reasoning in Knowledge Graphs
  6. python 城市身份证代号
  7. vscode php 无法跳转到定义
  8. 我和CSDN的故事(CDSN成立20周年———准程序员响应号召)
  9. 定义一个函数gcd,功能为求a与b的最大公约数
  10. 中图法检索计算机科学方面,信息检索 第一次上机答案lpar;南通大学rpar;