我在 中编写了一个脚本,asyncio**与aiohttp*库相关联,用于异步解析网站内容。我尝试在以下脚本中应用逻辑,就像在 中通常应用逻辑一样scrapy*。
asyncio
aiohttp
scrapy
但是,当我执行脚本时,它的行为就像同步库一样requests**。urllib.request**因此,它非常慢并且无法达到目的。
requests
urllib.request
我知道我可以通过定义变量中的所有下一页链接来解决这个问题*link*。但是,我使用现有脚本完成任务的方式是否正确?
link
在脚本中,processing_docs()函数的作用是收集不同帖子的所有链接,并将精炼的链接传递给函数,fetch_again()以从其目标页面获取标题。processing_docs()函数中应用了一种逻辑,它收集 next_page 链接并将其提供给fetch()函数以重复相同的操作This next_page call is making the script slower whereas we usually do the same in。scrapyand get expected performance.
processing_docs()
fetch_again()
fetch()
This next_page call is making the script slower whereas we usually do the same in
and get expected performance.
*我的问题是:如何才能实现相同的目标并且保持现有逻辑的完整性?*
import aiohttp import asyncio from lxml.html import fromstring from urllib.parse import urljoin link = "https://stackoverflow.com/questions/tagged/web-scraping" async def fetch(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() result = await processing_docs(session, text) return result async def processing_docs(session, html): tree = fromstring(html) titles = [urljoin(link,title.attrib['href']) for title in tree.cssselect(".summary .question-hyperlink")] for title in titles: await fetch_again(session,title) next_page = tree.cssselect("div.pager a[rel='next']") if next_page: page_link = urljoin(link,next_page[0].attrib['href']) await fetch(page_link) async def fetch_again(session,url): async with session.get(url) as response: text = await response.text() tree = fromstring(text) title = tree.cssselect("h1[itemprop='name'] a")[0].text print(title) if __name__ == '__main__': loop = asyncio.get_event_loop() l
使用 asyncio 的全部意义在于您可以同时(彼此并行)运行多个提取。让我们看看您的代码:
for title in titles: await fetch_again(session,title)
这部分意味着每个新fetch_again任务只有在等待(完成)前一个任务后才会开始。如果你这样做,是的,与使用同步方法没有区别。
fetch_again
要调用 asyncio 的所有功能,请使用以下命令同时启动多个提取操作asyncio.gather:
asyncio.gather
await asyncio.gather(*[ fetch_again(session,title) for title in titles ])
您会看到显著的加速。
您甚至可以继续并fetch同时开始下一页和fetch_again标题:
fetch
async def processing_docs(session, html): coros = [] tree = fromstring(html) # titles: titles = [ urljoin(link,title.attrib['href']) for title in tree.cssselect(".summary .question-hyperlink") ] for title in titles: coros.append( fetch_again(session,title) ) # next_page: next_page = tree.cssselect("div.pager a[rel='next']") if next_page: page_link = urljoin(link,next_page[0].attrib['href']) coros.append( fetch(page_link) ) # await: await asyncio.gather(*coros)
重要提示
虽然这种方法可以让你更快地完成工作,但您可能希望限制当时的并发请求数量,以避免在你的机器和服务器上使用大量资源。
您可以asyncio.Semaphore为此目的使用:
asyncio.Semaphore
semaphore = asyncio.Semaphore(10) async def fetch(url): async with semaphore: async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() result = await processing_docs(session, text) return result