Потери данных при асинхронном парсинге с использованием библиотеки Asyncio+aiohttp

Рейтинг: 0Ответов: 1Опубликовано: 27.03.2023

Я только начал вникать в асинхронку и еще плохо ориентируюсь в этом. Практикуюсь в написание парсера, возникла следующая проблема: При парсинге я сохраняю словари - {имя:цена} в общий список, на каждой странице 16 книг, я паршу 80 страниц, в результате длинна списка по окончании программы должна быть 1280. Проблема в том что парсер работает через раз, бывает одает пустой список, либо длинна меньше в 2-3 раза, тоесть есть потери данных, код прилагаю

from bs4 import BeautifulSoup as BS
import aiohttp
import asyncio
import time
from fake_useragent import UserAgent

URL = 'https://bashenka.by/60-katalog-brendy?p={}'
data = []
headers = {
    'user-agent': UserAgent().chrome
}

async def get_info_book(session, page):
    url = URL.format(page)

    async with session.get(url=url) as responce:
        html = await responce.text()
    
        soup = BS(html, 'html.parser')
    
        books = soup.find_all(class_='right-block')
    
        for book in books:
        
            name = book.find('h5').text.strip()
            price = soup.find(class_='price product-price').text
        
            data_dict = {name:price}
        
        data.append(data_dict)
print('Страница ', page)

async def main():
    async with aiohttp.ClientSession(headers=headers) as session:
        task_list = [asyncio.create_task(get_info_book(session, i)) for i in range(1, 80)]
        await asyncio.gather(*task_list)
    print(data)
    print(len(data))
        

start = time.time()
await main()
print(time.time()-start)

Ответы

▲ 0Принят

добавил параметр lock в функцию get_info_book, который является объектом asyncio.Lock. При каждой итерации цикла for мы захватываем блокировку и добавляем словарь data_dict в список data, чтобы избежать состояния гонки.

from bs4 import BeautifulSoup as BS
import aiohttp
import asyncio
import time
from fake_useragent import UserAgent

URL = 'https://bashenka.by/60-katalog-brendy?p={}'
data = []
headers = {
    'user-agent': UserAgent().chrome
}

async def get_info_book(session, page, lock):
    url = URL.format(page)

    async with session.get(url=url) as responce:
        html = await responce.text()
    
        soup = BS(html, 'html.parser')
    
        books = soup.find_all(class_='right-block')
    
        for book in books:
        
            name = book.find('h5').text.strip()
            price = soup.find(class_='price product-price').text
        
            data_dict = {name:price}
        
            async with lock:
                data.append(data_dict)
        
        print('Страница ', page)

async def main():
    lock = asyncio.Lock()

    async with aiohttp.ClientSession(headers=headers) as session:
        tasks = []
        for page in range(1, 81):
            task = asyncio.create_task(get_info_book(session, page, lock))
            tasks.append(task)

        await asyncio.gather(*tasks)

if __name__ == '__main__':
    start_time = time.monotonic()
    asyncio.run(main())
    end_time = time.monotonic()
    print('Elapsed time:', end_time - start_time)
    print('Data length:', len(data))