异步思维——把请求与解析分开

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 异步思维——把请求与解析分开

摄影:产品经理切面薄而整齐,到底是用什么刀切的?

在昨天的文章《Callback ——从同步思维切换到异步思维》,我们举的例子似乎还不能很好地说明 Callback 的优势。今天我们再来看另外一个场景。

例如有下面这个代码片段:

async def parse(html):
    selector = fromstring(html)
    print('...解析 HTML 的数据...')
    next_page = selector.xpath('//a[@class="next_page"]/@href')
    if next_page:
        next_page_url = next_page[0]
        next_page_html = await request(next_page_url)
        await parse(next_page_html)

这种场景常常发生在需要翻页的时候,不同页面的处理逻辑是完全一样的,但是每次获取到了当前页面才能获取下一页。于是不少人使用递归的办法来解决问题。

如果页数非常多,那么你就会面临一个问题:超出最大递归深度,导致报错。

并且,在定义这个parse函数的时候,我使用了async def把它定义为一个异步函数。但实际上,解析 HTML 是一个 CPU 密集型的工作,它没有 IO 等待,根本就没有必要异步!但为了在里面调用await request(next_page_url),却必须要使用async def

为了解决这个问题,我们可以把递归改成循环。于是可能有人会这样写代码:

...无关代码...
url_list = ['初始 URL']
while url_list:
    url = url_list.pop()
    html = await request(url)
    data = parse(html)
    next_page_url = data['next_page_url']
    if next_page_url:
        url_list.append(next_page_url)

通过这种写法,parse函数可以直接定义成普通函数,并且每次只会调用1层,不会递归调用。这就同时解决了两个问题。

看到这里,大家可能发现了,实际上我们只有在涉及到 IO 请求的地方,才需要使用async/await。在解析网页的地方,只需要使用普通函数就可以了。

而对于aiohttp请求网页来说,它的逻辑非常简单,你告诉它urlheadersmethodbody。它返回源代码给你。它不需要关心你传入的这一批URL 是不是对应同一个类型的页面,甚至不需要关心你请求的是不是同一个网站!

在这种情况下,如果我们使用 Callback,那么优势就凸现出来了。我们来看下面这个例子:

def parse_1(html):
    print('处理页面1的源代码')
def parse_2(html):
    print('处理页面2的源代码')
def parse_3(html):
    print('处理另外一个网站的源代码')
class RequestObj:
    def __init__(self, url, headers=None, method='get', body=None, callback):
        self.url = url
        self.headers = headers
        self.method = method
        self.body = body
        self.callback = callback
async def request(req_obj):
    async with aiohttp.ClientSession() as session:
        if req_obj.method == 'get':
            resp = await session.get(req_obj.url, headers=req_obj.headers)
        else:
            resp = await session.post(req_obj.url, headers=req_obj.headers, json=req_obj.body)
        html = await resp.text(encoding='utf-8')
    req_obj.callback(html)
async main():
    req_obj_list = [
        RequestObj('页面1的 url', headers1, callback=parse_1),
        RequestObj('页面2的 url', headers2, method='post', body={'xx': 1}, callback=parse_2),
        RequstObj('另一个网站的 url', headers3, callback=parse_3)
    ]
    tasks = [request(x) for x in req_obj_list]
    await asyncio.gather(*tasks)

在这个代码片段中,不同的 parse 函数处理不同的 url。我们在创建 RequestObj 对象的时候,把不同的 parse 函数通过 callback 参数与 url 关联起来。那么下载器在请求完成 url 以后,要做的仅仅是调用这个 callback 函数。

这样一来,假设有一个网站,我们先访问列表页,然后从列表页中拿到每一个详情页的 URL 去访问详情页。列表页可以翻页,详情页也可以翻页。通过维护一个全局的队列,我们可以实现,列表页要翻页的时候,把RequestObj 对象放到队列中,详情页要翻页的时候,把 RequestObj 对象也放到队列中。而负责请求网站的代码,不关心它自己请求的是哪个页面,它只管请求,然后调用 callback 传入 html 即可。这样就是实现了,列表页和详情页同时请求。速度大大提升。

下一篇文章,我们来实现这个全局的队列。

目录
相关文章
|
3月前
|
缓存 前端开发 中间件
[go 面试] 前端请求到后端API的中间件流程解析
[go 面试] 前端请求到后端API的中间件流程解析
|
1月前
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
深入探索:Python中的并发编程新纪元——协程与异步函数解析
27 3
|
1月前
|
前端开发 JavaScript UED
axios取消请求CancelToken的原理解析及用法示例
axios取消请求CancelToken的原理解析及用法示例
94 0
|
3月前
|
数据采集
深度解析CancellationToken在HttpClient请求中的应用
本文讨论了在.NET环境中使用HttpClient进行爬虫开发时,如何应用CancellationToken来控制请求的生命周期,提高爬虫的效率和稳定性。通过结合爬虫代理IP技术、多线程请求、设置User-Agent和Cookie等策略,可以增强爬虫的灵活性并降低被网站封禁的风险。文章提供了一个使用CancellationToken和代理IP的多线程爬虫实现示例代码,并详细解析了代码的关键部分,包括CancellationToken的使用、代理IP的配置、并发请求的实现以及User-Agent和Cookie的设置。
深度解析CancellationToken在HttpClient请求中的应用
|
2月前
|
存储 JSON API
Python编程:解析HTTP请求返回的JSON数据
使用Python处理HTTP请求和解析JSON数据既直接又高效。`requests`库的简洁性和强大功能使得发送请求、接收和解析响应变得异常简单。以上步骤和示例提供了一个基础的框架,可以根据你的具体需求进行调整和扩展。通过合适的异常处理,你的代码将更加健壮和可靠,为用户提供更加流畅的体验。
170 0
|
3月前
|
开发者 Python
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
180 1
|
3月前
|
负载均衡 Java API
深度解析SpringCloud微服务跨域联动:RestTemplate如何驾驭HTTP请求,打造无缝远程通信桥梁
【8月更文挑战第3天】踏入Spring Cloud的微服务世界,服务间的通信至关重要。RestTemplate作为Spring框架的同步客户端工具,以其简便性成为HTTP通信的首选。本文将介绍如何在Spring Cloud环境中运用RestTemplate实现跨服务调用,从配置到实战代码,再到注意事项如错误处理、服务发现与负载均衡策略,帮助你构建高效稳定的微服务系统。
91 2
|
4月前
|
Web App开发 域名解析 JSON
HTTP 及 http 请求解析过程
HTTP 及 http 请求解析过程
59 4
|
4月前
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
【7月更文挑战第15天】Python 3.5+引入的协程和异步函数革新了并发编程。协程,轻量级线程,由程序控制切换,降低开销。异步函数是协程的高级形式,允许等待异步操作。通过`asyncio`库,如示例所示,能并发执行任务,提高I/O密集型任务效率,实现并发而非并行,优化CPU利用率。理解和掌握这些工具对于构建高效网络应用至关重要。
49 6
|
4月前
|
缓存 图形学 UED
U3D开发技术深度解析:异步场景加载与资源管理优化策略
【7月更文第11天】在Unity3D(简称U3D)游戏开发中,优化场景加载与资源管理是提升用户体验的关键一环。通过实现高效的异步场景加载和智能的资源管理策略,我们能显著缩短玩家的等待时间,提升游戏流畅度。本文将详细介绍这两种技术的应用,并提供实用的代码示例。
333 0

推荐镜像

更多
下一篇
无影云桌面