Посібник із розподіленого веб-краулінгу: система та архітектура
У цій статті я покажу, як побудувати базову розподілену систему веб-краулінгу за допомогою Python, Celery та Redis. Я покроково проведу вас через налаштування архітектури, розподіл завдань між кількома вузлами та забезпечення ефективної роботи краулера. Отже, у вас буде масштабоване рішення для вебскрапінгу сайтів, навіть коли обсяг даних зростає. Давайте зануримося в це й створимо все разом!
Що таке розподілений веб-краулінг?
Веб-краулінг передбачає автоматичний обхід вебу, витягування корисних даних із різних сайтів і перехід за посиланнями на цих сайтах, щоб зібрати ще більше даних. Натомість розподілений веб-краулінг іде ще далі, розподіляючи процес обходу між кількома машинами або вузлами, щоб масштабувати його та підвищити ефективність.
У розподіленій системі навантаження ділиться між кількома комп’ютерами, і кожен із них працює над своєю частиною завдань. Використовуючи такий підхід, ми можемо обходити велику кількість сторінок за коротший час і обробляти величезні обсяги даних. Це критично важливо для великомасштабних операцій вебскрапінгу, як-от в аналізі електронної комерції, агрегації новин і дослідженнях ринку.
Ключові технології для створення розподіленого веб-краулера
Перш ніж зануритися в побудову системи розподіленого веб-краулінгу, обговорімо основні технології, які ми використовуватимемо:
- Python: Популярна мова програмування, відома своєю простотою та широкою підтримкою бібліотек. Python ідеально підходить для вебскрапінгу та пропонує потужні бібліотеки, як-от 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.
from celery import Celery
app = Celery('tasks', broker_url='redis://127.0.0.1:6379/1')
@app.task
def demo(str):
print(f'Str: {str}')
Запустіть воркер Celery
Щоб обробляти завдання, потрібно запустити воркер Celery, виконавши в терміналі таку команду:
celery -A tasks worker - loglevel=info
Це запустить воркер, який слухатиме завдання в черзі та оброблятиме їх у міру надходження.
Крок 2: Створення завдання веб-краулінгу
Після налаштування Celery наступний крок — створити завдання для краулінгу URL-адрес. Це завдання отримуватиме HTML-вміст URL-адреси, витягуватиме з неї посилання та додаватиме їх у чергу для подальшої обробки.
Ось як можна створити базове завдання краулінгу:
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
@app.task
def crawl(url):
html = get_html(url)
soup = BeautifulSoup(html, 'html.parser')
links = extract_links(url, soup)
print(посилання)
def get_html(url):
try:
response = requests.get(url)
return response.content
except Exception as e:
print(e)
return ''
def extract_links(url, soup):
return list({
urljoin(url, a.get('href'))
for a in soup.find_all('a')
if a.get('href') and not(a.get('rel') and 'nofollow' in 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:
from redis import 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)
while True:
item = connection.blpop('crawling:to_visit', 60)
if item is None:
print('Час вичерпано! Більше немає елементів для обробки')
break
url = item[1].decode('utf-8')
print('Витягнути URL', url)
crawl.delay(url)
Крок 4: Забезпечення масштабованості за допомогою кількох воркерів
Коли базову систему краулінгу вже налаштовано, наступний крок — розподілити навантаження між кількома воркерами. Celery дає змогу запускати кілька воркерів на різних машинах або в різних процесах, що спрощує масштабування процесу краулінгу.
Щоб запустити кілька воркерів, ви можете виконати такі команди:
celery -A tasks worker - concurrency=20 -n worker1
celery -A tasks worker - concurrency=20 -n worker2
Кожен воркер оброблятиме завдання незалежно, даючи змогу краулеру масштабуватися на кількох вузлах. Celery бере на себе розподіл завдань між доступними воркерами, забезпечуючи рівномірний розподіл навантаження.
Крок 5: Налаштування краулера для різних вебсайтів
Щоб зробити краулер гнучкішим, ви можете налаштувати спосіб, у який він витягує дані з різних вебсайтів. Замість того щоб жорстко прописувати логіку вилучення даних у функції обходу, можна створити модульну систему, яка дає змогу використовувати різні парсери для різних вебсайтів.
In the repo.py file, you can define a function to get a custom parser for each website:
from urllib.parse import urlparse
from parsers import defaults
parsers = {
'example.com': defaults,
'quotes.com': custom_parser,
}
def get_parser(url):
hostname = urlparse(url).hostname
if hostname in parsers:
return parsers[hostname]
return 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)
print(посилання)
Крок 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:
from headers import random_headers
from proxies import random_proxies
def get_html(url):
headers = random_headers()
proxies = random_proxies()
response = requests.get(url, headers=headers, proxies=proxies)
return response.content
Крок 7: Обробка помилок і повторні спроби
Під час масштабного вебскрапінгу часто виникають помилки, зокрема тайм-аути, відсутні сторінки або збої запитів. Важливо реалізувати обробку помилок і механізми повторних спроб, щоб краулер працював стабільно.
Celery має функцію повторних спроб, яка дає змогу автоматично повторювати невдалі завдання. Ось приклад того, як реалізувати повторні спроби у вашому завданні обходу:
@app.task(bind=True, max_retries=3)
def crawl(self, url):
try:
html = get_html(url)
soup = BeautifulSoup(html, 'html.parser')
links = extract_links(url, soup)
print(посилання)
except Exception as e:
print(f'Error: {e}')
raise self.retry(exc=e)
Шукаєте просте й масштабоване рішення?
Створення власного розподіленого краулера дає змогу детально вивчити технологію та налаштувати все під свої потреби. Однак якщо вам колись знадобиться збирати дані ще у більшому масштабі або ви захочете спростити деякі операційні труднощі, керовані сервіси на кшталт Bright Data or Firecrawl можуть бути корисними. Ці платформи беруть на себе базову інфраструктуру та технічні складнощі, даючи змогу приділяти більше часу роботі з вашими зібраними даними.
Conclusion
Використання Celery і Redis для побудови розподіленого вебкраулера допомагає виконувати вебскрапінг вебсайтів у великих масштабах, ефективно керуючи задачами між кількома воркерами. Такий підхід дає змогу обробляти великі обсяги даних і налаштовувати краулер для різних вебсайтів.
Це також допомагає впоратися з такими труднощами, як проксі, заголовки та повторні спроби. Оскільки вебскрапінг стає дедалі складнішим, розподілена система стає необхідною для досягнення оптимальної продуктивності та масштабованості. Використовуючи Celery і Redis, ви можете створити надійний і стійкий краулер, здатний швидко та точно обробляти мільйони URL.

