Python进程与多线程
进程与多线程
线程与进程的关系
进程:资源单位,一个进程中至少要有一个线程
线程:执行单位(好比员工)
启动每一个程序默认都会有一个主线程(开公司至少要有一个员工)
线程
# 单线程
def func():
for i in range(1000):
print("func",i)
if __name__ == '__main__':
func()
for i in range(1000):
print("main",i)
第一套写法(小白)
# 多线程
# 导入线程类
from threading import Thread
def func():
for i in range(1000):
print("func",i)
if __name__ == '__main__':
# 创建线程类,并给线程安排任务
t=Thread(target=func)
# t2=Thread(xxxx)
# 开始执行该线程,多线程状态为可以开始工作状态,具体的执行时间由cpu决定
t.start()
# t2.start()
for i in range(1000):
print("main",i)
from threading import Thread
def func(name):
for i in range(1000):
print(name,i)
if __name__ == '__main__':
# 创建线程类,并给线程安排任务,传参必须是元组
t=Thread(target=func,args=('周杰伦',))
t2=Thread(target=func,args=('王力宏',))
# 开始执行该线程,多线程状态为可以开始工作状态,具体的执行时间由cpu决定
t.start()
t2.start()
for i in range(1000):
print("main",i)
第二套写法(大佬)**
from threaning import Thread
#大佬写法
class MyThread(Thread):
# 固定的->当线程被执行的时候,被执行的就是run()
def run(self):
for i in range(1000):
print("子线程",i)
if __name__ == '__main__':
t=MyThread()
# 下面一行不可取,下面是单线程
# t.run()
# 开启线程
t.start()
for i in range(1000):
print("主线程",i)
# 传参可采用构造函数的方法
进程
from multiprocessing import Process
def func(name):
for i in range(1000):
print(name,i)
if __name__ == '__main__':
p=Process(target=func,args=('周杰伦',))
p2=Process(target=func,args=('周杰伦',))
p.start()
p2.start()
线程池和进程池
线程池
# 线程池:一次性开辟一些线程,我们用户直接给线程池子提交任务,线程任务的调度交给线程池来完成
# 50 10000
# 引入线程池和进程池
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
def fn(name):
for i in range(1000):
print(name,i)
if __name__ == '__main__':
# 创建线程池,由50个线程组成的线程池
with ThreadPoolExecutor(50) as t:
for i in range(1000):
t.submit(fn,name=f"线程{i}")
# 等待线程池中的任务全部执行完毕,才继续执行(守护)
print('123')
进程池
# 线程池:一次性开辟一些线程,我们用户直接给线程池子提交任务,线程任务的调度交给线程池来完成
# 50 10000
# 引入线程池和进程池
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
def fn(name):
for i in range(1000):
print(name,i)
if __name__ == '__main__':
# 创建线程池,由50个线程组成的线程池
with ProcessPoolExecutor(50) as t:
# 将1000个任务分批次运行,每批50个线程
for i in range(1000):
t.submit(fn,name=f"线程{i}")
# 等待线程池中的任务全部执行完毕,才继续执行(守护)
print('123')
线程池和进程池实战
目标:北京新发地
思路:多进程-先抓取一页,随后使用多线程进行多页抓取
table=html.xpath("/html/body/div[2]/div[4]/div[1]/table")[0]

多线程爬取菜价
# 1.如何提取单个页面的数据
# 2.上线程池,多个页面同时抓取
import requests
from lxml import etree
import csv
from concurrent.futures import ThreadPoolExecutor
f=open("data.csv",mode='w',encoding="gbk",newline="")
csvwriter=csv.writer(f)
def download_one_page(url):
# 拿到页面源代码
resp=requests.get(url)
# 根据网页编码进行添加
# resp.encoding="utf-8"
# print(resp.text)
html=etree.HTML(resp.text)
table=html.xpath("/html/body/div[2]/div[4]/div[1]/table")[0]
# print(table)
# 剔除表头
# trs=table.xpath("./tr")[1:]
trs=table.xpath("./tr[position()>1]")
# print(len(trs))
# 拿到每个tr
for tr in trs:
txt=tr.xpath('./td/text()')
# 对数据做简单的处理: \\ / 去掉
# 生成器
txt=(item.replace("\\","").replace("/","") for item in txt)
# 把数据存放在文件中
csvwriter.writerow(txt)
print(url,"提取完毕!")
if __name__ == '__main__':
# 效率极其低下
# for i in range(1,14870):
# download_one_page(f"http://www.xinfadi.com.cn/marketanalysis/0/list/{i}.shtml")
# 创建线程池
with ThreadPoolExecutor(50) as t:
for i in range(1,14870):
# 把下载任务提交给线程池
t.submit(download_one_page,f"http://www.xinfadi.com.cn/marketanalysis/0/list/{i}.shtml")
print("全部下载完毕!")
多进程多线程
# 1.如何提取单个页面的数据
# 2.上线程池,多个页面同时抓取
import requests
from lxml import etree
import csv
from multiprocessing import Process
from concurrent.futures import ThreadPoolExecutor
f0=open("data0.csv",mode='w',encoding="gbk",newline="")
csvwriter0=csv.writer(f0)
f1=open("data1.csv",mode='w',encoding="gbk",new line="")
csvwriter1=csv.writer(f1)
f2=open("data2.csv",mode='w',encoding="gbk",newline="")
csvwriter2=csv.writer(f2)
def download_one_page(url,n):
# 拿到页面源代码
resp=requests.get(url)
# 根据网页编码进行添加
# resp.encoding="utf-8"
# print(resp.text)
html=etree.HTML(resp.text)
table=html.xpath("/html/body/div[2]/div[4]/div[1]/table")[0]
# print(table)
# 剔除表头
# trs=table.xpath("./tr")[1:]
trs=table.xpath("./tr[position()>1]")
# print(len(trs))
# 拿到每个tr
for tr in trs:
txt=tr.xpath('./td/text()')
# 对数据做简单的处理: \\ / 去掉
# 生成器
txt=(item.replace("\\","").replace("/","") for item in txt)
# 把数据存放在文件中
eval("csvwriter"+n).writerow(txt)
print(url,"提取完毕!")
def func0():
with ThreadPoolExecutor(100) as t0:
for i in range(1,4956):
# 把下载任务提交给线程池
t0.submit(download_one_page,f"http://www.xinfadi.com.cn/marketanalysis/0/list/{i}.shtml","0")
print("全部下载完毕!")
def func1():
with ThreadPoolExecutor(100) as t1:
for i in range(4957,9912):
# 把下载任务提交给线程池
t1.submit(download_one_page,f"http://www.xinfadi.com.cn/marketanalysis/0/list/{i}.shtml","1")
print("全部下载完毕!")
def func2():
with ThreadPoolExecutor(100) as t2:
for i in range(9913,14870):
# 把下载任务提交给线程池
t2.submit(download_one_page,f"http://www.xinfadi.com.cn/marketanalysis/0/list/{i}.shtml","2")
print("全部下载完毕!")
if __name__ == '__main__':
# 效率极其低下
# for i in range(1,14870):
# download_one_page(f"http://www.xinfadi.com.cn/marketanalysis/0/list/{i}.shtml")
# 创建线程池
p0=Process(target=func0)
p1=Process(target=func1)
p2=Process(target=func2)
p0.start()
p1.start()
p2.start()
协程
概念
import time
def func():
print("我爱黎明")
# 让当前线程处于阻塞状态,cpu是不为我工作的
time.sleep(3)
print("我真的爱黎明")
if __name__ == '__main__'
func()
类似time.sleep()的语句还有
input()# 等待用户输入,程序也是处于阻塞状态
requests.get()# 请求数据,返回数据前也是处于阻塞状态
# 一般情况下,当程序处于IO状态时,线程会处于阻塞状态
# 协程:当程序遇见了IO操作的时候,可以选择性的切换到其他任务上
# 在微观上是一个任务一个任务的进行切换,切换条件一般是IO操作
# 在宏观上,我们能看到的其实是多个任务一起在执行
# 多任务异步操作
# 上方所讲的一切,都是在单线程的条件下
如何编写
# python编写协程的程序
# 想要运行协程对象,必须借助asyncio库
import asyncio
async def func():
print("你好呀,我叫赛利亚")
if __name__ == '__main__':
g=func()
# 此时的函数时异步协程函数,此时函数执行的得到的是一个协程对象
print(g)
# 协程程序运行需要asyncio模块的支持
asyncio.run(g)
对比
串行
# python编写协程的程序
# 想要运行协程对象,必须借助asyncio库
import asyncio
import time
async def func1():
print("你好,我叫潘金莲")
# 当程序出现同步操作的时候,一异步就中断了
time.sleep(3)
print("你好,我叫潘金莲")
async def func2():
print("你好,我叫王建国")
time.sleep(2)
print("你好,我叫王建国")
async def func3():
print("你好,我叫李雪琴")
time.sleep(4)
print("你好,我叫潘雪琴")
if __name__ == '__main__':
f1=func1()
f2=func2()
f3=func3()
tasks=[
f1,f2,f3
]
t1=time.time()
asyncio.run(asyncio.wait(tasks))
t2=time.time()
print(t2-t1)
结果
E:\A\Programming\py\one\venv\Scripts\python.exe E:/A/Programming/py/one/爬虫/第四章/4_6_协程.py
你好,我叫李雪琴
你好,我叫潘雪琴
你好,我叫潘金莲
你好,我叫潘金莲
你好,我叫王建国
你好,我叫王建国
9.001883029937744
# python编写协程的程序
# 想要运行协程对象,必须借助asyncio库
import asyncio
import time
# 异步
# async def func1():
# print("你好,我叫潘金莲")
# # 当程序出现同步操作的时候,一异步就中断了
# # time.sleep(3)
# # 异步操作的代码,await:挂起,切换到第二任务中
# await asyncio.sleep(3)
# print("你好,我叫潘金莲")
# async def func2():
# print("你好,我叫王建国")
# # time.sleep(2)
# # 异步操作的代码,await:挂起,切换到第二任务中
# await asyncio.sleep(2)
# print("你好,我叫王建国")
# async def func3():
# print("你好,我叫李雪琴")
# # time.sleep(4)
# # 异步操作的代码,await:挂起,切换到第二任务中
# await asyncio.sleep(4)
# print("你好,我叫潘雪琴")
# if __name__ == '__main__':
# f1=func1()
# f2=func2()
# f3=func3()
# tasks=[
# f1,f2,f3
# ]
# t1=time.time()
# asyncio.run(asyncio.wait(tasks))
# t2=time.time()
# print(t2-t1)
# 常用的写法
# 异步
async def func1():
print("你好,我叫潘金莲")
await asyncio.sleep(3)
print("你好,我叫潘金莲")
async def func2():
print("你好,我叫王建国")
await asyncio.sleep(2)
print("你好,我叫王建国")
async def func3():
print("你好,我叫李雪琴")
await asyncio.sleep(4)
print("你好,我叫潘雪琴")
async def main():
# 第一种写法
# f1=func1()
# await f1
# 第二种写法(推荐)
tasks=[
func1(),
func2(),
func3(),
]
await asyncio.wait(tasks)
if __name__ == '__main__':
t1=time.time()
# 一次性启动多个任务
asyncio.run(main())
t2=time.time()
print(t2-t1)
爬虫领域的应用
# python编写协程的程序
# 想要运行协程对象,必须借助asyncio库
import asyncio
import time
# 在爬虫领域的应用
async def download(url):
print("准备开始下载")
await asyncio.sleep(2) # 网络请求
print("下载完成")
async def main():
urls=[
"http://www.baidu.com/",
"http://bilibili.com/",
"http://163.com/"
]
tasks=[]
for url in urls:
d=download(url)
# py3.8以后加上asyncio.create_task()
tasks.append(asyncio.create_task(d))
await asyncio.wait(tasks)
if __name__ == '__main__':
asyncio.run(main())
aiohttp模块使用(异步抓取图片实战)
# requests.get() 同步的代码->异步操作aiohttp
# pip install aiohttp
import asyncio
import aiohttp
urls=[
"http://kr.shanghai-jiuxin.com/file/2020/0807/393f1cb4d5baebab6be3035cb8fa79d5.jpg",
"http://kr.shanghai-jiuxin.com/file/2020/1031/6b72c57a1423c866d2b9dc10d0473f27.jpg",
"http://kr.shanghai-jiuxin.com/file/2020/1031/fdc4cc14a27eddae76d09d7c854b3496.jpg"
]
async def aiodownload(url):
name=url.rsplit("/",1)[1]
# s=aiohttp.ClientSession() <==> requests
# 原先 requests.get() requests.post()
# 现在 s.get() s.post()
# with的作用是,当区域代码运行结束后,自动关闭某个资源(链接),相当于自动在后面加了个session.close
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
# 读取内容
# resp.content.read()=>resp.content
# 读取文本
# resp.text()=>resp.text
# 请求回来了,写入文件
with open(name,mode="wb") as f:
# 读取内容是异步的,需要await挂起
f.write(await resp.content.read())
print(name,"搞定")
# 1.发送请求
# 2.得到图片内容
# 3.保存到文件
async def main():
tasks=[]
for url in urls:
tasks.append(aiodownload(url))
await asyncio.wait(tasks)
if __name__ == '__main__':
asyncio.run(main())
用协程扒光一部小说
西游记_百度小说 (baidu.com)(http://dushu.baidu.com/pc/detail?gid=4306063500)
1.准备工作
准备网址:
http://dushu.baidu.com/pc/detail?gid=4306063500
审核元素,找到目录数据,以及章节地址:
http://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"4306063500"}
找到章节内容地址:
http://dushu.baidu.com/api/pc/getChapterContent?data={"book_id":"4306063500","cid":"4306063500|11348571","need_bookinfo":1}
2.导入需要用到的模块
# 发送同步的请求使用requests
import requests
# 异步:想要运行协程对象,必须借助asyncio库
import asyncio
# 异步网络模块:发送异步的请求使用aiohttp
import aiohttp
# 异步操作文件
import aiofiles
# json格式处理:包括json解码转码
import json
3.获取章节c_id并创建异步任务
async def getCatalog(rul):
resp=requests.get(url)
dic=resp.json()
#item就是对应每个章节的名称和cid
tasks=[]
for item in dic['data']['novel']['items']:
title=item['title']
cid=item['cid']
# 准备异步任务,循环创建异步任务对象,将创建的异步任务对象添加到tasks列表中
tasks.append(aiodownload(cid,b_id,title))
# print(cid,title)
await asyncio.wait(tasks)
4.下载小说
async def aiodownload(cid,b_id,title):
data={
"book_id": b_id,
"cid": f"{b_id}|{cid}",
"need_bookinfo": "1"
}
data=json.dumps(data)
url=f"http://dushu.baidu.com/api/pc/getChapterContent?data={data}"
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
dic=await resp.json()
async with aiofiles.open(f"xiyouji/{title}.txt",mode="w",encoding="utf-8") as f:
# 把小说内容写入
await f.write(dic['data']['novel']['content'])
5.创建主方法,填写准备好的网址
if __name__ == '__main__':
b_id="4306063500"
url='http://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"'+b_id+'"}'
asyncio.run(getCatalog(url))
6.全部代码
# http://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"4306063500"} =>所有章节的内容(名称,cid)
# 章节内部的内容
# http://dushu.baidu.com/api/pc/getChapterContent?data={"book_id":"4306063500","cid":"4306063500|11348571","need_bookinfo":1}
import requests
import asyncio
import aiohttp
import aiofiles
import json
"""
1.同步操作:访问getCatalog拿下所有的章节和cid的名称
2.异步操作:访问getChapterContent下载所有文章内容
"""
async def aiodownload(cid,b_id,title):
data={
"book_id": b_id,
"cid": f"{b_id}|{cid}",
"need_bookinfo": "1"
}
data=json.dumps(data)
url=f"http://dushu.baidu.com/api/pc/getChapterContent?data={data}"
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
dic=await resp.json()
async with aiofiles.open(f"xiyouji/{title}.txt",mode="w",encoding="utf-8") as f:
# 把小说内容写入
await f.write(dic['data']['novel']['content'])
# 访问getCatalog拿下所有的章节和cid的名称
async def getCatalog(rul):
resp=requests.get(url)
dic=resp.json()
#item就是对应每个章节的名称和cid
# 创建异步任务对象使用的列表
tasks=[]
# 准备异步任务,循环创建异步任务对象,将创建的异步任务对象添加到tasks列表中
for item in dic['data']['novel']['items']:
title=item['title']
cid=item['cid']
tasks.append(aiodownload(cid,b_id,title))
# 运行异步对象
await asyncio.wait(tasks)
if __name__ == '__main__':
b_id="4306063500"
url='http://dushu.baidu.com/api/pc/getCatalog?data={"book_id":"'+b_id+'"}'
asyncio.run(getCatalog(url))
综合训练
如何抓取一部视频(简单版)
"""
一般的网站是怎样将视频放入网站的
用户上传->转码(把视频做处理,2K,1080,标清)->切片处理(把单个的文件进行拆分)
例:60分钟的视频拆分为360段,这样每段10秒,使得用户拉动进度条的时候不必等待进度条前方是否加载完毕
这样很大程度的提升了用户的加载速度
============================================================
需要一个文件记录:
1.视频播放的顺序
2.视频存放的路径
M3U+utf8=M3U8 txt json => 文本
想要抓取一个视频:
1.找到M3U8(各种手段)
2.通过M3U8下载到ts文件
3.可以通过各种手段(不仅仅是编程手段还可能会用到pr)把ts文件合并为一个MP4文件
"""
流程:
1.拿到页面源代码
2.从源代码中提取到m3u8的url
3.下载m3u8
4.读取m3u8文件,下载视频
5.合并视频
1-3
"""
流程:
1.拿到页面源代码
2.从源代码中提取到m3u8的url
3.下载m3u8
4.读取m3u8文件,下载视频
5.合并视频
"""
import requests
import re
headers={
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.49"
}
# 用来提取m3u8的url地址
obj=re.compile(r"url: '(?P<url>.*?)',",re.S)
url="https://www.91kanju.com/vod-play/54812-1-1.html"
resp=requests.get(url,headers=headers)
# 拿到m3u8的地址
m3u8_url=obj.search(resp.text).group("url")
print(m3u8_url)
# 下载m3u8文件
resp2=requests.get(m3u8_url,headers=headers)
with open("哲仁往后.m3u8",mode="wb") as f:
f.write(resp2.content)
resp2.close()
print("下载完毕")
4.下载视频
# 解析m3u8文件
n=1
with open("哲仁往后.m3u8",mode="r",encoding="utf-8") as f:
for line in f:
# 去掉空格,空白,换行符
line=line.strip()
# 如果以#开头,丢掉
if line.startswith("#"):
continue
# print(line)
# 下载视频的片段
resp3=requests.get(line)
f=open(f"video/{n}.ts",mode="wb")
f.write(resp3.content)
f.close()
resp3.close()
n+=1
print("完成了一个")
5.合并视频
···
抓取91看剧视频(复杂)
"""
思路:
1.拿到主页面的页面源代码,找到iframe
2.从iframe的页面源代码中拿到m3u8文件
3.下载第一层m3u8文件->下载第二层m3u8文件
4.下载视频
5.下载密钥,进行解密操作
6.合并所有文件为一个MP4文件
https
verify=False
"""
import requests
from bs4 import BeautifulSoup
import re
import asyncio
import aiohttp
import aiofiles
from Crypto.Cipher import AES
import os
headers={
# 浏览器信息
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.49",
# 防盗链处理(溯源)
# "Referer": url
}
# 获取iframe链接
def get_iframe_src(url):
# 获取页面源代码
# resp=requests.get(url,headers=headers)
# 使用将页面代码转换为BeautifulSoup对象
# main_page=BeautifulSoup(resp.text,"html.parser")
# 使用BeautifulSoup语法查找iframe地址
# src=main_page.find("iframe").get("src")
# 返回iframe地址
# return src
# 网速原因,为了测试,不每次获取
return "https://boba.52kuyun.com/share/xfPs9NPHvYGhNzFp"
def get_first_m3u8_ulr(url):
resp=requests.get(url,headers=headers)
# 预加载re正则模块
obj=re.compile(r'var main = "(?P<m3u8_url>.*?)";',re.S)
m3u8_url=obj.search(resp.text).group("m3u8_url")
print(m3u8_url)
return m3u8_url
# 下载m3u8文件
def download_m3u8_file(url,name):
resp=requests.get(url)
with open(name,mode="wb") as f:
f.write(resp.content)
async def download_ts(url,name,session):
async with session.get(url) as resp:
async with aiofiles.open(f"video2/{name}",mode="wb") as f:
# 把下载的内容写入到文件中
await f.write(await resp.content.read())
print(f"{name}下载完毕")
#https://boba.52kuyun.com/20170906/Moh2l9zV/hls/
async def aio_download(up_ulr):
# 创建一个异步的任务列表
tasks=[]
# 提前准备好session
async with aiohttp.ClientSession() as session:
async with aiofiles.open("越狱第一季第一集_first_m3u8.txt",mode="r",encoding="utf-8") as f:
async for line in f:
if line.startswith("#"):
continue
# line就是xxxx.ts
# 去掉没用的换行,空格,空白符
line=line.strip()
# 拼接真正的ts路径
ts_url=up_ulr+line
# 创建任务
task=asyncio.create_task(download_ts(ts_url,line,session))
tasks.append(tasks)
# 等待任务结束
await asyncio.wait(tasks)
def get_key(url):
resp=requests.get(url)
return resp.text
async def dec_ts(name,key):
aes=AES.new(key=key,IV=b"0000000000000000",mode=AES.MODE_CBC)
async with aiofiles.open(f"video2/{name}",mode="rb") as f1,aiofiles.open(f"video2/temp_{name}",mode="wb") as f2:
# 从源文件中读取内容
bs=await f1.read()
# 把解密好的内容写入文件
await f2.write(aes.decrypt(bs))
print(f"{name}处理完毕")
async def aio_dec(key):
# 解密
tasks=[]
async with aiofiles.open("越狱第一季第一集_second_m3u8.txt",mode="r",encoding="utf-8") as f:
async for line in f:
if line.startswith("#"):
continue
line=line.strip()
# 开始创建异步任务
task=asyncio.create_task(dec_ts(line,key))
tasks.append(task)
await asyncio.wait(tasks)
def merge_ts():
# mac:cat 1.ts 2.ts 3.ts > xxx.mp4
# windows:copy /b 1.ts+2.ts+3.ts xxx.mp4
lst=[]
with open("越狱第一季第一集_second_m3u8.txt",mode="r",encoding="utf-8") as f:
for line in f:
if line.startswith("#"):
continue
line=line.strip()
lst.append(f"video2/temp_{line}")
s="+".join(lst)
os.system(f"copy /b {s} movie.mp4")
print("ok")
def main(url):
# 1.拿到页面源代码,找到iframe对应的url
iframe_src=get_iframe_src(url)
# 2.拿到第一层的m3u8文件的下载地址
first_m3u8_url=get_first_m3u8_ulr(iframe_src)
# 拿到iframe的域名
"https://boba.52kuyun.com/share/xfPs9NPHvYGhNzFp"
iframe_domain=iframe_src.split("/share")[0]
# 拼接出真正的m3u8的下载路径
first_m3u8_url=iframe_domain+first_m3u8_url
# https://boba.52kuyun.com/20170906/Moh2l9zV/index.m3u8?sign=
# https://boba.52kuyun.com/20170906/Moh2l9zV/hls/index.m3u8
# 3.1下载第一层m3u8文件
download_m3u8_file(first_m3u8_url,"越狱第一季第一集_first_m3u8.txt")
# 3.2下载第二层m3u8
with open("越狱第一季第一集_first_m3u8.txt",mode="r",encoding="utf-8") as f:
for line in f:
if line.startswith("#"):
continue
else:
# 去掉空白或者换行符
line=line.strip()
# 准备拼接第二层m3u8的下载路径
# https://boba.52kuyun.com/20170906/Moh2l9zV/ + hls/index.m3u8
# https://boba.52kuyun.com/20170906/Moh2l9zV/hls/index.m3u8
# https://boba.52kuyun.com/20170906/Moh2l9zV/hls/cFN8o3436000.ts
second_m3u8_url=first_m3u8_url.split("index.m3u8")[0]+line
download_m3u8_file(second_m3u8_url,"越狱第一季第一集_second_m3u8.txt")
print("第二层下载完毕")
# 4.下载视频
second_m3u8_url_up=second_m3u8_url.replace("index.m3u8","")
# 异步协程
asyncio.run(aio_download(second_m3u8_url_up))
# 5.1拿到密钥(偷懒写法)
key_url=second_m3u8_url_up+"key.key"
key=get_key(key_url)
# 5.2 解密(异步)
asyncio.run(aio_dec(key))
# 6.合并ts文件为MP4文件
merge_ts()
if __name__ == '__main__':
url="https://www.91kanju.com/vod-play/541-2-1.html"
main(url)
爬取云播TV刺杀小说家
简单的问题复杂化,复杂的问题简单化
"""
思路:
1.拿到主页面的页面源代码,找到iframe
2.从iframe的页面源代码中拿到m3u8文件
3.下载第一层m3u8文件->下载第二层m3u8文件
4.下载视频
5.下载密钥,进行解密操作
6.合并所有文件为一个MP4文件
https
verify=False
"""
import requests
from bs4 import BeautifulSoup
import re
import asyncio
import aiohttp
import aiofiles
from Crypto.Cipher import AES
import os
headers={
# 浏览器信息
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.49",
# 防盗链处理(溯源)
# "Referer": url
}
# 获取iframe链接
def get_iframe_src(url):
# 获取页面源代码
resp=requests.get(url,headers=headers)
obj_1=re.compile(r',"link_pre":"","url":"(?P<src>.*?)","url_next":"",',re.S)
src=obj_1.search(resp.text).group("src").replace("\\","")
print(src)
return src
def get_first_m3u8_ulr(url):
resp=requests.get(url,headers=headers)
obj=re.compile(r'var main = "(?P<m3u8_url>.*?)";',re.S)
m3u8_url=obj.search(resp.text).group("m3u8_url")
print(m3u8_url)
return m3u8_url
# 下载m3u8文件
def download_m3u8_file(url,name):
resp=requests.get(url,headers=headers)
with open(name,mode="wb") as f:
f.write(resp.content)
async def download_ts(url,name,session):
async with session.get(url,headers=headers) as resp:
async with aiofiles.open(f"video2/{name}",mode="wb") as f:
# 把下载的内容写入到文件中
await f.write(await resp.content.read())
print(f"{name}下载完毕")
#https://boba.52kuyun.com/20170906/Moh2l9zV/hls/
async def aio_download(up_url):
# 创建一个异步的任务列表
tasks=[]
# 提前准备好session
async with aiohttp.ClientSession() as session:
async with aiofiles.open("刺杀小说家_second_m3u8.txt",mode="r",encoding="utf-8") as f:
async for line in f:
if line.startswith("#"):
continue
# line就是xxxx.ts
# 去掉没用的换行,空格,空白符
ts_url=line.strip()
# 拼接真正的ts路径
# ts_url=up_url+line
line=ts_url.split("/")[-1]
print(ts_url,line)
# 创建任务
task=asyncio.create_task(download_ts(ts_url,line,session))
tasks.append(task)
# 等待任务结束
await asyncio.wait(tasks)
def get_key(url):
resp=requests.get(url,headers=headers)
return resp.text
async def dec_ts(name,key):
aes=AES.new(key=key,IV=b"0000000000000000",mode=AES.MODE_CBC)
async with aiofiles.open(f"video2/{name}",mode="rb") as f1,aiofiles.open(f"video2/temp_{name}",mode="wb") as f2:
# 从源文件中读取内容
bs=await f1.read()
# 把解密好的内容写入文件
await f2.write(aes.decrypt(bs))
print(f"{name}处理完毕")
async def aio_dec(key):
# 解密
tasks=[]
async with aiofiles.open("刺杀小说家_second_m3u8.txt",mode="r",encoding="utf-8") as f:
async for line in f:
if line.startswith("#"):
continue
line=line.strip().split("/")[-1]
# 开始创建异步任务
task=asyncio.create_task(dec_ts(line,key))
tasks.append(task)
await asyncio.wait(tasks)
def merge_ts():
# mac:cat 1.ts 2.ts 3.ts > xxx.mp4
# windows:copy /b 1.ts+2.ts+3.ts xxx.mp4
lst=[]
with open("刺杀小说家_second_m3u8.txt",mode="r",encoding="utf-8") as f:
for line in f:
if line.startswith("#"):
continue
line=line.strip().split("/")[-1]
# print(f"video2/temp_{line}")
lst.append(f"video2\\temp_{line}")
# print(lst)
# dos命令有长度限制,所以循环进行合并
for i in range(int(len(lst)/100)):
print(i+1)
print(lst[i::100])
s="+".join(lst[i:100])
# print(s)
os.system('copy /b '+s+' .\\file\\movie'+str(i)+'.mp4')
print("ok")
lst=[]
for i in range(38):
lst.append("file\\movie"+str(i)+".mp4")
s="+".join(lst)
print(s)
os.system('copy /b '+s+' .\\file\\movie989.mp4')
def main(url):
# 1.拿到页面源代码,找到iframe对应的url
iframe_src=get_iframe_src(url)
# iframe_src="https://vod3.buycar5.cn/share/qaqLG8Crlmonyofu"
# 2.拿到第一层的m3u8文件的下载地址
first_m3u8_url=get_first_m3u8_ulr(iframe_src)
# first_m3u8_url="/20210409/jyvh1E3q/index.m3u8"
# 拿到iframe的域名
# "https://boba.52kuyun.com/share/xfPs9NPHvYGhNzFp"
iframe_domain=iframe_src.split("/share")[0]
# 拼接出真正的m3u8的下载路径
first_m3u8_url=iframe_domain+first_m3u8_url
# https://boba.52kuyun.com/20170906/Moh2l9zV/index.m3u8?sign=
# https://boba.52kuyun.com/20170906/Moh2l9zV/hls/index.m3u8
# 3.1下载第一层m3u8文件
download_m3u8_file(first_m3u8_url,"刺杀小说家_first_m3u8.txt")
# 3.2下载第二层m3u8
with open("刺杀小说家_first_m3u8.txt",mode="r",encoding="utf-8") as f:
for line in f:
if line.startswith("#"):
continue
else:
# 去掉空白或者换行符
line=line.strip()
# 准备拼接第二层m3u8的下载路径
second_m3u8_url=first_m3u8_url.split("/2021")[0]+line
print(second_m3u8_url)
# "https://vod3.buycar5.cn/20210409/jyvh1E3q/1000kb/hls/index.m3u8?_t=1618561729094"
download_m3u8_file(second_m3u8_url,"刺杀小说家_second_m3u8.txt")
print("第二层下载完毕")
# 4.下载视频
second_m3u8_url_up = second_m3u8_url.replace("index.m3u8", "")
# second_m3u8_url_up="https://ts3.xarxrljt.com:9999/20210409/jyvh1E3q/1000kb/hls/"
# # 异步协程
asyncio.run(aio_download(second_m3u8_url_up))
# 5.1拿到密钥(偷懒写法)
key_url=second_m3u8_url_up+"key.key"
key=get_key(key_url)
# print(key)
# 5.2 解密(异步)
asyncio.run(aio_dec(key))
merge_ts()
if __name__ == '__main__':
url="https://www.hnxmz.com/vodplay/106209-2-1.html"
main(url)

浙公网安备 33010602011771号