(十二)把独立详情改为异步采集
1. 分页依赖与详情独立性
下一页链接要读完当前目录才知道,所以发现目录仍然逐页推进。已经发现的六个详情彼此独立,可以用上一章的 AsyncClient 和 TaskGroup 重叠等待:
# 项目文件:demos/details_async.py
# 已知的独立详情先并发读取;完成次序可以不同,结果按传入任务排列。
import asyncio
import httpx
from crawlsite import running_site
from catalogcrawl.parsers import parse_detail
async def read_all(base_url):
async with httpx.AsyncClient(
base_url=base_url, trust_env=False, timeout=1
) as client:
async def read_one(key):
response = await client.get("/books/" + key)
response.raise_for_status()
return parse_detail(response.text, str(response.url))
async with asyncio.TaskGroup() as group:
tasks = [
group.create_task(read_one(key)) for key in ["b101", "b102", "b103"]
]
return [task.result().source_id for task in tasks]
if __name__ == "__main__":
with running_site() as site:
print(asyncio.run(read_all(site.base_url)))输出 ['b101', 'b102', 'b103']。等待响应时任务让出执行机会;响应到达后,同一个任务继续做状态检查和 HTML 解析。任务列表保留输入次序,最后沿该列表取得结果。
2. 分批安排任务并在调用层保存
完整项目每批建立两个详情任务,等本批收尾后逐项提交结果,再安排下一批。六本书共三批,内存里不用先建立所有详情的 Task。第二本即使先完成,也仍按本批输入顺序保存。
这里的同步解析和 SQLite 写入都在事件循环所在的线程执行。图中的并发发生在网络等待处;记录检查与提交继续保持明确次序。想让空闲工作任务立即接下一项,可以接上上一章的有界队列,再用单独的保存环节收集结果。