首页 / DrissionPage 入门教程 / 多线程与并发

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))

注意事项

  1. 不要共享页面对象:ChromiumPage 实例不能跨线程使用
  2. 每个线程独立创建:在 worker 函数里创建和销毁页面对象
  3. 异常处理:一个线程崩了不该影响其他线程
  4. 资源限制:开太多浏览器会吃内存,量力而行

小结

多线程能大幅提升爬取效率。

核心要点:每个线程独立创建页面对象,用线程池管理,共享资源加锁保护。

无头模式、禁图片、设超时都是常用优化手段。