Разбор большого количества HTML-файлов с помощью asyncio aiofiles и парсинг их в pandas DataFrame

У меня есть около 40 000 файлов HTML на диске и функция, которая анализирует HTML с помощью Beautiful Soup и возвращает словарь для каждого HTML. Во время чтения / разбора я добавляю все словари в список и в конце создаю pandas DataFrame.

Все работает нормально в синхронном режиме, но на запуск требуется много времени, поэтому я хочу работать с aiofiles.

В настоящее время мой код выглядит так:

# Function for fetching all ad info from single page
async def getFullAdSoup(soup):
     ...
     adFullFInfo = {} # dictionary parsed from Beautifoul soup object
    return await adFullFInfo


async def main():
    adExtendedDF = pd.DataFrame()
    adExtendednfo = {}
    htmls = glob.glob("HTML_directory" + "/*.html") # Get all HTML files from directory

    htmlTasks = [] # Holds list of returned dictionaries
    for html in natsorted(htmls):
        async with aiofiles.open(html, mode='r', encoding='UTF-8', errors='strict', buffering=1) as f:
            contents = await f.read()
            htmlTasks.append(getFullAdSoup(BeautifulSoup(contents, features="lxml")))
        htmlDicts = await asyncio.gather(*htmlTasks)
    adExtendedDF = pd.DataFrame(data=htmlDicts, ignore_index=True)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Я получаю следующую ошибку:

Файл "C:/Users/.../test.py", строка 208, в getFullAdSoup return await adFullFInfo TypeError: объект dict не может использоваться в выражении 'await'

Я нахожу здесь аналогичный вопрос, но я не могу заставить его работать. Я не знаю, как преобразовать мою функцию синтаксического анализа в асинхронный режим и как перебирать файлы, вызывающие эту функцию.

1 ответ

Ваша ошибка возникает из-за того, что вы ждете dict, я предполагаю, что вы неправильно поняли, вам не нужно ждать в операторе возврата, чтобы он был асинхронным. Я бы реорганизовал это так

# Function for fetching all ad info from single page
async def getFullAdSoup(soup):
     ...
     adFullFInfo = {} # dictionary parsed from Beautifoul soup object
    return adFullFInfo #*****1****


async def main():
    adExtendedDF = pd.DataFrame()
    adExtendednfo = {}
    htmls = glob.glob("HTML_directory" + "/*.html") # Get all HTML files from directory

    htmlTasks = [] # Holds list of returned dictionaries
    for html in natsorted(htmls):
        async with aiofiles.open(html, mode='r', encoding='UTF-8', errors='strict', buffering=1) as f:
            contents = await f.read()
            htmlTasks.append(asyncio.create_task( #****2****
                getFullAdSoup(BeautifulSoup(contents, features="lxml"))))
        await asyncio.sleep(0) #****3****
    htmlDicts = await asyncio.gather(*htmlTasks) #****4****
    adExtendedDF = pd.DataFrame(data=htmlDicts, ignore_index=True)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

4 изменения:

  1. Не нужно ждать диктата
  2. Используйте asyncio.create_task, чтобы запланировать запуск задачи как можно скорее
  3. sleep(0), чтобы освободить цикл событий и разрешить запуск задачи
  4. Переместите метод сбора за пределы цикла, чтобы вы могли собирать все задачи сразу, а не по одной.

2 и 3 не являются обязательными, но я считаю, что это сильно влияет на скорость в зависимости от того, что вы делаете.

Другие вопросы по тегам