异步爬虫实践:使用Aiohttp加速抓取多个星座运势

小白学大数据
• 阅读 5

一、同步与异步:为何效率天差地别? 在开始代码之前,理解其背后的理念至关重要。 ● 同步爬虫(阻塞式): 程序发送一个HTTP请求后,会一直“傻等”直到服务器返回响应。在此期间,CPU资源被闲置。就像一个收银员一次只服务一位顾客,结账、装袋、收款,完成后才服务下一位。队伍长时,总等待时间非常可观。 ● 异步爬虫(非阻塞式): 程序发送一个请求后,不会原地等待,而是立刻去执行其他任务(例如发送下一个请求)。当某个请求的响应返回时,程序再回来处理它。这就像一个收银员同时服务多条队伍,一个顾客在掏钱时,他立刻为下一个顾客扫描商品,极大地提高了效率。 Python的 asyncio 库提供了构建异步程序的底层基础设施。而 Aiohttp 则是基于 asyncio 的,专门用于处理HTTP请求的库,它使得编写异步爬虫变得异常简单和高效。 二、项目实战:构建异步星座运势爬虫 我们的目标是并发地抓取一个假设的星座网站上的12个星座运势。

  1. 环境准备 首先,确保安装了必要的库。aiohttp 是核心,asyncio 是Python的内置库。我们还会使用 beautifulsoup4 来解析HTML。
  2. 目标分析 假设我们的目标URL结构为:https://example-astrology.com/aquarius.html。每个星座一个页面,我们可以构建一个URL列表。
  3. 代码实现 以下是完整的、带有详细注释的代码实现。
    import asyncio
    from bs4 import BeautifulSoup
    import time
    import logging
    

配置日志,方便观察异步执行过程

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(name)

目标星座列表和基础URL(此为示例,请替换为真实可用的URL)

ZODIAC_SIGNS = [ 'aries', 'taurus', 'gemini', 'cancer', 'leo', 'virgo', 'libra', 'scorpio', 'sagittarius', 'capricorn', 'aquarius', 'pisces' ] BASE_URL = "https://example-astrology.com/{sign}.html"

定义异步函数:获取并解析单个星座的页面

async def fetch_zodiac_forecast(session, sign): """ 使用传入的aiohttp session异步抓取单个星座的运势。

Args:
    session: aiohttp.ClientSession 实例,用于发起请求。
    sign: 星座名称字符串。

Returns:
    dict: 包含星座名称和运势文本的字典。
"""
url = BASE_URL.format(sign=sign)
try:
    logger.info(f"正在发起请求: {sign}")
    # 发起异步GET请求,with语句确保响应被正确关闭
    async with session.get(url) as response:
        # 确保请求成功,否则抛出异常
        response.raise_for_status()
        # 异步读取响应内容
        html = await response.text()

    # 使用BeautifulSoup同步解析HTML(注意:这里是同步操作)
    # 由于解析通常很快,对整体性能影响不大。如果解析极复杂,可考虑异步方式。
    soup = BeautifulSoup(html, 'html.parser')

    # 假设运势文本在一个 <div class='forecast'> 标签内
    # 请根据实际网页结构修改此选择器
    forecast_element = soup.find('div', class_='forecast')
    forecast_text = forecast_element.get_text(strip=True) if forecast_element else "运势未找到"

    logger.info(f"成功抓取: {sign}")
    return {
        'sign': sign,
        'forecast': forecast_text
    }

except aiohttp.ClientError as e:
    logger.error(f"请求星座 {sign} 时发生错误: {e}")
    return {
        'sign': sign,
        'forecast': f"抓取失败: {str(e)}"
    }
except Exception as e:
    logger.error(f"解析星座 {sign} 时发生未知错误: {e}")
    return {
        'sign': sign,
        'forecast': f"解析失败: {str(e)}"
    }

定义主异步函数:创建任务并并发执行

async def main(): """ 主函数,创建会话和所有抓取任务,并并发执行。 """ # 创建一个TCPConnector,可以限制总连接数和每台主机的连接数,避免对服务器造成过大压力 connector = aiohttp.TCPConnector(limit=10) # 限制同时连接数为10

# 创建一个ClientSession,所有请求共享这个session的连接池和其他资源
async with aiohttp.ClientSession(connector=connector) as session:
    # 为每个星座创建一个异步任务(Task)
    tasks = []
    for sign in ZODIAC_SIGNS:
        # 创建task对象,但此时尚未执行
        task = asyncio.create_task(fetch_zodiac_forecast(session, sign))
        tasks.append(task)

    logger.info(f"已创建 {len(tasks)} 个异步任务,开始并发执行...")

    # 使用 asyncio.gather 并发运行所有任务,并等待它们全部完成
    # gather会返回一个结果列表,顺序与tasks列表一致
    results = await asyncio.gather(*tasks)

    # 所有任务完成后,打印结果
    logger.info("所有任务已完成!以下是抓取结果:")
    print("\n" + "="*50)
    for result in results:
        print(f"【{result['sign'].upper()}】")
        print(f"运势:{result['forecast']}")
        print("-" * 30)

程序入口点

if name == "main": start_time = time.time() # 记录开始时间

# 获取或创建事件循环并运行主程序
# asyncio.run() 是Python 3.7+的推荐方式,它负责管理事件循环
asyncio.run(main())

end_time = time.time() # 记录结束时间
elapsed_time = end_time - start_time
print(f"\n>>> 总耗时: {elapsed_time:.2f} 秒 <<<")

``` 三、代码核心解析与最佳实践

  1. async with aiohttp.ClientSession(): ○ 这是整个异步爬虫的核心。ClientSession 是一个连接池,它会在内部复用TCP连接,而不是为每个请求都创建新的连接。这极大地减少了建立和断开连接的开销。 ○ TCPConnector(limit=10) 用于控制并发量,既保护了目标网站,也避免了我们自己的程序因连接数过多而出现问题。
  2. asyncio.create_task(): ○ 它将要运行的协程(fetch_zodiac_forecast)包装成一个 Task 对象,并立即将其加入事件循环等待调度。此时函数并不会马上执行,只是做好了准备。
  3. await asyncio.gather(tasks): ○ 这是并发执行的关键。gather 等待所有传入的 Task 完成,并收集它们的结果。tasks 是解包操作,将任务列表展开为多个参数。 ○ 所有任务在此处真正开始并发执行。当某个任务在等待网络响应时,事件循环会立即切换到其他就绪的任务。
  4. 错误处理: ○ 在异步环境中,一个任务的异常不会直接导致整个程序崩溃,但可能会被 gather 收集或抛出。因此,在每个独立的任务函数内部进行完善的 try...except 捕获至关重要,这确保了即使某个星座抓取失败,也不会影响其他任务的执行。
  5. 性能对比: ○ 你可以尝试将 fetch_zodiac_forecast 函数和主逻辑用 requests 库重写为同步版本。你会发现,异步版本的耗时接近于最慢的那个单个请求的耗时(例如,如果每个请求都耗时1秒,异步总耗时约1秒),而同步版本的总耗时则是所有请求耗时的总和(12秒)。 四、总结与拓展 通过本次实践,我们成功构建了一个高效、健壮的异步星座运势爬虫。Aiohttp 与 asyncio 的结合,将I/O密集型任务的性能提升了一个数量级。 在实际应用中,你还可以考虑以下拓展方向: ● 速率限制: 使用 asyncio.sleep() 在请求间加入延迟,做一个有道德的爬虫。 ● 代理IP池: 在 ClientSession 中集成代理IP,用于应对反爬策略。例如:https://www.16yun.cn/ ● 更复杂的解析: 如果解析成为瓶颈,可以将解析工作丢到线程池中执行,避免阻塞事件循环。 ● 数据存储: 将结果异步地写入数据库(如 aiomysql 用于MySQL,asyncpg 用于PostgreSQL)。
点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
8个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皮卡皮卡皮 皮卡皮卡皮
4年前
ajax
ajax定义:异步的JavaScript和XML是一种综合技术:运用了XMLHTTPRequest(xhr)和服务器交换数据,通过JavaScript局部渲染页面,从而实现异步的局部更新同步与异步同步代码按顺序执行,会阻塞代码执行(alert)异步不会阻塞代码XMLHTTPRequestxhrjsvarxhrnew
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
JS批量根据地址查询百度地图经纬度
因为百度的JSAPI是异步的,批量查询的时候会乱掉,就把每次请求变成同步的,等请求返回了之后才进行下一个地址的查询。设置一个全局flag来标识当前请求是否正常响应,并且不断的检查这个flag,如果正常响应,才进行下一次的请求,这样就不容易乱掉引入JS<scripttype"text/javascript"src"http://ap
Easter79 Easter79
4年前
SpringCloud 微服务 (十) 消息队列MQ 基础
壹之前学习了SpringCloudBus结合MQ,没有多学习MQ,本次学习相关内容,先了解异步,同步就不说了异步:客户端非阻塞进程,服务端响应可以是非即时的应用场景: ①通知类的服务发出去即可,无需回应; ②请求的异步响应就是客户端发送请求,服务端异步响应请求,客户端不会产生阻塞且是默认响应,但不会立刻送达;①②
Stella981 Stella981
4年前
Linux应急响应(二):捕捉短连接
0x00前言​短连接(shortconnnection)是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时,才去建立一个连接,数据发送完成后,则断开此连接,即每次连接只完成一项业务的发送。在系统维护中,一般很难去察觉,需要借助网络安全设备或者抓包分析,才能够去发现。0x01应急场景​
Easter79 Easter79
4年前
SwiftUI 跨组件数据传递
作者:Cyandev,iOS和MacOS开发者,目前就职于字节跳动0x00前言众所周知,SwiftUI的开发模式与React、Flutter非常相似,即都是声明式UI,由数据驱动(产生)视图,视图也会与数据自动保持同步,框架层会帮你处理“绑定”的问题。在声明式UI中不存在命令式地让一个视图变成xxx
异步爬虫实战:实际应用asyncio和aiohttp库构建异步爬虫
在网络爬虫的开发中,异步爬虫已经成为一种非常流行的技术。它能够充分利用计算机的资源,提高爬虫效率,并且能够处理大量的运算请求。Python中的asyncio和aiohttp库提供了强大的异步爬虫支持,使得开发者能够轻松构建高效的异步爬虫。什么是异动爬虫?为
小白学大数据 小白学大数据
3个月前
优化 Python 爬虫性能:异步爬取新浪财经大数据
一、同步爬虫的瓶颈传统的同步爬虫(如requestsBeautifulSoup)在请求网页时,必须等待服务器返回响应后才能继续下一个请求。这种阻塞式I/O操作在面对大量数据时存在以下问题:速度慢:每个请求必须串行执行,无法充分利用网络带宽。易被封禁:高频
小白学大数据
小白学大数据
Lv1
男 · 亿牛云 · python技术
宁为代码类弯腰,不为bug点提交!
文章
120
粉丝
5
获赞
18