Screaming Frog це основний краулер для більшості SEO-спеціалістів, але ви, напевно, вже натрапляли на його обмеження: ліміт 500 URL у безкоштовній версії, переповнення RAM на великих сайтах, або бажання автоматизувати краули без нагляду за GUI. Scrapy це фреймворк Python з відкритим кодом, який усуває ці обмеження.
Якщо ви можете запустити npm install або git clone, ви можете запустити Scrapy. Крива навчання реальна, але керована, особливо якщо ви вже освоюєтесь з CLI-інструментами через агентні робочі процеси кодування.
Чому Scrapy?
Screaming Frog чудово працює для швидких аудитів. Але має обмеження:
| Обмеження | Вплив |
|---|---|
| Безкоштовний ліміт 500 URL | Потрібна ліцензія $259/рік для більших сайтів |
| Пожирає пам’ять | Великі краули можуть споживати 8GB+ RAM |
| Залежність від GUI | Важко автоматизувати або планувати |
| Обмежена кастомізація | Параметри конфігурації фіксовані |
Scrapy вирішує ці проблеми:
| Scrapy | Що ви отримуєте |
|---|---|
| Безкоштовний і з відкритим кодом | Без лімітів URL, без ліцензійних зборів |
| Менший обсяг пам’яті | Черги на диску тримають RAM під контролем |
| CLI-нативний | Скриптований, cron-сумісний, готовий до CI/CD |
| Повна Python-кастомізація | Екстрактуйте що потрібно, фільтруйте як хочете |
| Призупинення/Відновлення | Зупиняйте та продовжуйте великі краули будь-коли |
Встановлення
Scrapy працює на Python. Використовуйте віртуальне середовище, щоб тримати все в чистоті:
Debian/Ubuntu:
sudo apt install python3.11-venv
python3 -m venv venv
source venv/bin/activate
pip install scrapy
macOS:
python3 -m venv venv
source venv/bin/activate
pip install scrapy
Windows:
python -m venv venv
venv\Scripts\activate
pip install scrapy
Створення проекту
Зі встановленим Scrapy:
scrapy startproject myproject
cd myproject
scrapy genspider sitename example.com
Це створює:
myproject/
scrapy.cfg
myproject/
__init__.py
items.py
middlewares.py
pipelines.py
settings.py
spiders/
__init__.py
sitename.py
Код спайдера йде в spiders/sitename.py. Конфігурація знаходиться в settings.py.
Налаштування для ввічливого краулінгу
Налаштуйте settings.py перед запуском чого-небудь. Блокування витрачає більше часу, ніж повільний краулінг.
# Ввічливий краулінг
CONCURRENT_REQUESTS_PER_DOMAIN = 5
DOWNLOAD_DELAY = 1
ROBOTSTXT_OBEY = True
# AutoThrottle - регулює швидкість на основі відповіді сервера
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 5
AUTOTHROTTLE_MAX_DELAY = 60
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
AUTOTHROTTLE_DEBUG = True
# Ліміти безпеки
CLOSESPIDER_PAGECOUNT = 10000
# Вивід
FEED_EXPORT_ENCODING = "utf-8"
AutoThrottle
AutoThrottle моніторить час відповіді сервера та автоматично регулює швидкість краулу:
- Швидкі відповіді → прискорює
- Повільні відповіді → уповільнює
- Помилки/таймаути → значно уповільнює
На відміну від фіксованих затримок Screaming Frog, він адаптується до реальних умов сервера.
Обробка кодів статусу
За замовчуванням HttpErrorMiddleware Scrapy тихо відкидає відповіді не-2xx. Це означає, що 404, 301, 500 відкидаються до того, як досягнуть вашого callback. Ваш краул може показувати 100% кодів статусу 200, не тому, що сайт ідеальний, а тому, що помилки фільтруються.
Додайте це до вашого класу спайдера, щоб захоплювати всі коди статусу:
handle_httpstatus_list = [200, 301, 302, 403, 404, 500, 502, 503]
Screaming Frog за замовчуванням захоплює всі коди статусу. Це налаштування приводить Scrapy у відповідність з цією поведінкою.
Продуктивність у реальному світі
Реальні цифри з тестового краулу з 5 одночасними запитами та увімкненим AutoThrottle:
| Прогрес краулу | Сторінок/Хвилина | Примітки |
|---|---|---|
| 0-200 сторінок | 14-22 | Розгін |
| 200-500 сторінок | 10-12 | Стабілізація |
| 500-1000 сторінок | 7-10 | AutoThrottle регулює |
| 1000+ сторінок | 5-7 | Стабільний стан |
Порівняння функцій
| Функція | Screaming Frog | Scrapy |
|---|---|---|
| Вартість | Безкоштовно <500 URL, ~$259/рік | Безкоштовно, відкритий код |
| Макс. розмір краулу | Обмежено пам’яттю | Черги на диску |
| Кастомізація | Обмежені опції конфігурації | Повний Python-код |
| Планування | Вручну або сторонні | Нативний CLI, cron-сумісний |
| Призупинення/Відновлення | Так | Так (з JOBDIR) |
| Крива навчання | Низька (GUI) | Середня (код) |
| Обмеження швидкості | Базові фіксовані затримки | AutoThrottle (адаптивний) |
| Рендеринг JavaScript | Опціонально (Chrome) | Опціонально (playwright/splash) |
| Коди статусу | Всі за замовчуванням | Потребує налаштування |
| Фільтрація піддоменів | GUI чекбокси | Код (гнучкий regex) |
| Формати експорту | CSV, Excel тощо | JSON, CSV, XML, кастомний |
| CI/CD інтеграція | Складна | Нативна |
Фільтрація URL
Screaming Frog використовує чекбокси. Scrapy використовує код. Компроміс: крива навчання за точність.
Виключення міжнародних шляхів:
import re
from urllib.parse import urlparse
class MySiteSpider(scrapy.Spider):
name = "mysite"
allowed_domains = ["example.com", "www.example.com"]
start_urls = ["https://www.example.com/"]
# Пропускаємо міжнародні шляхи типу /uk/, /fr/, /de/
EXCLUDED_PATTERNS = re.compile(
r"/(in|au|th|es|hk|sg|ph|my|ca|cn|uk|kr|id|fr|vn|de|jp|nl|it|tw)/"
)
def filter_links(self, links):
filtered = []
for link in links:
hostname = urlparse(link.url).hostname or ""
if hostname not in ("example.com", "www.example.com"):
continue
if self.EXCLUDED_PATTERNS.search(link.url):
continue
filtered.append(link)
return filtered
Ви можете фільтрувати за URL-патернами, параметрами запиту, заголовками відповіді, вмістом сторінки або будь-якою комбінацією.
Призупинення та відновлення
Для краулів понад 1000 сторінок увімкніть призупинення/відновлення з JOBDIR:
scrapy crawl myspider -o output.json -s JOBDIR=crawl_state
Scrapy зберігає стан у crawl_state/. Натисніть Ctrl+C для призупинення. Запустіть ту саму команду для відновлення.
Стан включає URL в очікуванні, переглянуті URL та чергу запитів. Це надійніше, ніж функція збереження/завантаження Screaming Frog, оскільки базується на файлах і витримує перезапуски системи.
Рендеринг JavaScript
Scrapy отримує лише сирий HTML. Він не рендерить JavaScript. Це те саме, що повертає curl.
Для більшості SEO-краулів це нормально:
- Мета-теги, canonical та h1 зазвичай в початковому HTML
- Пошукові системи переважно індексують серверний контент
- Більшість e-commerce та контент-сайтів рендеряться на сервері
Якщо ваш цільовий сайт рендерить контент на стороні клієнта, є варіанти:
| Пакет | Примітки |
|---|---|
| scrapy-playwright | Використовує Chromium/Firefox/WebKit. Рекомендовано для сучасних JS-сайтів |
| scrapy-splash | Легкий, Docker-based рендерер |
| scrapy-selenium | Старіший підхід, все ще працює |
JS-рендеринг значно повільніший та більш ресурсомісткий. Додавайте лише якщо сайт цього потребує.
Screaming Frog має подібний компроміс. Увімкнення рендерингу JavaScript використовує Chrome під капотом і значно уповільнює краули.
Управління пам’яттю
На ~1300 сторінках з повною екстракцією полів:
- Пам’ять: ~265 МБ
- CPU: ~4%
Використання JOBDIR переносить черги запитів на диск, тримаючи пам’ять низькою. Для дуже великих краулів (100k+ URL) додайте ці налаштування:
MEMUSAGE_LIMIT_MB = 1024
MEMUSAGE_WARNING_MB = 800
SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue'
Це обмежує використання пам’яті та примушує дискові черги для планувальника.
Вивід даних
Базовий вивід спайдера:
{
"url": "https://www.example.com/page/",
"title": "Заголовок сторінки тут",
"status": 200
}
Для SEO-краулів вам потрібні поля, подібні до того, що експортує Screaming Frog:
def parse_page(self, response):
yield {
"url": response.url,
"status": response.status,
"title": response.css("title::text").get(),
"meta_description": response.css("meta[name='description']::attr(content)").get(),
"meta_robots": response.css("meta[name='robots']::attr(content)").get(),
"h1": response.css("h1::text").get(),
"canonical": response.css("link[rel='canonical']::attr(href)").get(),
"og_title": response.css("meta[property='og:title']::attr(content)").get(),
"og_description": response.css("meta[property='og:description']::attr(content)").get(),
"word_count": len(response.text.split()) if response.status == 200 else None,
"content_type": response.headers.get("Content-Type", b"").decode("utf-8", errors="ignore"),
}
Додавайте або видаляйте поля залежно від потреб. CSS-селектори працюють для будь-якого елемента на сторінці.
Формати експорту: JSON (-o output.json), JSON Lines (-o output.jsonl), CSV (-o output.csv), XML (-o output.xml).
JSON Lines найкращий для великих краулів. Файли валідні рядок за рядком під час краулу, тому ви можете моніторити за допомогою tail -f. Стандартний JSON не валідний, поки краул не завершиться.
Screaming Frog → Scrapy
Відображення робочих процесів SF на Scrapy:
| Дія Screaming Frog | Еквівалент Scrapy |
|---|---|
| Почати новий краул | scrapy crawl spidername |
| Встановити затримку краулу | DOWNLOAD_DELAY у налаштуваннях |
| Обмежити одночасні потоки | CONCURRENT_REQUESTS_PER_DOMAIN |
| Дотримуватись robots.txt | ROBOTSTXT_OBEY = True |
| Експортувати в CSV | -o output.csv |
| Зберегти/Завантажити краул | -s JOBDIR=crawl_state |
| Фільтрувати піддомени | Код у спайдері (regex) |
| Кастомна екстракція | CSS/XPath селектори в parse() |
Зміни мислення:
- Конфігурація це код. Редагуйте
settings.pyзамість клікання чекбоксів. - Екстракція явна. Ви пишете, які дані захоплювати.
- Планування нативне. Додавайте команди до cron або CI/CD.
- Дебагінг це логи. Увімкніть
AUTOTHROTTLE_DEBUG, щоб бачити, що відбувається.
Повний робочий процес
Зі стандартними налаштуваннями вище ви можете мати Scrapy встановленим і краулити менш ніж за 15 хвилин:
python3 -m venv venv
source venv/bin/activate # venv\Scripts\activate на Windows
pip install scrapy
scrapy startproject urlcrawler
cd urlcrawler
scrapy genspider mysite example.com
# Відредагуйте settings.py з конфігурацією ввічливого краулу
# Відредагуйте spiders/mysite.py з вашою логікою parse
scrapy crawl mysite -o urls.jsonl -s JOBDIR=crawl_state
Scrapy Shell
Створюючи кастомні конфігурації, використовуйте Scrapy Shell для інтерактивного тестування селекторів та налаштувань:
scrapy shell "https://example.com"
Це відкриває інтерактивну Python-консоль з уже завантаженою відповіддю. Тестуйте CSS та XPath селектори в реальному часі перед додаванням їх до спайдера:
>>> response.css('title::text').get()
'Example Domain'
>>> response.xpath('//h1/text()').get()
'Example Domain'
Scrapy Shell значно скорочує час ітерації. Валідуйте логіку екстракції без запуску повних краулів.
Повний шаблон спайдера
Готовий до продакшн спайдер з фільтрацією URL, обробкою кодів статусу та повною екстракцією SEO-полів:
import re
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from urllib.parse import urlparse
class SEOSpider(CrawlSpider):
name = "seospider"
allowed_domains = ["example.com"]
start_urls = ["https://www.example.com"]
# Capture all HTTP status codes, not just 2xx
handle_httpstatus_list = [200, 301, 302, 403, 404, 500, 502, 503]
# URL patterns to exclude
EXCLUDED_PATTERNS = re.compile(
r"/(in|au|th|es|hk|sg|ph|my|ca|cn|uk|kr|id|fr|vn|de|jp|nl|it|tw)/"
)
rules = (
Rule(
LinkExtractor(allow=()),
callback="parse_page",
follow=True,
process_links="filter_links",
),
)
def filter_links(self, links):
filtered = []
for link in links:
parsed = urlparse(link.url)
hostname = parsed.hostname or ""
if hostname not in ("example.com", "www.example.com"):
continue
if self.EXCLUDED_PATTERNS.search(link.url):
continue
filtered.append(link)
return filtered
def parse_page(self, response):
yield {
"url": response.url,
"status": response.status,
"title": response.css("title::text").get(),
"meta_description": response.css("meta[name='description']::attr(content)").get(),
"meta_robots": response.css("meta[name='robots']::attr(content)").get(),
"h1": response.css("h1::text").get(),
"canonical": response.css("link[rel='canonical']::attr(href)").get(),
"og_title": response.css("meta[property='og:title']::attr(content)").get(),
"og_description": response.css("meta[property='og:description']::attr(content)").get(),
"word_count": len(response.text.split()) if response.status == 200 else None,
"content_type": response.headers.get("Content-Type", b"").decode("utf-8", errors="ignore"),
}
Замініть example.com на ваш цільовий домен. Налаштуйте EXCLUDED_PATTERNS для структури URL вашого сайту.
Коли що використовувати
Screaming Frog:
- Швидкі аудити до 500 URL
- Результати потрібні за хвилини
- Візуальне дослідження сайту
- Не комфортно з CLI
- Використання даних Screaming Frog з Redirects.net
Scrapy:
- Сайти понад 10 000 URL
- Автоматизовані, заплановані краули
- Потреби в кастомній екстракції
- CI/CD інтеграція
- Обмеження пам’яті
- Версіоновані конфігурації
Підсумок
Scrapy має крутішу криву налаштування, ніж Screaming Frog, але усуває практичні обмеження, які накладають GUI-краулери. Без лімітів URL, без ліцензійних зборів, нижче використання пам’яті та нативна автоматизація.
Почніть з малого. Прокраульте сайт, який знаєте. Використовуйте консервативні налаштування. Порівняйте вивід зі Screaming Frog. Дані збігатимуться, але у вас буде інструмент, який масштабується.