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

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

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

在昨天的文章《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 即可。这样就是实现了,列表页和详情页同时请求。速度大大提升。

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

目录
相关文章
|
缓存 前端开发 中间件
[go 面试] 前端请求到后端API的中间件流程解析
[go 面试] 前端请求到后端API的中间件流程解析
|
6月前
|
消息中间件 JavaScript 前端开发
最细最有条理解析:事件循环(消息循环)是什么?为什么JS需要异步
度一教育的袁进老师谈到他的理解:单线程是异步产生的原因,事件循环是异步的实现方式。 本质是因为渲染进程因为计算机图形学的限制,只能是单线程。所以需要“异步”这个技术思想来解决页面阻塞的问题,而“事件循环”是实现“异步”这个技术思想的最主要的技术手段。 但事件循环并不是全部的技术手段,比如Promise,虽然受事件循环管理,但是如果没有事件循环,单一Promise依然能实现异步不是吗? 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您
|
9月前
|
XML JSON JavaScript
HttpGet 请求的响应处理:获取和解析数据
HttpGet 请求的响应处理:获取和解析数据
|
10月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
939 2
|
11月前
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
深入探索:Python中的并发编程新纪元——协程与异步函数解析
116 3
|
11月前
|
前端开发 JavaScript UED
axios取消请求CancelToken的原理解析及用法示例
axios取消请求CancelToken的原理解析及用法示例
734 0
|
数据采集
深度解析CancellationToken在HttpClient请求中的应用
本文讨论了在.NET环境中使用HttpClient进行爬虫开发时,如何应用CancellationToken来控制请求的生命周期,提高爬虫的效率和稳定性。通过结合爬虫代理IP技术、多线程请求、设置User-Agent和Cookie等策略,可以增强爬虫的灵活性并降低被网站封禁的风险。文章提供了一个使用CancellationToken和代理IP的多线程爬虫实现示例代码,并详细解析了代码的关键部分,包括CancellationToken的使用、代理IP的配置、并发请求的实现以及User-Agent和Cookie的设置。
187 0
深度解析CancellationToken在HttpClient请求中的应用
|
开发者 Python
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
432 1
|
负载均衡 Java API
深度解析SpringCloud微服务跨域联动:RestTemplate如何驾驭HTTP请求,打造无缝远程通信桥梁
【8月更文挑战第3天】踏入Spring Cloud的微服务世界,服务间的通信至关重要。RestTemplate作为Spring框架的同步客户端工具,以其简便性成为HTTP通信的首选。本文将介绍如何在Spring Cloud环境中运用RestTemplate实现跨服务调用,从配置到实战代码,再到注意事项如错误处理、服务发现与负载均衡策略,帮助你构建高效稳定的微服务系统。
303 2
|
存储 JSON API
Python编程:解析HTTP请求返回的JSON数据
使用Python处理HTTP请求和解析JSON数据既直接又高效。`requests`库的简洁性和强大功能使得发送请求、接收和解析响应变得异常简单。以上步骤和示例提供了一个基础的框架,可以根据你的具体需求进行调整和扩展。通过合适的异常处理,你的代码将更加健壮和可靠,为用户提供更加流畅的体验。
614 0

推荐镜像

更多
  • DNS