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

异步网页爬虫与同步版本耗时相同的问题排查

问题分析与解决方案

你的异步代码没体现性能优势,核心原因是所有网络IO操作都是同步阻塞的,导致asyncio的事件循环无法并发执行任务,最终和同步版本的执行逻辑完全一致。

具体问题点:

  • 同步HTTP请求阻塞事件循环:get_html_data用的是requests.get,这是同步阻塞调用。在异步函数里执行这类同步IO时,整个事件循环会被挂起,直到请求完成,其他异步任务根本没机会执行,本质上还是串行运行。
  • 同步方法阻塞异步流程:__get_function_data是普通同步方法,内部调用了同步的get_html_data;而get_info虽然标记为async,但里面直接调用这个同步方法,同样会阻塞事件循环,无法实现任务切换。
  • (次要问题)data变量作用域错误:__get_functions里定义的data,在get_info里直接data.append会报错(除非是全局变量),这会导致数据无法正确收集。

修复方案:

  1. 替换为异步HTTP库:用aiohttp替代requests,实现异步网络请求。
  2. 改造IO相关方法为异步:
    • 把get_html_data改成异步方法,使用aiohttp.ClientSession发起请求。
    • 把__get_function_data改成异步方法,内部await异步的get_html_data。
    • 在get_info里await __get_function_data(href),确保异步流程不被阻塞。
  3. 修正数据收集的作用域问题:比如把data作为参数传递给get_info,或者用类的实例变量存储。

示例修正后的关键代码片段:

# 异步获取HTML文本
async def get_html_data(cls, url, session):
    async with session.get(url, headers=HEADERS) as response:
        return await response.content.read()

# 异步获取二级页面数据
async def __get_function_data(self, href, session):
    html = await self.get_html_data(href, session)
    soup = bs4.BeautifulSoup(html, self.PARSER_MODE)
    # 后续解析逻辑保持不变
    data = [self.check_each_info(soup, key) for key in [
        "FUNCTION_DESCRIPTION",
        "FUNCTION_SYNTAX",
        "VERSION",
        "ARGS",
        "RETURN"]]
    clean_data = [*map(self.get_clean_text, data)]
    return dict(zip(["desc", "syntax", "version", "args", "return"], clean_data))

# 异步处理单个条目
async def get_info(self, li, session, data):
    a = li.find("a")
    name = a.text       
    href = fr"https://examplewebsite.com/{a.get('href')}"
    info = await self.__get_function_data(href, session)
    data.append({"Name": name, **info})

# 主异步函数
async def __get_functions(self):
    data = []
    # 创建aiohttp会话(复用连接提升性能)
    async with aiohttp.ClientSession() as session:
        # 异步获取首页HTML
        html = await self.get_html_data(URL, session)
        soup = bs4.BeautifulSoup(html, self.PARSER_MODE)
        lists = soup.findAll(**HTML_TAGS["FUNCTION_LIST"])
        for ul in lists:
            li_items = ul.findAll("li")
            # 传递session和data到每个任务
            tasks = [asyncio.create_task(self.get_info(li, session, data)) for li in li_items]
            done, pending = await asyncio.wait(tasks)
    self.save_as_json(data)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 03:08:32