You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中使用asyncio调度协程优化网页表格爬取问题

问题分析与解决

你的代码存在两个核心问题:

  1. asyncio.create_task()返回的是Future对象,必须通过await获取实际结果,直接传给pd.DataFrame()必然报错。
  2. Selenium的WebDriver API是完全同步阻塞的,driver.find_element()这类方法不是协程函数,用await和asyncio.gather()包装同步代码不仅无法实现异步并行,还会导致逻辑混乱。

要实现表格数据的高效抓取,以下是两种可行方案:

方案一:用原生支持异步的Playwright(推荐)

Playwright是新一代浏览器自动化工具,原生支持asyncio,能真正并行处理元素查询,性能远优于Selenium+asyncio的组合。

示例代码:

import asyncio
from playwright.async_api import async_playwright
import pandas as pd

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page()
        await page.goto("你的目标网页URL")
        
        # 等待表格加载完成
        await page.wait_for_selector('table tbody')
        
        # 异步批量获取行数据
        rows = await page.query_selector_all('table tbody tr')
        data = []
        for row in rows:
            cells = await row.query_selector_all('td')
            # 并行获取单元格文本
            row_data = await asyncio.gather(*[cell.text_content() for cell in cells])
            data.append(row_data)
        
        # 生成DataFrame
        names = ['A', 'B', 'C', 'D', 'E', 'F','G', 'H', 'I']
        df = pd.DataFrame(data, columns=names)
        print(df.head())
        await browser.close()

asyncio.run(main())

方案二:Selenium结合线程池并行抓取

如果必须使用Selenium,由于它不支持asyncio原生异步,可以用线程池并行处理每行抓取(注意:每个线程需使用独立的WebDriver实例,避免线程安全问题)。

示例代码:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import pandas as pd
from concurrent.futures import ThreadPoolExecutor

def fetch_row(driver_url, row_index, ncols):
    # 每个线程创建独立的driver实例
    driver = webdriver.Remote(command_executor=driver_url)
    try:
        # 一次性获取整行所有单元格,减少浏览器交互次数
        cells = driver.find_elements(By.XPATH, f'//table/tbody/tr[{row_index}]/td')
        return [cell.text for cell in cells]
    finally:
        driver.quit()

def main():
    # 先初始化driver获取表格行列数,再启动远程服务供线程复用
    driver = webdriver.Chrome()
    driver.get("你的目标网页URL")
    WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.XPATH, '//table/tbody')))
    
    nrows = len(driver.find_elements(By.XPATH, '//table/tbody/tr'))
    ncols = len(driver.find_elements(By.XPATH, '//table/tbody/tr[1]/td'))
    driver_url = driver.command_executor._url
    driver.quit()
    
    # 线程池并行抓取
    with ThreadPoolExecutor(max_workers=10) as executor:
        futures = [executor.submit(fetch_row, driver_url, i+1, ncols) for i in range(nrows)]
        data = [future.result() for future in futures]
    
    # 生成DataFrame
    names = ['A', 'B', 'C', 'D', 'E', 'F','G', 'H', 'I']
    df = pd.DataFrame(data, columns=names)
    print(df.head())

if __name__ == "__main__":
    main()

额外优化建议

  • 优先用批量查询(如find_elements一次性获取整行单元格)替代逐个单元格的XPATH查询,减少浏览器交互次数,大幅提升速度。
  • Selenium开启headless模式、禁用图片加载等配置,也能进一步缩短抓取时间。

内容的提问来源于stack exchange,提问作者Ivan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 11:18:28