Руководство по распределённому веб-краулингу: система и архитектура

Руководство по распределённому веб-краулингу: система и архитектура

В этой статье я покажу, как построить базовую распределённую систему веб-краулинга с помощью Python, Celery и Redis. Я покажу, как настроить архитектуру, распределить задачи между несколькими узлами и обеспечить эффективную работу краулера. В результате у вас будет масштабируемое решение для сбора данных с сайтов, даже по мере роста объёма данных. Давайте разберёмся и соберём это вместе!

Что такое распределённый веб-краулинг?

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

В распределённой системе нагрузка делится между несколькими компьютерами, каждый из которых отвечает за свою часть задач. Такой подход позволяет за более короткое время обходить большое количество страниц и обрабатывать огромные объёмы данных. Это особенно важно для крупномасштабных операций по веб-скрейпингу, например в анализе e-commerce, агрегации новостей и исследовании рынка.

Ключевые технологии для создания распределенного веб-краулера

Прежде чем перейти к построению распределенной системы веб-краулинга, обсудим основные технологии, которые мы будем использовать:

  1. Python: Популярный язык программирования, известный своей простотой и широкой поддержкой библиотек. Python отлично подходит для web scraping и предоставляет мощные библиотеки, такие как requests и BeautifulSoup, для разбора HTML.
  2. Celery: Система распределенной очереди задач с открытым исходным кодом, которая позволяет нам обрабатывать задачи асинхронно. Celery позволяет распределять задачи обхода по нескольким воркерам или машинам, делая процесс веб-краулинга более масштабируемым и эффективным.
  3. Redis: Высокопроизводительное хранилище данных в памяти, используемое как брокер сообщений для Celery. Redis помогает управлять очередью URL для краулинга, хранить посещенные ссылки и отслеживать прогресс краулинга.

Требования для создания распределенного краулера

Перед созданием распределенной системы веб-краулинга нужно установить на машину несколько обязательных компонентов:

  • Python 3: Убедитесь, что Python 3 установлен в вашей системе.
  • Redis: Установите Redis, который будет использоваться как брокер сообщений.
  • Celery и другие зависимости: Установите необходимые библиотеки для сбора данных с веб-страниц и асинхронной обработки задач.

Вы можете установить эти зависимости с помощью pip следующим образом:

pip install requests beautifulsoup4 celery[redis] playwright
npx playwright install

Шаг 1: Настройка Celery и Redis

Первый шаг в построении распределенного веб-краулера — настроить Celery и Redis.

Celery будет использоваться для распределения задач, а Redis будет выступать в роли брокера сообщений, отслеживая URL-адреса, которые нужно обойти. Вот как настроить Celery:

Создайте приложение Celery

We will define a simple Celery application to handle tasks asynchronously. In the tasks.py file, we will initialize the Celery app and configure it to use Redis as the broker.

с сайта celery импорт Celery
app = Celery('tasks', broker_url='redis://127.0.0.1:6379/1')
@app.task
def demo(str):
печать(f'Str: {str}')

Запустите воркер Celery

Чтобы выполнять задачи, нужно запустить worker Celery, выполнив в терминале следующую команду:

celery -A tasks worker - loglevel=info

Это запустит worker, который слушает задачи в очереди и обрабатывает их по мере поступления.

Шаг 2: Создание задачи обхода

После настройки Celery следующий шаг — создать задачу для обхода URL. Эта задача будет загружать HTML-содержимое URL, извлекать ссылки со страницы и помещать их в очередь для дальнейшей обработки.

Вот как можно создать базовую задачу обхода:

импорт requests
с сайта bs4 импорт BeautifulSoup
с сайта urllib.parse импорт urljoin
@app.task
def crawl(url):
html = get_html(url)
soup = BeautifulSoup(html, 'html.parser')
links = extract_links(url, soup)
печать(ссылки)
def get_html(url):
попробуйте:
response = requests.get(url)
возврат response.content
кроме Исключение в роли e:
печать(e)
возврат ''
def extract_links(url, soup):
возврат list({
urljoin(url, a.get('href'))
для a в soup.find_all('a')
если a.get('href') и не(a.get('rel') и 'nofollow' в a.get('rel'))
})

Эта задача обхода загружает HTML веб-страницы, разбирает его с помощью BeautifulSoup и извлекает все ссылки. Затем она выводит список ссылок, найденных на странице.

Шаг 3: Использование Redis для отслеживания URL-адресов

To prevent the crawler from visiting the same page multiple times, we will use Redis to keep track of URLs. We will store URLs in a Redis list called crawling:to_visit, and the crawler will pop URLs from this list one by one for processing.

Мы также будем хранить в Redis два множества: одно для посещённых URL и одно для URL в очереди. Это поможет избежать повторной обработки URL.

Вот как можно изменить краулер, чтобы использовать Redis для отслеживания URL-адресов:

с сайта redis импорт Redis
connection = Redis(db=1)
starting_url = 'https://example.com/start-page'
# Push the starting URL to Redis
connection.rpush('crawling:to_visit', starting_url)
в то время как Правда:
item = connection.blpop('crawling:to_visit', 60)
если пункт это Нет:
печать('Тайм-аут! Больше нет элементов для обработки')
перерыв
url = item[1].decode('utf-8')
печать('Pop URL', url)
crawl.delay(url)

Шаг 4: Обеспечение масштабируемости с несколькими воркерами

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

Чтобы запустить несколько worker-ов, можно выполнить следующие команды:

celery -A tasks worker - concurrency=20 -n worker1
celery -A tasks worker - concurrency=20 -n worker2

Каждый воркер будет обрабатывать задачи независимо, что позволит краулеру масштабироваться на несколько узлов. Celery берет на себя распределение задач между доступными воркерами, обеспечивая равномерное распределение нагрузки.

Шаг 5: Настройка краулера для разных сайтов

Чтобы сделать краулер более гибким, вы можете настраивать способ извлечения данных с разных сайтов. Вместо того чтобы жестко зашивать логику извлечения данных в функцию crawl, можно создать модульную систему, которая позволяет использова��ь разные парсеры для разных сайтов.

In the repo.py file, you can define a function to get a custom parser for each website:

с сайта urllib.parse импорт urlparse
с сайта parsers импорт defaults
parsers = {
'example.com': defaults,
'quotes.com': custom_parser,
}
def get_parser(url):
hostname = urlparse(url).hostname
если hostname в parsers:
возврат parsers[hostname]
возврат defaults

Затем в задаче обхода можно использовать подходящий парсер для каждого сайта:

@app.task
def crawl(url):
parser = get_parser(url)
html = parser.get_html(url)
soup = BeautifulSoup(html, 'html.parser')
links = extract_links(url, soup)
печать(ссылки)

Шаг 6: Как избежать обнаружения с помощью прокси и заголовков

Websites often block crawlers by detecting unusual traffic patterns. To avoid getting blocked, you can rotate proxies and use custom headers for your requests. You can create a headers.py file to store different sets of headers and a proxies.py file for managing proxies.

Here’s an example of how to use random headers and proxies in the get_html function:

с сайта заголовки импорт random_headers
с сайта proxies импорт random_proxies
def get_html(url):
headers = random_headers()
proxies = random_proxies()
response = requests.get(url, headers=headers, proxies=proxies)
возврат response.content

Шаг 7: Обработка ошибок и повторных попыток

При web scraping в больших масштабах часто возникают ошибки, включая тайм-ауты, отсутствующие страницы или сбои запросов. Важно реализовать обработку ошибок и механизмы повторных попыток, чтобы краулер продолжал работать стабильно.

Celery предоставляет механизм повторных попыток, который позволяет автоматически повторять неудачные задачи. Вот пример того, как реализовать повторные попытки в вашей задаче crawl:

@app.task(bind=Правда, max_retries=3)
def crawl(self, url):
попробуйте:
html = get_html(url)
soup = BeautifulSoup(html, 'html.parser')
links = extract_links(url, soup)
печать(ссылки)
кроме Исключение в роли e:
печать(f'Error: {e}')
поднять self.retry(exc=e)

Ищете простое и масштабируемое решение?

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

Заключение

Использование Celery и Redis для создания распределенного web crawler позволяет выполнять scraping сайтов в большом масштабе, эффективно распределяя задачи между несколькими worker-ами. Такой подход помогает обрабатывать большие объемы данных и настраивать краулер под разные сайты.

Это также помогает решать такие задачи, как работа с прокси, заголовками и повторными попытками. По мере того как веб-скрапинг становится всё сложнее, распределённая система становится необходимой для достижения оптимальной производительности и масштабируемости. Используя Celery и Redis, вы можете создать надёжный и устойчивый краулер, способный быстро и точно обрабатывать миллионы URL.

 

Похожие записи