什么是 asyncio?
Scrapeless Scraping Browser 提供云浏览器执行,Python 应用程序可以在异步网页收集工作流中协调。
asyncio 是 Python 的标准库,用于编写使用 async 和 await 的并发代码。它通过事件循环协调协程、任务和异步 I/O。在爬虫应用中,asyncio 可以在程序跟踪每个操作何时完成的同时,重叠独立的网络等待。
asyncio 不是一个 HTTP 客户端或 HTML 解析器。您使用异步网络库来请求文档,并使用解析器提取其内容。asyncio 提供这些操作之间的协调。理解这个边界有助于解释它的有用性以及一个表面上异步的程序仍然顺序执行的常见原因。
asyncio 解决了什么问题?
asyncio 帮助程序在一个操作等待兼容 I/O 时在其他工作上取得进展。页面请求可以花时间等待连接或响应字节。如果应用程序在开始另一个独立请求之前不需要那种响应,那么这些等待时间可以重叠。
该 Python 异步 I/O 模型 提供网络操作、任务、子进程和同步的高级功能。库构建在这些功能之上,以暴露与事件循环协作的操作。应用程序仍然负责判断哪些工作是独立的以及要接受多少。
一个有用的例子是一组无关的公共文档页面。程序可以在另一个请求进行的同时等待一个页面。相关工作流的行为不同:如果下一个地址仅在当前响应中可用,那么该特定依赖关系将保持顺序。异步语法无法消除实际的数据依赖关系。
协程、任务和事件循环
协程描述一个异步操作,任务为协程的执行调度,而事件循环协调准备好的工作。调用协程函数会创建一个协程对象,而不是自动完成其主体。调用者必须等待它或者安排它作为任务运行。
该 Python 协程和任务生命周期 解释了调度、等待和完成如何相互作用。在一个事件循环线程中,任务运行直到它挂起或完成;然后其他准备好的工作可以继续。这是协作调度,因此占据循环而不让出的代码会延迟无关任务。
等待意味着当前协程依赖于可等待对象的结果。如果操作必须等待,控制可以返回事件循环。这并不是意味着“启动一个后台线程”,也不保证每次都会发生挂起。已经完成的操作可以立即继续。
在普通脚本的边界,asyncio.run 管理异步入口点。在已经拥有事件循环的主机内部,例如某些交互式环境或服务,使用该主机支持的异步集成,而不是试图启动嵌套循环。创建循环的组件也应拥有其生命周期。
并发与 CPU 并行不同
asyncio 协调重叠操作;它不会自动在多个核心上运行 CPU 密集型 Python 函数。长时间计算或同步解析器调用仍可能占用事件循环线程。网络部分可能是异步的,而本地处理仍然是瓶颈。
Python 的 阻塞事件循环工作指导 描述了为什么阻塞操作需要单独处理。如果一个依赖只暴露一个阻塞 I/O 接口,基于线程的桥接可能是合适的。CPU 密集型处理可能需要基于运行时、库和数据移动成本的不同执行策略。
在改变执行模型之前进行测量。如果请求大部分时间花在等待源上,重叠等待可能有帮助。如果每个响应触发一个占据循环的大转化,更多的调度下载可能只会增加内存压力。一个具有受控处理阶段的小活动集可以产生更可预测的完成。
| 工作量 | asyncio 的角色 | 额外决策 |
|---|---|---|
| 独立 HTTP 请求 | 协调重叠等待 | 选择一个异步客户端和源限制。 |
| 顺序分页依赖 | 等待每个必要的响应 | 识别依赖关系周围的任何独立工作。 |
| 大规模本地转化 | 协调周围的工作流 | 选择 CPU 工作应该执行的位置。 |
| 慢输出存储 | 等待一个兼容的写入器 | 界定在存储之前接受的积压。 |
为什么带有等待的循环仍然可以是顺序的
在创建下一个操作之前等待一个操作的循环会顺序处理那些操作。当顺序或数据依赖需要时,这可能是正确的设计。对于独立工作,并发需要在等待所有结果之前调度多个操作。
任务组为相关任务提供了一个范围,并在组退出时等待它们。它们还定义了失败如何影响兄弟任务。收集结果是另一个协调模式,但其失败行为与任务组并不相同。根据所有权和完成语义选择原语,而不仅仅是基于较短的示例。
保留对结果重要的工作的引用。没有所有者的操作可能会失败,而程序的其余部分无法考虑其结果。收集任务应具有输入身份、完成状态以及观察异常的地方。这些属性无论作业是否具有小的或大的活动集都很重要。
队列和信号量控制不同的资源
有界队列限制被接纳的等待消费者的工作,而信号量限制对受保护操作的并发访问。这些控制相互补充,但不可互换。网络调用周围的信号量可能会使大量预创建的任务在内存中等待。
该 asyncio 队列模型 可以使生产者在配置的队列满时等待。这会产生反压:当消费者无法跟上时,生产速度减慢。因此,基于工作者的收集器可以限制其活动操作和等待开始的工作。
在资源存在的地方应用限制。特定源的请求限制保护与该源的关系。浏览器会话限制保护浏览器容量。输出队列限制在存储比收集慢时保护内存。一个信号量环绕整个应用程序通常过于模糊,无法描述所有这些约束。
还要区分活动的并发与请求速率。少量非常快速的请求仍然可以产生频繁的流量。选择适合该源的活动工作限制和节奏。避免将任意的工作者数量作为每个收集的通用设置。
取消和关闭需要所有权
取消请求异步操作停止,关闭必须考虑该操作所占有的资源。被取消的任务仍然可能需要释放响应、关闭浏览器页面或记录未完成的输入。清理应属于操作的生命周期,而不是对进程退出将处理它的希望假设。
使用上下文管理的资源和显式的完成会计。如果任务拥有一个网络响应,即使处理停止,其清理也应该释放该响应。如果它拥有一个输出记录,则决定该记录是已提交还是仍未完成。取消不应默默地将未完成的项目转换为成功的空结果。
明确的关闭序列停止接受新输入,解决活动工作政策,并在依赖项完成后关闭共享客户端。确切的政策取决于应用程序:有些工作应该完成已接纳的项目,而其他工作应该及时停止。无论如何,运行报告应解释哪些仍未处理。
一个示例异步收集管道
异步收集管道可以协调发现、获取、验证和存储,同时保持每个积压的限制。想象一个经过批准的公共文档 URL 列表。生产者将这些地址提供给工作者;工作者获取文档并将接受的记录传递给输出阶段。
获取方法可以变化而不改变协调模型。一个异步 HTTP 客户端适合页面,其响应已经包含数据。 Scrapeless 爬虫浏览器 为动态页面提供浏览器执行。每个操作仍需要定义的输入、预期结果和资源范围。
该 爬虫浏览器介绍 解释浏览器服务,而对 Python 中的动态网站收集 的讨论提供了相关背景。渲染并没有消除任务限制或字段验证的需求;它改变了生成文档的获取阶段。
分别跟踪完成的记录、被拒绝的文档和未完成的输入。使用 Scrapeless 定价 在设计中评估浏览器资源。当调度更多任务时,应该通过更好的有效输出来证明,而不是通过显示为活动的操作数量。
结论
asyncio 为 Python 提供了一个明确的模型,用于协调并发 I/O。首先识别独立等待的地方,然后分配任务、队列和资源的明确所有者。结果程序应解释什么是活动的、什么是等待的以及什么是已完成的,即使操作失败或任务提前停止。
协调您的动态页面收集
使用 Scrapeless 爬虫浏览器作为页面执行层,并在您的 Python 应用程序中保持任务所有权、队列边界和输出验证。
今天注册并获得 $5 的免费积分 — 无需信用卡.
立即领取您的 $5 积分 →常见问题
问:asyncio 是 Python 的一部分吗?
asyncio 是 Python 标准库的一部分。它提供异步协调,而不是完整的 HTTP 爬虫堆栈。您可能仍然需要异步 HTTP 客户端、HTML 解析器或浏览器自动化库,具体取决于源如何公开其数据。
问:await 会启动一个新线程吗?
Await 不会启动新线程。它在协程模型中等待可等待对象,并可以允许事件循环在操作待处理时运行其他准备好的工作。线程执行是通过适当的 API 或库做出的单独选择。
问:为什么我的异步爬虫仍然一次只发送一个请求?
如果异步爬虫在安排下一个独立请求之前等待每个请求,它将保持顺序。如果操作可以重叠,仅在此处引入显式任务协调,并限制接纳的工作。在当前响应中发现的下一页地址等依赖项仍然保持顺序。
问:信号量是否阻止所有内存增长?
信号量限制访问它所保护的操作;它不会自动限制应用程序创建的任务或结果数量。当输入列表或响应量超过可用内存时,也要使用有界队列和受控输出缓冲。