Руководство по распределённому веб-краулингу: система и архитектура
В этой статье я покажу, как построить базовую распределённую систему веб-краулинга с помощью Python, Celery и Redis. Я покажу, как настроить архитектуру, распределить задачи между несколькими узлами и обеспечить эффективную работу краулера. В результате у вас будет масштабируемое решение для сбора данных с сайтов, даже по мере роста объёма данных. Давайте разберёмся и соберём это вместе!
Что такое распределённый веб-краулинг?
Веб-краулинг подразумевает автоматический обход сайтов, извлечение полезных данных с разных ресурсов и переход по ссылкам на этих сайтах, чтобы собрать ещё больше данных. В свою очередь, распределённый веб-краулинг идёт на шаг дальше: процесс краулинга распределяется между несколькими машинами или узлами, чтобы масштабировать его и повысить эффективность.
В распределённой системе нагрузка делится между несколькими компьютерами, каждый из которых отвечает за свою часть задач. Такой подход позволяет за более короткое время обходить большое количество страниц и обрабатывать огромные объёмы данных. Это особенно важно для крупномасштабных операций по веб-скрейпингу, например в анализе e-commerce, агрегации новостей и исследовании рынка.
Ключевые технологии для создания распределенного веб-краулера
Прежде чем перейти к построению распределенной системы веб-краулинга, обсудим основные технологии, которые мы будем использовать:
- Python: Популярный язык программирования, известный своей простотой и широкой поддержкой библиотек. Python отлично подходит для web scraping и предоставляет мощные библиотеки, такие как requests и BeautifulSoup, для разбора HTML.
- Celery: Система распределенной очереди задач с открытым исходным кодом, которая позволяет нам обрабатывать задачи асинхронно. Celery позволяет распределять задачи обхода по нескольким воркерам или машинам, делая процесс веб-краулинга более масштабируемым и эффективным.
- 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.

