爬虫 | 多线程、多进程、协程

进程和线程
进程就相当于各个部门,线程就相当于各个部门里的干事员们

"""
进程中至少有一条线程,线程和进程的开启非常类似,这里就展示线程
"""
from threading import Thread
from multiprocessing import Process

# 第一种开启的方法就是直接def
def func(name):
    for i in range(100):
        print(name, i)

if __name__ == '__main__':
    t = Thread(target=func, args=('T1',))  # 创建线程,布置任务,传入参数(注意这里的参数必须是元素,里面加逗号)
    t.start()  # 告诉线程可以开始工作了

    # 下面是主任务
    for n in range(100):
        print('主任务', n)


# 第二种方法是类继承
class MyThread(Thread):
    def __init__(self, name):   # 这里就是通过构造函数来传入参数name
        Thread.__init__(self)  #  >>> 等价于super().__init__(),先继承父类的东西
        self.name = name

    def run(self):
        for i in range(100):
            print(self.name, i)

if __name__ == '__main__':
    t = MyThread("T1")     # 创建一个实例对象,并传参T1
    t.start()              # 告诉线程可以开始工作了,这里如果是t.run()是不对的,这是在调用方法,实际上是在主线程中同步执行的,而非并发

    # 下面是主任务
    for n in range(100):
        print('主任务', n)

进程池和线程池
就是一次性开很多个进程或者线程

from concurrent.futures import ThreadPoolExecutor


def fn(name):
    for i in range(1000):
        print(name, i)

# main
# 创建线程池,并指定线程池大小为50workers
with ThreadPoolExecutor(max_workers=50) as t:
    for i in range(100):      # 提交100个任务到线程池,这50个workers会共同完成这100个任务。
        t.submit(fn, name=f"线程{i}")  # >>>> 这里是用submit提交,而不是start

    # 提交完所有任务后,线程池会在with块结束时自动关闭
print("123")

协程(单线程下实现并发)
电脑cpu在运行时,遇到input等交互、sleep等休息、或者网络发送get等IO操作(输入输出)时,是在等待状态中的,即阻塞状态,这个时候cpu是没事儿干闲着的,协程就是让它在一闲下来的时候,选择性的切换到其他任务上继续工作。
常见的阻塞操作:同步IO操作,time.sleep,request.get()等

协程在微观和宏观上
微观上:一个任务接一个任务的切换,切换条件一般就是IO操作
宏观上:我们看到的其实是多个任务一起在执行
也就是多任务异步操作

这是程序来切换的,不是操作系统来切换

# 其实有很多种写法,这里就展示主流的一种,在def前面加async

async def func():
  print("你好啊,小明")

if __name__ == '__main__':
    g = func()   # 创建协程对象。此时的函数是异步协程函数,此时函数执行得到的是一个协程对象,而不会直接执行
    asyncio.run(g)  # 运行协程对象(但不能直接运行多个)

"""async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行。(不然整个界面有很多异步函数,大家都各自并发执行了,就完全没有顺序科研了,await就是来确保要fun1完成后,再执行func2)"""

运行多个协程对象

async def func1():
    print("你好啊,小明")
    # time.sleep(3)    # time.sleep有阻塞性质,是一种同步操作
    await asyncio.sleep(3)  # 如果这里想发挥time.sleep的功能,只能用模块中的功能asyncio.sleep
    print("你好啊,小明")


async def func2():
    print("你好啊,塞尔达")
    # time.sleep(2)
    await asyncio.sleep(2)
    print("你好啊,塞尔达")


async def func3():
    print("你好啊,林克")
    # time.sleep(4)
    await asyncio.sleep(4)
    print("你好啊,林克")


async def main():
    f1 = func1()  # 创建协程对象
    f2 = func2()
    f3 = func3()
    tasks = [
        asyncio.create_task(f1),  # 派发任务:告诉程序,这就是需要处理的协程对象
        asyncio.create_task(f2),
        asyncio.create_task(f3)
    ]

    await asyncio.wait(tasks)  # await只能放在异步函数内
    await asyncio.gather(*tasks) # 是另一种返回方法,返回的信息简单一点
    print(await asyncio.wait(tasks))  # 返回了很丰富的信息,包括任务名、函数名、函数所在的文件路径、函数返回值
    print(await asyncio.gather(*tasks))   # 只返回了函数的返回值(这里都是none)

if __name__ == '__main__':
    # 一次性启动多个任务(协程)需要用wait来处理列表
    t1 = time.time()
    asyncio.run(main())
    t2 = time.time()
    print(t2 - t1)

爬虫异步模板

async def download(url):
    print('准备开始下载')
    await asyncio.sleep(1)  # 这一步是在模拟异步中的网络请求,不能是request.get,这个不适用于异步操作。要使用aiohttp里的方法
    print('下载完成')

async def main():
    urls = [
        'https://www.bilibili.com',
        'https://www.baidu.com',
        'https://www.wechat.com'
    ]

    tasks = []
    for url in urls:
        task = asyncio.create_task(download(url))
        tasks.append(task)
    
    await asyncio.wait(tasks)   # await我理解是,挂起,告诉程序这一步可以挂在这里进行协程,一边等下载,一边cpu继续去干其他的

if __name__ == '__main__':
    asyncio.run(main())   # run这个异步函数返回的协程对象(异步函数返回值自动就是协程对象)

关于报错:coroutine 'func1' was never awaited

# 过去的写法是:
import asyncio
import time

async def func1():
    print("你好啊,我是1")
    await asyncio.sleep(3)
    print("你好啊,我是1")


async def func2():
    print("你好啊,我是2")
    await asyncio.sleep(2)
    print("你好啊,我是2")


async def func3():
    print("你好啊,我是3")
    await asyncio.sleep(4)
    print("你好啊,我是3")


if __name__ == '__main__':
    f1 = func1()  # 此时的函数是异步协程函数。此时函数执行得到的是一个协程对象
    f2 = func2()
    f3 = func3()
    # asyncio.run(f1)  #协程程序运行需要asyncio模块的支持
    tasks = [
        f1, f2, f3
    ]
    t1 = time.time()
    # 一次性启动多个任务(协程)
    asyncio.run(asyncio.wait(tasks))
    t2 = time.time()
    print(t2 - t1)

"""由于python 3.8以后的一些调整,这么写无法运行,方法就是要包成task对象,也就是create_task。可参考:https://fishc.com.cn/thread-224340-1-1.html"""
posted @   abloger  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· Qt个人项目总结 —— MySQL数据库查询与断言
点击右上角即可分享
微信分享提示