Ускорение парсера на питоне
Я решил переделать асинхронный парсер с нуля. Убрал всё что мешало, а именно bs4 и заменил его на lxml(Xpath). Работа моего парсера заключается в сборе данных с сайта animedia, но пока для теста я беру лишь заголовки h1. В чём проблема. Проблема в скорости этого парсера если мы возьмём 1 страницу он обработает её за 1 секунду, если возьмём 25 он начнёт тормозить и это при 25 страницах, а их 103! Помогите разобраться как ускорить парсер.
import asyncio
import aiohttp
import time
from lxml import html
async def download(url):
async with asyncio.Semaphore(3):#Более удачный 3
async with aiohttp.ClientSession() as session:
async with session.get(url) as r:
try:
tree = html.fromstring(await r.text())
title = tree.xpath("//article[@class='film-wr']/h1/text()")
print(title)
except Exception:
pass
async def getpage(url):
async with asyncio.Semaphore(2): #Более удачный 2
async with aiohttp.ClientSession() as session:
async with session.get(url) as r:
tasks = []
tree = html.fromstring(await r.text())
d = tree.xpath("//div[@class='c1-item']/a/@href")
for link in d:
if link.startswith("/"):
link = "https://amedia.online" + link
#print(link)
task = asyncio.create_task(download(link))
tasks.append(task)
await asyncio.gather(*tasks)
async def main():
t0 = time.monotonic()
tasks = []
for i in range(1,25):
url = "https://amedia.online/anime/page/"+str(i)
task = asyncio.create_task(getpage(url))
tasks.append(task)
await asyncio.gather(*tasks)
print("Время: "+str(time.monotonic() - t0))
if __name__ == "__main__":
asyncio.run(main())
Ответы (1 шт):
Есть несколько мест, которые снижают производительность:
В вашем коде вы создаете новый ClientSession для каждой страницы. Это может быть накладно, особенно если вы обрабатываете много страниц. Лучше создать один ClientSession и повторно использовать его для каждого запроса.
Если число страниц больше, чем количество запросов, которые можно выполнить одновременно, вы будете тратить время на ожидание выполнения запросов, что может снижать производительность. Вам может помочь увеличение количества семафоров и уменьшение количества страниц, обрабатываемых каждым заданием.
Использование asyncio.gather() может привести к накоплению большого количества объектов в памяти, особенно если вы обрабатываете много страниц. Лучше использовать итераторы async for, чтобы избежать накопления объектов в памяти.
Вот пример, как это можно сделать:
import asyncio
import aiohttp
import time
from lxml import html
async def download(session, url):
async with session.get(url) as r:
try:
tree = html.fromstring(await r.text())
title = tree.xpath("//article[@class='film-wr']/h1/text()")
print(title)
except Exception:
pass
async def getpage(session, url):
async with session.get(url) as r:
tree = html.fromstring(await r.text())
d = tree.xpath("//div[@class='c1-item']/a/@href")
tasks = []
for link in d:
if link.startswith("/"):
link = "https://amedia.online" + link
task = asyncio.create_task(download(session, link))
tasks.append(task)
await asyncio.gather(*tasks)
async def main():
t0 = time.monotonic()
async with aiohttp.ClientSession() as session:
tasks = []
for i in range(1, 25):
url = f"https://amedia.online/anime/page/{i}"
tasks.append(asyncio.create_task(getpage(session, url)))
await asyncio.gather(*tasks)
print("Время: " + str(time.monotonic() - t0))
if __name__ == "__main__":
asyncio.run(main())