文章目录
- 前言
- 一、多任务的概念
- 1、什么是多任务?
- 2、多任务的两种表现形式
- 二、进程的概念
- 1、程序中实现多任务的方式
- 2、进程的概念
- 3、多进程的作用
- 三、多进程完成多任务
- 1、多进程完成多任务的核心逻辑
- 2、通过进程类创建进程对象
- 3、进程执行带有参数的任务
- 四、获取进程编号
- 1、进程编号的作用
- 2、两种进程编号
- 3、获取当前进程编号
- 五、进程应用注意点
- 1、进程间不共享全局变量
- 2、主进程与子进程的结束顺序
- 解决方案一:设置守护进程
- 解决方案二:销毁子进程
- 六、线程的概念
- 1、线程的概念
- 2、 为什么使用多线程?
- 3、多线程的作用
- 单线程执行
- 多线程执行
- 七、多线程完成多任务
- 1、多线程完成多任务的核心逻辑
- 2、线程创建与启动代码
- 3、线程执行带有参数的任务
- 4、主线程和子线程的结束顺序
- 守护线程
- 全局变量控制
- 5、线程间的执行顺序
- 获取当前线程信息
- 线程间的执行顺序
- 6、线程间共享全局变量
- 八、进程和线程对比
- 1、关系对比
- 2、区别对比
- 3、优缺点对比
- 进程的优缺点
- 线程的优缺点
- 九、协程
- 1、协程的定义与优势
- 协程的定义
- 协程的核心优势
- 2、协程适用场景
- 3、Python 协程的实现方式
- 1、基础语法
- 2、事件循环与任务创建
- 5、协程爬虫(结合 aiohttp )
- 6、协程爬虫综合应用
- 结语
在日常开发中,“让程序同时干多件事” 是提升效率的核心需求 —— 比如一边下载文件一边处理数据,或是批量爬取网页。Python 提供了进程、线程、协程三种多任务实现方式,它们各有适用场景与优劣。本文从基础概念出发,结合代码实例拆解这三种技术的用法、注意事项与实战场景,帮你掌握 Python 多任务编程的核心逻辑。
1、什么是多任务?
2、多任务的两种表现形式
多任务主要有两种核心表现形式,分别对应不同的资源调度策略:
- 并发:多个任务在“同一时间段内”交替执行。比如单CPU核心下,操作系统快速切换“下载文档”和“编辑文档”两个任务,每个任务执行一小段时间后暂停,切换到另一个任务继续执行。从宏观上看,两个任务在同一时间段内都在推进,但微观上是交替执行的。
- 并行:多个任务在“同一时刻”真正同时执行。这需要依赖多CPU核心,比如电脑有两个CPU核心,一个核心专门执行“下载文件”任务,另一个核心专门执行“编辑文档”任务,两个任务在同一时刻都在运行,是真正的同时执行。
简单总结:并发是“交替执行”,并行是“同时执行”;并发可以在单CPU核心实现,并发必须依赖多CPU核心。
1、程序中实现多任务的方式
在Python中,实现多任务的核心有三种:进程。线程、协程。其中,进程是操作系统进行资源分配和调度的基本单位,也是实现多任务的最基础方式。通过创建多个进程,操作系统会为每个进程分配独立的内存空间、CPU时间片等资源,让多个进程交替或同时执行,从而实现多任务。
2、进程的概念
3、多进程的作用
- 未使用多进程(单进程)
- 单进程模式下,任务只能顺序执行,耗时等于所有任务耗时之和。
import time
定义任务1:模拟下载文件
def download_file(file_name, cost_time):
print(f"开始下载{file_name}...") time.sleep(cost_time) # 模拟下载耗时 print(f"{file_name}下载完成!")
单进程执行两个下载任务
if name == “main”:
start_time = time.time() # 记录开始时间 # 顺序执行两个任务 download_file("文件A", 3) # 耗时3秒 download_file("文件B", 4) # 耗时4秒 end_time = time.time() # 记录结束时间 print(f"总耗时:{end_time - start_time:.2f}秒")

- 使用多进程
使用多进程时,两个任务可以同时执行(并发或并行),总耗时等于耗时最长的任务耗时。Python中可以通过multiprocessing模块创建多进程。
import time
from multiprocessing import Process # 导入Process类
定义任务1:模拟下载文件
def download_file(file_name, cost_time):
print(f"开始下载{file_name}...") time.sleep(cost_time) # 模拟下载耗时 print(f"{file_name}下载完成!")
多进程执行两个下载任务
if name == “main”:
start_time = time.time() # 1. 创建进程对象:target指定任务函数,args指定任务参数(元组形式) p1 = Process(target=download_file, args=("文件A", 3)) p2 = Process(target=download_file, args=("文件B", 4)) # 2. 启动进程 p1.start() p2.start() # 3. 等待进程执行完成(主进程阻塞,直到子进程结束) p1.join() p2.join() end_time = time.time() print(f"总耗时:{end_time - start_time:.2f}秒")

1、多进程完成多任务的核心逻辑
多进程完成多任务的核心步骤:
- 定义需要执行的任务函数;
- 通过Process类创建多个进程对象,每个进程对象绑定一个任务;
- 调用进程对象的start()方法启动进程;
- (可选)调用join()方法让主进程等待子进程执行完成,避免主进程提前结束。
2、通过进程类创建进程对象
Process类的核心参数说明:
- target:指定进程要执行的任务函数(不需要加括号);
- args:指定任务函数的参数,必须是元组(tuple)类型,即使只有一个参数,也要加逗号(如args=(10,));
- name:指定进程的名称(可选,默认是Process-1、Process-2…)。
基础示例(创建3个进程执行不同任务):
import time
from multiprocessing import Process
任务1:打印数字
def print_numbers(name, count):
for i in range(count): print(f"{name}: {i}") time.sleep(0.5)
任务2:打印字母
def print_letters(name, count):
for i in range(count): print(f"{name}: {chr(65 + i)}") # 65是'A'的ASCII码 time.sleep(0.5)
if name == “main”:
# 创建3个进程 p1 = Process(name="数字进程", target=print_numbers, args=("数字进程", 3)) p2 = Process(name="字母进程1", target=print_letters, args=("字母进程1", 3)) p3 = Process(name="字母进程2", target=print_letters, args=("字母进程2", 3)) # 启动所有进程 p1.start() p2.start() p3.start() # 等待所有进程结束 p1.join() p2.join() p3.join() print("所有任务执行完成!")

3、进程执行带有参数的任务
当任务函数需要接收参数时,通过Process类的args参数传递,注意参数必须是元组类型。下面是一个更具体的示例:模拟多人同时下载不同大小的文件。
import time
from multiprocessing import Process
任务函数:模拟下载文件,参数为用户名、文件名、文件大小(决定下载耗时)
def download(user, file_name, file_size):
print(f"{user}开始下载{file_name}(大小:{file_size}MB)...") # 假设1MB需要1秒下载时间 time.sleep(file_size) print(f"{user}的{file_name}下载完成!")
if name == “main”:
# 创建4个进程,执行不同参数的下载任务 tasks = [ ("用户A", "电影.mp4", 5), ("用户B", "文档.pdf", 2), ("用户C", "图片.png", 1), ("用户D", "音乐.mp3", 3) ] # 存储所有进程对象 processes = [] for task in tasks: # 解包元组,分别赋值给三个变量user, file_name, size user, file_name, size = task p = Process(target=download, args=(user, file_name, size)) processes.append(p) p.start() # 等待所有进程完成 for p in processes: p.join() print("所有用户下载任务完成!")

1、进程编号的作用
进程编号(PID)是操作系统为每个进程分配的唯一标识,用于区分不同的进程。其核心作用包括:
- 定位和管理进程(如通过PID终止异常进程);
- 实现进程间的通信和同步(如通过PID识别通信对象);
- 调试程序(如查看哪个进程出现问题)。
2、两种进程编号
- 当前进程编号(PID):当前进程的唯一标识;
- 父进程编号(PPID):当前进程的父进程(创建当前进程的进程)的唯一标识。
比如在Python程序中,主进程创建的子进程,子进程的PPID就是主进程的PID。
3、获取当前进程编号
Python中获取进程编号需要用到两个模块:
- os模块:通过os.getpid()获取当前进程PID,os.getppid()获取父进程PPID;
- multiprocessing模块:通过current_process()获取当前进程对象,再通过pid属性获取PID。
代码示例:
import os
import time from multiprocessing import Process, current_process
任务函数:打印当前进程信息
def task(name):
# 方式1:通过os模块获取 pid = os.getpid() ppid = os.getppid() print(f"任务{name} - PID:{pid},PPID:{ppid}(os模块)") # 方式2:通过current_process()获取 current_proc = current_process() print(f"任务{name} - 进程名:{current_proc.name},PID:{current_proc.pid}(multiprocessing模块)") time.sleep(2)
if name == “main”:
# 打印主进程信息 main_pid = os.getpid() print(f"主进程 - PID:{main_pid}") # 创建两个子进程 p1 = Process(name="子进程1", target=task, args=("A",)) p2 = Process(name="子进程2", target=task, args=("B",)) p1.start() p2.start() p1.join() p2.join() print("主进程结束!")

1、进程间不共享全局变量
from multiprocessing import Process
定义全局变量
global_num = 0
任务函数:修改全局变量
def modify_global():
global global_num # 声明使用全局变量 for i in range(): global_num += 1 print(f"子进程修改后:global_num = {global_num}")
if name == “main”:
# 创建子进程 p = Process(target=modify_global) p.start() p.join() # 等待子进程修改完成 # 主进程打印全局变量 print(f"主进程中:global_num = {global_num}")

2、主进程与子进程的结束顺序
默认情况下,主进程会等待所有子进程执行完成后才会结束,但有时我们需要让主进程结束时,子进程也随之结束(比如主进程是一个监控程序,主进程退出后,子进程无需继续运行)。此时有两种解决方案:设置守护进程、主动销毁子进程。
解决方案一:设置守护进程
import time
from multiprocessing import Process
任务函数:模拟长时间运行
def long_task():
while True: print("子进程正在运行...") time.sleep(1)
if name == “main”:
# 创建子进程,设置为守护进程 p = Process(target=long_task) p.daemon = True # 必须在start()前设置 p.start() # 主进程运行3秒后结束 time.sleep(3) print("主进程结束,守护子进程被强制终止!")

解决方案二:销毁子进程
通过调用进程对象的terminate()方法,可以主动销毁子进程。这种方式适合需要灵活控制子进程生命周期的场景(比如根据特定条件终止子进程)。
import time
from multiprocessing import Process
任务函数:模拟运行
def task():
i = 0 while True: print(f"子进程运行中,i = {i+1}") i += 1 time.sleep(1)
if name == “main”:
p = Process(target=task) p.start() # 主进程运行3秒后,主动销毁子进程 time.sleep(3) p.terminate() # 销毁子进程 print("主进程主动销毁子进程!") print("主进程结束!")

1、线程的概念
线程是进程内部的一个“执行单元”,是操作系统进行任务调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的内存空间和系统资源(如文件描述符、网络连接等),线程之间的通信比进程更高效(直接访问共享内存即可)。
2、 为什么使用多线程?
虽然多进程也能实现多任务,但多线程有以下核心优势,使其在很多场景下更适合:
- 资源消耗更少:创建一个线程的资源消耗远小于创建一个进程(线程共享进程资源,无需单独分配一个内存空间);
- 通信更高效:线程之间共享全局变量,无需使用复杂的进程间通信机制;
- 切换速度更快:线程是操作系统调度的最小单位,切换线程的开销比切换进程小得多。
多线程的适用场景:IO密集型任务(如文件读写、网络请求、数据库操作等),这类任务的大部分时间都是在等待IO完成,多线程可以在一个线程等待IO时,切换到另一个线程执行,提升CPU利用率。
3、多线程的作用
多线程的核心作用是在同一个进程内实现多个任务的并发执行,提升程序的执行效率,尤其是在IO密集型场景下。下面通过对比“单线程执行”和“多线程执行”的代码实例,直观感受多线程的优势。
单线程执行
单线程模式下,任务顺序执行,总耗时等于所有任务耗时之和。
import time
任务1:模拟网络请求
def request_url(url, cost_time):
print(f"开始请求{url}...") time.sleep(cost_time) # 模拟网络等待时间 print(f"{url}请求完成!")
单线程执行三个网络请求
if name == “main”:
start_time = time.time() request_url("https://www.baidu.com", 2) request_url("https://www..com", 3) request_url("https://www.163.com", 1) end_time = time.time() print(f"总耗时:{end_time - start_time:.2f}秒")

多线程执行
Python中实现多线程主要使用threading模块,核心类是Thread。多线程执行时,任务并发执行,总耗时等于最长的任务耗时。
import time
from threading import Thread # 导入Thread类
任务1:模拟网络请求
def request_url(url, cost_time):
print(f"开始请求{url}...") time.sleep(cost_time) # 模拟网络等待时间 print(f"{url}请求完成!")
多线程执行三个网络请求
if name == “main”:
start_time = time.time() # 1. 创建线程对象 t1 = Thread(target=request_url, args=("https://www.baidu.com", 2)) t2 = Thread(target=request_url, args=("https://www..com", 3)) t3 = Thread(target=request_url, args=("https://www.163.com", 1)) # 2. 启动线程 t1.start() t2.start() t3.start() # 3. 等待线程执行完成 t1.join() t2.join() t3.join() end_time = time.time() print(f"总耗时:{end_time - start_time:.2f}秒")

1、多线程完成多任务的核心逻辑
多线程完成多任务的步骤与多进程类似:
- 定义任务函数;
- 通过Thread类创建多个线程对象,绑定任务和参数;
- 调用start()方法启动线程;
- (可选)调用join()方法让主线程等待子线程完成。
2、线程创建与启动代码
Thread类的核心参数与Process类相似:
- target:指定线程要执行的任务函数;
- args:指定任务函数的参数,元组类型;
- name:指定线程名称(可选);
基础示例(创建多个线程执行不同任务):
import time
from threading import Thread
任务1:打印时间
def print_time(name, interval):
""" 打印指定名称的当前时间,并按照指定间隔重复打印 参数: name (str): 打印时间时显示的名称前缀 interval (int/float): 打印时间的间隔(秒) """ while True: # 无限循环,持续打印时间 print(f"{name}:{time.strftime('%H:%M:%S', time.localtime())}") # 格式化并打印当前时间 time.sleep(interval) # 暂停指定秒数后继续执行
任务2:计数
def count_numbers(name, max_count):
for i in range(max_count): print(f"{name}:{i}") time.sleep(1)
if name == “main”:
# 创建线程 t1 = Thread(name="时间线程", target=print_time, args=("时间线程", 2)) t2 = Thread(name="计数线程", target=count_numbers, args=("计数线程", 5)) # 启动线程 t1.start() t2.start() # 等待计数线程完成 t2.join() # 计数线程完成后,终止时间线程(通过设置全局变量控制) print("计数线程完成,程序结束!")
3、线程执行带有参数的任务
线程执行带有参数的任务,与进程类似,通过args参数传递元组类型的参数。下面示例模拟多线程下载不同类型的文件:
import time
from threading import Thread
任务函数:模拟下载,参数为文件类型、文件数量、单个文件耗时
def download_files(file_type, count, cost_per):
for i in range(1, count + 1): print(f"开始下载{file_type}{i},预计耗时{cost_per}秒...") time.sleep(cost_per) print(f"{file_type}{i}下载完成!")
if name == “main”:
# 定义任务列表 tasks = [ ("图片", 3, 1), # 下载3张图片,每张1秒 ("视频", 2, 3), # 下载2个视频,每个3秒 ("文档", 4, 0.5) # 下载4个文档,每个0.5秒 ] # 存储线程对象 threads = [] for task in tasks: file_type, count, cost = task t = Thread(target=download_files, args=(file_type, count, cost)) threads.append(t) t.start() # 等待所有线程完成 for t in threads: t.join() print("所有文件下载完成!")

4、主线程和子线程的结束顺序
默认情况下,主线程会等待所有子线程执行完成后才会结束。如果需要主线程结束时子线程也随之结束,可以通过“全局变量控制”或“守护线程”实现。
守护线程
与进程类似,线程也可以设置守护线程,通过daemon属性设置(必须在start()前设置)。主线程结束时,守护线程会被强制终止。
import time
from threading import Thread
def task():
while True: print("子线程运行中...") time.sleep(1)
if name == “main”:
t = Thread(target=task) t.daemon = True # 设置为守护线程 t.start() # 主线程运行3秒后结束 time.sleep(3) print("主线程结束,守护子线程被终止!")

全局变量控制
通过定义一个全局变量(如running),控制子线程的循环执行。主线程需要结束时,修改全局变量的值,子线程检测到后主动退出。这种方式比守护线程更优雅,可以让子线程在退出前完成收尾工作。
import time
from threading import Thread
全局变量:控制子线程运行状态
running = True
def task():
while running: print("子线程运行中...") time.sleep(1) print("子线程检测到退出信号,正在收尾...") time.sleep(2) print("子线程收尾完成,退出!")
if name == “main”:
t = Thread(target=task) t.start() # 主线程运行3秒后,修改全局变量 time.sleep(3) running = False print("主线程发出退出信号!") # 等待子线程完成收尾 t.join() print("主线程结束!")

5、线程间的执行顺序
获取当前线程信息
通过threading.current_thread()可以获取当前正在执行的线程对象,进而获取线程名称、线程ID等信息。
import time
from threading import Thread, current_thread
def task(name):
print(f"当前线程:{current_thread().name},线程ID:{current_thread().ident}") time.sleep(1)
if name == “main”:
print(f"主线程:{current_thread().name},线程ID:{current_thread().ident}") t1 = Thread(name="子线程1", target=task, args=("子线程1",)) t2 = Thread(name="子线程2", target=task, args=("子线程2",)) t1.start() t2.start() t1.join() t2.join()

线程间的执行顺序
import time
from threading import Thread
def task(name):
for i in range(3): print(f"{name}:{i}") time.sleep(0.1) # 短暂休眠,让线程切换更明显
if name == “main”:
t1 = Thread(name="线程A", target=task, args=("线程A",)) t2 = Thread(name="线程B", target=task, args=("线程B",)) t1.start() t2.start() t1.join() t2.join()

6、线程间共享全局变量
import time
from threading import Thread
全局变量
global_num = 0
任务函数:修改全局变量
def add_num():
global global_num for i in range(): global_num += 1
if name == “main”:
# 创建两个线程,同时修改全局变量 t1 = Thread(target=add_num) t2 = Thread(target=add_num) t1.start() t2.start() t1.join() t2.join() print(f"最终global_num的值:{global_num}")
import time
from threading import Thread, Lock
全局变量
global_num = 0
创建锁对象
lock = Lock()
任务函数:修改全局变量(加锁)
def add_num():
global global_num for i in range(): lock.acquire() # 上锁:获取锁,若锁被占用则阻塞 global_num += 1 lock.release() # 解锁:释放锁,让其他线程可以获取
if name == “main”:
t1 = Thread(target=add_num) t2 = Thread(target=add_num) t1.start() t2.start() t1.join() t2.join() print(f"最终global_num的值:{global_num}") # 输出
1、关系对比
- 一个进程可以包含多个线程,线程是进程的组成部分;
- 线程共享进程的内存空间和系统资源,进程之间资源独立;
- 进程是操作系统资源分配的基本单位,线程是操作系统调度的基本单位;
- 进程退出时,其内部的所有线程都会被强制终止;线程退出不会影响其他线程和进程。
2、区别对比
3、优缺点对比
进程的优缺点
- 优点:稳定性高,资源独立,无线程安全问题;适合CPU密集型任务(多进程可实现并行执行);
- 缺点:创建/切换开销大,通信复杂,资源消耗多。
线程的优缺点
- 优点:创建/切换开销小,通信高效,资源消耗少;适合IO密集型任务;
- 缺点:存在线程安全问题,稳定性低,一个线程崩溃可能导致整个进程崩溃。
1、协程的定义与优势
协程的定义
协程的核心优势
和进程、线程比,协程的优势主要在成本、效率、安全性这几个方面,也是它能成为处理等待类任务首选的原因:
- 切换成本特别低:协程的切换是 Python 程序内部控制的,不用操作系统参与,不用保存和恢复进程、线程的运行信息,切换一次的成本只有线程的几百分之一。
- 占用资源极少:所有协程共享一个线程的内存和资源,创建几千个协程的资源消耗,还比不上创建几十个线程的消耗。
- 不用考虑数据安全问题:协程是单线程里串行执行的,同一时间只有一个协程在运行,不会出现多个任务同时修改一个数据的情况,不用像多线程那样加锁保护数据,代码写起来更简单。
- 处理等待类任务效率极高:等待类任务的大部分时间,CPU 都是闲着的,协程通过在等待时切换任务,让 CPU 全程不空闲,处理同样的任务,效率比多线程高很多。
- 代码逻辑更清晰:现在的协程语法和普通的同步代码逻辑差不多,不会出现多线程里嵌套回调函数的复杂情况。
2、协程适用场景
3、Python 协程的实现方式
1、基础语法
import asyncio
定义⼀个异步协程函数
async def my_coroutine():
""" ⼀个简单的协程函数,⽤于演示asyncio库的基本使⽤。 此函数没有参数和返回值。 """ print("Start") await asyncio.sleep(1) # ⾮阻塞式休眠 print("End")
运⾏协程
asyncio.run(my_coroutine()) # Python3.7+推荐⽅式
2、事件循环与任务创建
import asyncio 定义⼀个异步任务,该任务会在指定延迟后完成
async def task(name, delay):
""" 异步任务函数,模拟⼀个需要时间完成的任务。 参数: name: 任务名称,⽤于标识任务。 delay: 延迟时间,任务完成前需要等待的时间(秒)。 """ await asyncio.sleep(delay) # 模拟任务耗时 print(f"{name} completed")
定义主协程,⽤于管理和执⾏其他协程任务
async def main():
""" 主协程函数,负责调度和执⾏多个异步任务。 """ # 创建任务列表 tasks = [ asyncio.create_task(task("A", 2)), # 创建任务A,延迟2秒完成 asyncio.create_task(task("B", 1)) # 创建任务B,延迟1秒完成 ] await asyncio.gather(*tasks) # 并发执⾏所有任务
运⾏主协程
asyncio.run(main())

import asyncio # 导入官方的asyncio模块 用async def定义协程函数
async def simple_coro():
print("协程开始运行了") # await后面跟asyncio.sleep,这是异步的休眠,模拟等待操作 await asyncio.sleep(1) # 不能用time.sleep,会阻塞整个线程 print("协程运行完成了")
if name == “main”:
# 1. 调用协程函数,得到协程对象,不会执行函数体 coro = simple_coro() print("调用协程函数后,还没执行函数里的代码") # 2. 用asyncio.run()运行协程对象,这是协程的入口函数 asyncio.run(coro)

5、协程爬虫(结合 aiohttp )
爬虫是协程最经典的实战场景,因为爬虫的核心耗时不是代码计算,而是网络请求的等待:向网站服务器发送请求后,需要等服务器返回网页数据,这个过程中 CPU 一直是空闲的。协程可以在等待一个请求的同时,发送其他的请求,实现单线程批量爬取,效率比普通的同步爬虫高很多。
import aiohttp
import asyncio import time
异步爬取协程函数
async def async_crawl(url):
print(f"开始爬取:{url}")
# 创建异步会话对象,类似requests的session,推荐复用,减少资源消耗
async with aiohttp.ClientSession() as session:
# 异步发起GET请求,await等待请求完成
async with session.get(url) as response:
# 异步获取网页源码,await等待结果
html = await response.text()
print(f"爬取完成:{url},状态码:{response.status}")
return len(html)
async def main():
# 待爬取的网址 urls = [ "https://www.baidu.com", "https://www..com", "https://www.163.com" ] # 批量创建协程对象 coros = [async_crawl(url) for url in urls] # 批量并发执行,获取所有结果 results = await asyncio.gather(*coros) return results
if name == “main”:
start_time = time.time() # 运行协程并获取结果 results = asyncio.run(main()) print(f"各网站源码长度:{results}") print(f"协程爬虫总耗时:{time.time() - start_time:.2f}秒")

6、协程爬虫综合应用
目的:爬取网站上所有有关于迪丽热巴的图片
import requests
正则表达式模块
import re import aiohttp import aiofiles import asyncio
async def aio_main():
async with aiohttp.ClientSession() as session: headers = cookies = { "BDUSS_BFESS": "ZlRzhySTJzblU0b3hLU1RZMkZRMFhlcjNqUUdsS1dDYXBvZHl3RXVVWUdGVk5tSUFBQUFBJCQAAAAAAQAAAAEAAACPU~OKTEZNd29vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaIK2YGiCtmR3", "BAIDUID": "7F4CD7DD1ECBFC1532ADF5E63:FG=1", "PSTM": "", "BIDUPSID": "A28FE7D64A787D28990CDC0D49C7ECF1", "BAIDUID_BFESS": "7F4CD7DD1ECBFC1532ADF5E63:FG=1", "H_WISE_SIDS_BFESS": "____________________________________________________________________________________________", "ZFY": "wqTIA3wcBK:AoNdvIrDPioyXJA1gkGQ6JJU4Pe:AopmEM:C", "H_PS_PSSID": "64006_66586_66594_66674_66684_66805_66852_67003_67043_67050_67094_67051_67044_67119_67149_66953_67152_67162_67170_67180_67210_67229", "BA_HECTOR": "ag818g2h242la0a0ag2g2g821klmc8526", "BDORZ": "FFFB88EA3F8A630C64834BD6D0", "BDRCVFR[qPHJWzYt5q0]": "mk3SLVN4HKm", "delPer": "0", "PSINO": "6", "BCLID": "", "BCLID_BFESS": "", "BDSFRCVID": "rEDOJexroGWmTkTESnvsuOTVSmKK0gOTDYrE7z5SvfFPejIVvNwYEG0Pt8XCmL8hQ5mfogKKLmOTHPkF_2uxOjjg8UtVJeC6EG0Ptf8g0M5", "BDSFRCVID_BFESS": "rEDOJexroGWmTkTESnvsuOTVSmKK0gOTDYrE7z5SvfFPejIVvNwYEG0Pt8XCmL8hQ5mfogKKLmOTHPkF_2uxOjjg8UtVJeC6EG0Ptf8g0M5", "H_BDCLCKID_SF": "JRFD_KLMJID3HnRY-P4_-tAt2qoXetJyaR3HKlbvWJ5WqR7j3PcxMp0k0UTqbbQ9QNcgWlvctn3cShbXKx_b26L95xjlQPQBWGcq2t3w3l02V--9WMbVX5kJ0HoLKtRMW23rWl7mWUJOsxA45J7cM4IseboJLfT-0bc4KKJxbnLWeIJEjj6jK4JKDH0etT5P", "H_BDCLCKID_SF_BFESS": "JRFD_KLMJID3HnRY-P4_-tAt2qoXetJyaR3HKlbvWJ5WqR7j3PcxMp0k0UTqbbQ9QNcgWlvctn3cShbXKx_b26L95xjlQPQBWGcq2t3w3l02V--9WMbVX5kJ0HoLKtRMW23rWl7mWUJOsxA45J7cM4IseboJLfT-0bc4KKJxbnLWeIJEjj6jK4JKDH0etT5P", "H_WISE_SIDS": "66586_66594_66674_66684_66852_67003_67050_67094_67051_67119_67149_66953_67152_67162_67170_67180_67210_67229", "ab_sr": "1.0.1_NGNjOGE2NDc4MTczNTk4M2VmM2U4Y2RkYjQwZWI3MmU0NDM4NmQyMDc0ZDRmYzk2Yzg1ZWE3ZjhhNGUxZGE3ZDU5NTQ2NDkxZDNlNzdjYzBjZjQzMzNlMjY0MmMyYmFjNjBiYjRmMGJkMThlZTg0MDA3OTE5MTI4ZmM3Mjc0YzJiMmJkNjQwZjk0NzA2MDI3OGZkN2RjNmE3MDkxMjhkYg==" } url = "https://image.baidu.com/search/index" params = { "tn": "baiduimage", "ps": "1", "ct": "", "lm": "-1", "cl": "2", "nc": "1", "ie": "utf-8", "lid": "ee58b5a000014496", "dyTabStr": "MTIsMCwzLDEsMiwxMyw3LDYsNSw5", "word": "迪丽热巴" } async with session.get(url, headers=headers, params=params) as response: response_text = await response.text() """ 问题: 1、如何从源代码中提取到所有图片的链接 2、如何将所有图片路径中的/u0026改为& https://img2.baidu.com/it/u=,&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750 https://img2.baidu.com/it/u=,/u0026fm=253/u0026fmt=auto/u0026app=138/u0026f=JPEG?w=500 &h=750 """ img_src = re.findall('"thumburl":"(.*?)"', response_text) list_data = [] for i in img_src: new_url = i.replace(r"&", '&') # res = requests.get(new_url) a = await down_load(session, new_url) list_data.append(a) # index = 1 # for i in img_src: # new_url = i.replace(r"&", '&') # res = requests.get(new_url) # with open(f"img/{index}.png", "wb") as f: # f.write(res.content) # index += 1
index = 1
async def down_load(session, url):
global index async with session.get(url) as res: context = await res.read() async with aiofiles.open(f"img/{index}.png", "wb") as f: await f.write(context) print(f"{index}下载完成") index += 1
if name == “main”:
asyncio.run(aio_main())


S结语
进程、线程、协程是 Python 应对不同场景的多任务工具:进程适合 CPU 密集型任务,线程适配 IO 密集型场景,协程则是单线程下的异步效率利器。掌握它们的特性与适用场景,既能让程序更高效,也能理解现代编程中 “并发” 的核心逻辑。多敲代码、多做实战,你会更精准地选择合适的多任务方案。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/260076.html