Event事件,进程池与线程池,高性能爬取梨视频,协程,协程的目的,gevent。

1.Event事件

Event 事件的作用:

用来控制线程的执行。

由一些线程去控制另一些线程。

from threading import Event

调用Event 类实例化一个对象。 e = Event
若该方法出现在任务中,则为False,阻塞  
e.wait()  #False
若该方法出现在任务中,则将其他线程的Flase改为True,进入就绪态与运行态
e.set()  # True

2.进程池与线程池

1)什么是进程池与线程池?
进程池与线程池是用来控制当前程序允许创建(进程/线程)的数量.
2)进程池与线程池的作用:
保证在硬件允许的范围内创建 (进程/线程) 的数量.

3)如何使用:

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import time

# ProcessPoolExecutor(5)  # 5代表只能开启5个进程
# ProcessPoolExecutor()  # 默认以CPU的个数限制进程数

pool = ThreadPoolExecutor(5)  # 5代表只能开启5个线程 -5 +1 -1 +1 -1
# ThreadPoolExecutor()  # 默认以CPU个数 * 5 限制线程数

# t = Tread()  # 异步提交
# t.start(0)

# pool.submit('传函数地址')  # 异步提交任务
# def task():
#     print('线程任务开始了...')
#     time.sleep(1)
#     print('线程任务结束了...')
#
#
# for line in range(5):
#     pool.submit(task)


# 异步提交任务
# pool.submit('传函数地址').add_done_callback('回调函数地址')
def task():
    print('线程任务开始了...')
    time.sleep(1)
    print('线程任务结束了...')
    return 123

# 回调函数
def call_back(res):
    print(type(res))
    # 注意: 赋值操作不要与接收的res同名
    res2 = res.result()
    print(res2)

for line in range(2):
    pool.submit(task).add_done_callback(call_back)
    
#
线程任务开始了...
线程任务开始了...
线程任务结束了...
<class 'concurrent.futures._base.Future'>
123
线程任务结束了...
<class 'concurrent.futures._base.Future'>
123

高性能爬取梨视频

网站主页:
    https://www.pearvideo.com/

requests: 用户封装底层socket套接字
    - 打开CMD
        # 下载第三方模块
        >> pip3 install requests

https://www.pearvideo.com/video_1614813
https://www.pearvideo.com/video_1615201

1.从主页中获取所有的视频ID号(1615201,1614813...)
    - 拼接视频详情页url
    https://www.pearvideo.com/video_ + '视频ID号'

2.在视频详情页中获取真实视频url
    srcUrl="()"

3.往真实视频url地址发送请求获取 视频 二进制数据

4.最后把视频二进制数据保存到本地

## pip3 install requests  #终端输入:pip3 install requests 
from concurrent.futures import ThreadPoolExecutor
import requests
import re
import uuid

代码:

from concurrent.futures import ThreadPoolExecutor
import requests
import re
import uuid

pool = ThreadPoolExecutor(200)

# 1.发送请求函数
def get_page(url):
    response = requests.get(url)
    return response

# 2.解析主页获取视频ID号
def parse_index(response):
    id_list = re.findall(
        '<a href="video_(.*?)".*?>',
        response.text,
        re.S
    )
    return id_list

# 3.解析视频详情页获取真实 视频链接
def parse_detail(res):
    response = res.result()
    movie_detail_url = re.findall('srcUrl="(.*?)"', response.text, re.S)[0]
    print(f'往视频链接: {movie_detail_url}发送请求...')

    # 异步往视频详情页链接发送请求,把结果交给
    pool.submit(get_page, movie_detail_url).add_done_callback(save_movie)
    return movie_detail_url

# 4.往真实视频链接发送请求,获取数据并保存到本地
def save_movie(res):
    movie_response = res.result()
    # print(1111)
    # movie_response = get_page(movie_detail_url)
    # print(movie_response)
    name = str(uuid.uuid4())
    print(f'{name}.mp4视频开始保存...')
    with open(f'{name}.mp4', 'wb') as f:
        f.write(movie_response.content)
    print('视频下载完毕!')

if __name__ == '__main__':

    # 1.访问主页获取数据
    index_response = get_page('https://www.pearvideo.com/')

    # # 2.解析主页获取所有的视频id号
    id_list = parse_index(index_response)
    print(id_list)
    # 3.循环对每个视频详情页链接进行拼接
    for id in id_list:
        print(id)
        detail_url = 'https://www.pearvideo.com/video_' + id

        # 异步提交爬取视频详情页,把返回的数据,交给parse_detail(回调函数)
        pool.submit(get_page, detail_url).add_done_callback(parse_detail)

3.协程

进程:资源单位

线程:执行单位。

协程:在单线程下实现并发。

注意:

协程不是操作系统资源,他是程序取的名字,为让单线程能实现并发。

协程的目的:

​ 操作系统:

​ 躲到技术,切换+保存状态

​ 1.遇到 IO

​ 2.CPU执行时间长

协程意义:

​ 通过手动模拟操作系统‘多道技术’,实现 切换+保存。

​ 1.手动实现 遇到 IO却换,欺骗操作系统误以没有 IO操作。

​ 单线程 遇到 IO ,切换 + 保存状态。

​ 单线程 计算密集型。来回切换 + 保存状态,反而效果更低

优点:

在 IO密集型的情况下,会提高效率。

缺点:

若在计算密集型的情况下,来回切换,反而效率更低。

如何实现协程:切换 + 保存状态。

基于 yield 并发执行(验证计算密集型的情况下,反而效率更低)

并发:切换

# 串行执行(串行比协程并发性更高)
import time
def func1():
    for i in range(10000000):
        i+1
def func2():
    for i in range(10000000):
        i+1

start = time.time()
func1()
func2()
stop = time.time()
print(stop-start)   #1.0149040222167969


# 验证计算密集型的情况下效果更低  #  1.2908563613891602   还比如串行
# 基于yield并发执行
import time
def func1():
    while True:
        10000000+1
        yield
def func2():
    # g 生成器对象
    g = func1()
    for i in range(10000000):
        # time.sleep(100)# 模拟 IO,yield 并不会捕捉到并自动切换。
        i+1
        next(g)   #每次执行next相当与切换到func1下面。

start = time.time()
func2()
stop = time.time()
print(stop-start)  # 1.2908563613891602

# 两个都是计算密集型,但是通过手动实现 切换+保存执行效率会低一些(用的秒长一些)。

gevent

是一个第三方模块,可以帮你监听 IO操作,并切换。

需要下载:pip3 install gevent

使用gevent目的:

为了实现单行线程下,实现遇到 IO, 保存状态 +切换

原文地址:https://www.cnblogs.com/WQ577098649/p/11735795.html