DrissionPage 入门教程
多线程与并发
本教程共 42 篇 · 第 39 篇 · 更新于 2026-07-29 · 约 6 分钟阅读
DrissionPageDrissionPage 入门教程多线程并发线程池threading
39. 多线程与并发
本节目标:学会用多线程同时处理多个页面,搞懂线程安全和性能优化的基本套路。
为什么要多线程
单线程爬取,一次只能处理一个页面。
网络请求大部分时间在等,CPU 闲着也是浪费。多线程可以同时处理多个页面,效率直接翻倍。
但多线程也有坑。资源共享、线程安全都要注意。
基本多线程模式
每个线程创建自己的页面对象:
from DrissionPage import ChromiumPage
import threading
import queue
# 待爬取的 URL 队列
url_queue = queue.Queue()
for i in range(1, 11):
url_queue.put(f'https://example.com/page/{i}')
def worker():
page = ChromiumPage()
while not url_queue.empty():
try:
url = url_queue.get(timeout=1)
page.get(url)
title = page('tag:h1').text
print(f'{url}: {title}')
url_queue.task_done()
except queue.Empty:
break
page.quit()
# 启动 4 个线程
threads = []
for _ in range(4):
t = threading.Thread(target=worker)
t.start()
threads.append(t)
for t in threads:
t.join()
使用线程池
concurrent.futures 更简洁:
from DrissionPage import ChromiumPage
from concurrent.futures import ThreadPoolExecutor
urls = [f'https://example.com/page/{i}' for i in range(1, 11)]
def crawl(url):
page = ChromiumPage()
page.get(url)
title = page('tag:h1').text
page.quit()
return {'url': url, 'title': title}
# 4 个线程并行
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(crawl, urls))
for r in results:
print(r)
线程安全写入
多线程写同一个文件会乱。
用锁来保护:
import threading
file_lock = threading.Lock()
def crawl(url):
page = ChromiumPage()
page.get(url)
title = page('tag:h1').text
page.quit()
# 加锁写入
with file_lock:
with open('results.txt', 'a', encoding='utf-8') as f:
f.write(f'{url}: {title}\n')
多标签页 vs 多线程
两者都能”同时”处理多个页面,但机制不同。
多标签页:共享浏览器实例,共享 cookies 和会话。适合需要登录状态的场景。
多线程:每个线程独立浏览器,真正并行。适合彼此独立的任务。
# 多标签页示例
page = ChromiumPage()
tabs = [page.new_tab(url) for url in urls[:5]]
for tab in tabs:
page.to_tab(tab)
print(page('tag:h1').text)
性能优化建议
1. 控制线程数量
不是越多越好。
一般设成 CPU 核心数的 2-3 倍。太多反而会抢资源。
import os
# 根据 CPU 核心数设置
workers = min(os.cpu_count() * 2, 8)
2. 使用无头模式
不需要看浏览器界面时,用无头模式省资源:
from DrissionPage import ChromiumOptions
co = ChromiumOptions()
co.headless() # 无头模式
page = ChromiumPage(co)
3. 禁用图片加载
图片加载很耗资源,爬虫一般不需要:
co = ChromiumOptions()
co.no_imgs(True) # 禁用图片
page = ChromiumPage(co)
4. 设置合理超时
避免某个页面卡住拖慢整体:
co = ChromiumOptions()
co.set_timeouts(page_load=10, script=10)
page = ChromiumPage(co)
5. 及时释放资源
用完就关,别让浏览器实例堆积:
def crawl(url):
page = ChromiumPage()
try:
page.get(url)
return page('tag:h1').text
finally:
page.quit() # 确保关闭
多线程 + 多标签页结合
每个线程管几个标签页,兼顾并行和资源共享:
def worker(url_group):
page = ChromiumPage()
tabs = []
for i, url in enumerate(url_group):
if i == 0:
page.get(url)
tabs.append(page)
else:
tab = page.new_tab(url)
tabs.append(tab)
results = []
for tab in tabs:
page.to_tab(tab)
results.append(page('tag:h1').text)
page.quit()
return results
# 把 URL 分成 3 组
url_groups = [urls[i:i+3] for i in range(0, len(urls), 3)]
with ThreadPoolExecutor(max_workers=3) as executor:
all_results = list(executor.map(worker, url_groups))
注意事项
- 不要共享页面对象:ChromiumPage 实例不能跨线程使用
- 每个线程独立创建:在 worker 函数里创建和销毁页面对象
- 异常处理:一个线程崩了不该影响其他线程
- 资源限制:开太多浏览器会吃内存,量力而行
小结
多线程能大幅提升爬取效率。
核心要点:每个线程独立创建页面对象,用线程池管理,共享资源加锁保护。
无头模式、禁图片、设超时都是常用优化手段。