@Maxwell012

Как ускорить проект на Python?

хочу разогнать мой скрипт на более быструю скорость + найти ответы на некоторые вопросы. Также я буду очень рад ссылкам на какие-то источники которые могут мне помочь.
У меня есть проект написанный на Python который осуществляет запросы на разные домены. Доменов было около 10к.
Думаю будет правильно рассказать подробнее о моем проекте прежде чем, как углублюсь в вопросы. Мой проект достаточно прост, он состоит из 3 частей:
  1. Запрос на сайт и получения в ответе код страницы
  2. Далее я достаю нужные мне данные с кода
  3. В завершение я помещаю данные в бд

Теперь пройдусь по каждому пункту поподробнее:
  • Первая часть кода - Все URLs лежат в txt > я читаю файл, и создаю список асинхронных тасков которые я в итоге вызываю в gather > в каждом таске выполняется один запрос. Для более простого понимания прикреплю часть кода:
# Создания гейзера
async def create_gather(urls):
    tasks = []
    for url in urls:
        tasks.append(asyncio.create_task(main(url.strip())))

    await asyncio.gather(*tasks)

# Запрос
async def main(url):
    page = None
    try:
        async with aiohttp.ClientSession(timeout=timeout) as session:
            headers = {
                'User-Agent': UserAgent().chrome
            }
            response = await session.post(url, headers=headers)

        if response.status in [301, 302, 403, 404, 500, 504]:
            print(f'-----------------------{response.status}----------------------- {url}')
        else:
            page = await response.text()
    except UnicodeDecodeError:
        print(f"------ {url} -------- UnicodeDecodeError")
    except TimeoutError:
        print(f"------ {url} -------- TimeoutError")
    except Exception as ex:
        print(f'------ {url} --------\n{ex}')
    finally:
        return [page, url]

  • Вторая часть кода у меня синхронная, с помощью библиотеки bs4 я извлекаю мне нужные данные
  • Третья часть кода - использую Postgrersql, библиотеки psycopg2

В чем конкретно проблема, количество доменов выросло до 1м
Основной вопрос как я могу ускорить мой скрипт, я понимаю что я могу переписать на более низкоуровневый язык весь скрипт, но мне очень интересно как я могу на пайтоне добиться максимальной скорости, мне очень интересно как это делают гуру пайтона)

Вопросы которые меня также волнуют:
  • Если ли лучше библиотека чем aiohttp?
  • Какие бы вы использовали библиотеки место bs4 и psycopg2 для большей производительности?
  • И важный вопрос, как лучше всего считать кол-во страниц сайта?

  • Вопрос задан
  • 184 просмотра
Пригласить эксперта
Ответы на вопрос 1
@Jack444
Первое что бросается в глаза так это то что сессия создаётся для каждого запроса, в итоге питон очень много времени затрачивает на инициализацию и подготовку подключений, сессиию надо создать в create_gather и после передовать в main.

aiohttp под капотом имеет лимит в 100 tcp подключений в пуле, если на сервере ресурсов хватает то конечно хотелось бы иметь возможность держать хотя-бы 500 подключений на один воркер, ещё aiohttp получая ответ приводит заголовки в CMultiDict, лично у меня на тестах он работал в 20 раз медленее чем стандартный словарь

Я бы заменил aiohttp на httpcore, httpcore это минимальный клиентский интерфейс который используется в другой популярной библиотеке httpx.
httpcore работает с байтами, заголовки с ответа элементарно сплитятся и возвращаются в виде списка кортежей, можно задать хоть 1000 подключений в пуле, настроить keep-alive и слать запросы по протоколу HTTP/2.0, результат в разы быстрее чем aiohttp.

Создав большое количество подключений + задач столько же или в разы больше то скрипт начнёт подвисать на обработке цикла событий, чтобы снизить издержки стоит установить uvloop, он работает под linux.

Вместо bs4 я бы использовал parsel, по скоростям не могу сказать, чисто вкусовщина.
Если учитывать что у вас там милион доменов и они все разные то скорее всего вам нужны какие-то общие данные в виде тегов meta/title/h1 то быстрее будет написать свою функцию для анализа html.

psycopg нужно заменить на asyncpg он очень быстро преобразует python типы в типы postgresql.
asyncpg позволяет создать пул подключений к базе а после создать воркеров в количестве созданных подключений, каждый воркер должен прослушивать asyncio.Queue, в очередь можно сразу закидывать аргументы для asyncpg.
Все данные желательно кэшировать локально в dict, в словаре хоть миллион хоть 100 миллионов ключей, доступ к ним по методу .get() отрабатывается за доли микросекунды но потребление оперативной будет не малой. Пришедший и распарсенный ответ чекаем в локальном кэше, если данные изменились то отправляем задачу в очередь на обновление, если нет такого ключа то аппем куда-то значения для одного запроса INSERT с множеством VALUES, для обработки INSERT отдельную задачу надо сделать чтобы чекать раз в несколько секунд VALUES и если они есть то генерить SQL и отправлять в очередь.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы