【Py】关于aiohttp的一段示例代码

本文对比了使用Python的Requests库和aiohttp库进行同步与异步网页抓取的效率。通过具体示例展示了异步请求如何显著提高批量网页抓取的速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

"""Comparison of fetching web pages sequentially vs. asynchronously
Requirements: Python 3.5+, Requests, aiohttp, cchardet
For a walkthrough see this blog post:
http://mahugh.com/2017/05/23/http-requests-asyncio-aiohttp-vs-requests/
"""
import asyncio
from timeit import default_timer

from aiohttp import ClientSession
import requests

def demo_sequential(urls):
    """Fetch list of web pages sequentially."""
    start_time = default_timer()
    for url in urls:
        start_time_url = default_timer()
        _ = requests.get(url)
        elapsed = default_timer() - start_time_url
        print('{0:30}{1:5.2f} {2}'.format(url, elapsed, asterisks(elapsed)))
    tot_elapsed = default_timer() - start_time
    print(' TOTAL SECONDS: '.rjust(30, '-') + '{0:5.2f} {1}'. \
        format(tot_elapsed, asterisks(tot_elapsed)) + '\n')

def demo_async(urls):
    """Fetch list of web pages asynchronously."""
    start_time = default_timer()

    loop = asyncio.get_event_loop() # event loop
    future = asyncio.ensure_future(fetch_all(urls)) # tasks to do
    loop.run_until_complete(future) # loop until done

    tot_elapsed = default_timer() - start_time
    print(' WITH ASYNCIO: '.rjust(30, '-') + '{0:5.2f} {1}'. \
        format(tot_elapsed, asterisks(tot_elapsed)))

async def fetch_all(urls):
    """Launch requests for all web pages."""
    tasks = []
    fetch.start_time = dict() # dictionary of start times for each url
    async with ClientSession() as session:
        for url in urls:
            task = asyncio.ensure_future(fetch(url, session))
            tasks.append(task) # create list of tasks
        _ = await asyncio.gather(*tasks) # gather task responses

async def fetch(url, session):
    """Fetch a url, using specified ClientSession."""
    fetch.start_time[url] = default_timer()
    async with session.get(url) as response:
        resp = await response.read()
        elapsed = default_timer() - fetch.start_time[url]
        print('{0:30}{1:5.2f} {2}'.format(url, elapsed, asterisks(elapsed)))
        return resp

def asterisks(num):
    """Returns a string of asterisks reflecting the magnitude of a number."""
    return int(num*10)*'*'

if __name__ == '__main__':
    URL_LIST = ['https://facebook.com',
                'https://github.com',
                'https://google.com',
                'https://microsoft.com',
                'https://yahoo.com']
    demo_sequential(URL_LIST)
    demo_async(URL_LIST)
https://facebook.com           1.45 **************
https://github.com             0.51 *****
https://google.com             1.14 ***********
https://microsoft.com          1.43 **************
https://yahoo.com              2.07 ********************
-------------- TOTAL SECONDS:  6.60 ******************************************************************

https://github.com             0.53 *****
https://microsoft.com          1.05 **********
https://facebook.com           1.07 **********
https://google.com             1.15 ***********
https://yahoo.com              1.92 *******************
--------------- WITH ASYNCIO:  1.93 *******************

参考:https://gist.github.com/dmahugh/b043ecbc4c61920aa685e0febbabb959

<think>好的,我现在需要解决用户在使用aiohttp库时遇到的ClientOSError,具体是WinError 121信号灯超时的问题。首先,我得回忆一下这个错误通常是什么原因引起的。根据用户提供的引用,引用[2]提到Boost的Asio库出现类似错误是因为TCP连接在无数据交互时被断开,而引用[3]中的管道读写错误121也涉及到超时问题。这说明这个错误可能和网络连接超时或系统层面的设置有关。 首先,用户的环境是Windows,所以需要考虑系统对TCP连接的限制。比如,可能由于防火墙、NAT或者路由器设置导致连接被中断。另外,aiohttp是基于异步IO的,可能在长时间没有数据传输时,连接被操作系统或中间设备关闭。这时候,心跳机制可能是解决方案之一,用来维持连接的活跃状态。 接下来,我需要检查用户提供的代码示例是否正确处理了超时和心跳。根据引用[4]中的示例代码,用户可能在使用aiohttp.ClientSession时没有设置超时参数,或者没有实现心跳机制。因此,解决方案可能包括设置合理的超时时间、添加心跳包,或者调整系统TCP保持活动的参数。 另外,引用[2]提到,如果在非稳定网络环境下(如GPRS),心跳是必要的。虽然用户的环境是Windows服务器,但可能由于网络配置问题,同样需要心跳。因此,在aiohttp中实现定期发送心跳包可以维持连接,避免超时。 然后,考虑系统层面的TCP Keep-Alive设置。Windows默认的Keep-Alive时间可能较长,导致在应用层面未及时检测到断开。可以通过修改注册表或代码中设置SO_KEEPALIVE选项来调整。 此外,用户可能没有正确处理重连机制。当连接意外断开时,代码应该能够自动重连,而不是直接报错。这需要在异常处理中添加重试逻辑。 最后,要检查是否有防火墙或安全软件拦截了连接,导致超时。用户可能需要暂时关闭防火墙进行测试,或者添加相应的例外规则。 总结可能的解决方案步骤:1. 在aiohttp中设置心跳机制;2. 调整超时参数;3. 修改系统TCP Keep-Alive设置;4. 添加重连机制;5. 检查防火墙设置。需要逐一验证这些方法,找到最适合用户情况的方案。</think>### 解决aiohttp ClientOSError WinError 121信号灯超时问题的分步方案 #### 1. 添加TCP心跳机制 在aiohttp客户端中启用TCP Keep-Alive并设置心跳包,维持连接活跃状态: ```python from aiohttp import ClientSession, TCPConnector async def fetch(url): connector = TCPConnector(keepalive_timeout=30) # 每30秒发送心跳 async with ClientSession(connector=connector) as session: async with session.get(url, timeout=60) as response: return await response.text() ``` #### 2. 显式设置超时参数 通过`timeout`参数控制请求各阶段超时阈值: ```python from aiohttp import ClientTimeout timeout = ClientTimeout( total=300, # 总超时5分钟 connect=20, # 连接超时20秒 sock_connect=15, # socket连接超时15秒 sock_read=30 # 数据读取超时30秒 ) ``` #### 3. 操作系统TCP参数调优 修改Windows注册表调整TCP Keep-Alive参数(需管理员权限): 1. 打开注册表:`Win+R`输入`regedit` 2. 导航路径:`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters` 3. 新建DWORD值: - `KeepAliveTime` = 30000 (30秒) - `KeepAliveInterval` = 5000 (5秒) - `TcpMaxDataRetransmissions` = 10 #### 4. 实现自动重连机制 添加指数退避重试策略: ```python import asyncio from aiohttp import ClientError async def fetch_with_retry(url, max_retries=3): for attempt in range(max_retries): try: async with ClientSession() as session: async with session.get(url) as response: return await response.text() except (ClientError, asyncio.TimeoutError) as e: if attempt == max_retries - 1: raise wait = 2 ** attempt # 指数退避 await asyncio.sleep(wait) ``` #### 5. 网络环境检查 - 使用`telnet <host> <port>`测试基础连通性 - 临时禁用防火墙测试:`netsh advfirewall set allprofiles state off` - 检查路由器/NAT超时设置(企业网络通常为5-30分钟) #### 6. 连接池优化配置 ```python connector = TCPConnector( limit=50, # 最大并发连接数 limit_per_host=10, # 单主机最大连接 enable_cleanup_closed=True # 自动清理关闭连接 ) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值