📖 知识点
1. 为什么需要并发调度?
创建多个 Page 后,如果按顺序一个个执行(先跑完 page1 再跑 page2),其实是串行,没提速。用线程(threading)并发,让多个任务同时跑,才真正并行。
串行:A 做完 → 才做 B(耗时相加) 并行:A、B 同时进行(耗时≈最慢那个)
2. 用线程并发调度
import threading
def task(page, url):
page.get(url)
# 执行采集逻辑
return page.title
# 创建线程,分别跑不同Page
t1 = threading.Thread(target=task, args=(page1, "https://www.baidu.com"))
t2 = threading.Thread(target=task, args=(page2, "https://www.bing.com"))
t1.start()
t2.start()
t1.join()
t2.join()
start()启动线程(并行开始),join()等待线程结束(确保所有任务完成再汇总)。
3. 结果汇总
线程各自采集后,把结果收集到共享列表:
results = []
def task(page, url):
page.get(url)
results.append(f"{url} → {page.title}") # 各线程把结果加进列表
主线程 join 所有线程后,results 里就汇总了所有 Page 的成果。
完整示例
import threading
from DrissionPage import ChromiumPage
page1 = ChromiumPage()
page2 = ChromiumPage()
results = [] # 汇总结果
def work(page, url):
page.get(url)
results.append(f"{url} → 标题:{page.title}")
# 并行合击:两个线程同时跑
t1 = threading.Thread(target=work, args=(page1, "https://www.baidu.com"))
t2 = threading.Thread(target=work, args=(page2, "https://www.bing.com"))
t1.start()
t2.start()
# 等待两个任务都完成
t1.join()
t2.join()
# 汇总输出
for item in results:
print("📦", item)
⚠️ 易错提醒
- 每个线程操作各自的 Page,别让两个线程抢同一个 Page
- 共享列表 results 在多线程下追加,注意线程安全(小任务量一般没问题)
- 记得
join(),否则主线程可能在子线程完成前就结束
⚔️ Kata 实战任务
任务需求:
- 导入 threading 和 ChromiumPage
- 创建两个 Page 对象
- 编写一个 work 函数:接收 page 和 url,访问后把结果存进共享列表
- 用两个线程分别跑两个 Page,并行访问百度、必应
- join 等待完成,汇总打印所有结果
参考代码
import threading
from DrissionPage import ChromiumPage
page1 = ChromiumPage()
page2 = ChromiumPage()
results = []
def work(page, url):
"""单个Page的采集任务"""
page.get(url)
results.append(f"{url} → 标题:{page.title}")
# 并行合击:两个线程同时施法
t1 = threading.Thread(target=work, args=(page1, "https://www.baidu.com"))
t2 = threading.Thread(target=work, args=(page2, "https://www.bing.com"))
t1.start()
t2.start()
# 等待全部完成
t1.join()
t2.join()
# 结果汇总
print("🎯 并行采集完成,共", len(results), "条结果")
for item in results:
print("📦", item)
运行输出(示意):
🎯 并行采集完成,共 2 条结果
📦 https://www.baidu.com → 标题:百度一下,你就知道
📦 https://www.bing.com → 标题:Bing
💡 观察:两个任务真正并行,整体耗时约等于最慢那一个,而不是两者相加。这就是并发的威力。
🧠 思考题
- 线程里
start()和join()分别做什么?不写 join 会有什么问题? - 如果多个线程要抓同一个网站的多个页面,该怎么分工(每个线程分几页)?相比单线程优势在哪?
行者孙Python