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)
posted @ 2021-05-16 23:48  double大博  阅读(120)  评论(0)    收藏  举报