Почему скрапинг цен сложнее, чем кажется
Один раз соскрапить сайт — легко. Запустить скрапер, который работает каждый день, адаптируется к изменениям на сайте и не блокируется — вот настоящий вызов.
В этом посте рассмотрена практическая сторона: инструменты, меры защиты от ботов и как создать то, что можно реально запустить в продакшене.
Шаг 1: Поймите, что вы скрапите
Прежде чем писать ни одной строки кода, откройте DevTools (F12) → вкладка Network → перезагрузите страницу.
Ищите:
- XHR/Fetch запросы — загружается ли цена через API-вызов? Если да, обращайтесь напрямую к API. Это намного проще, чем парсить HTML.
- Статический HTML — цена есть в исходном коде страницы (View Source)
- Отрендерено JavaScript — цена появляется только после выполнения JS (не видно в View Source)
# Быстрый тест: если это показывает цену, то это статический HTML
curl -s "https://example.com/product" | grep -i "price"
Если curl показывает цену, вы можете использовать простой HTTP-клиент. Если нет — нужен безголовый браузер.
Правильный инструмент для каждого случая
| Тип сайта | Лучший инструмент |
|---|---|
| Статический HTML | Python requests + BeautifulSoup |
| REST API (JSON) | только requests |
| Отрендерено JavaScript | Playwright или Puppeteer |
| Мощный антибот | Playwright + stealth-плагин + прокси |
Базовый статический скрапер (Python)
import requests
from bs4 import BeautifulSoup
def get_price(url, selector):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
r = requests.get(url, headers=headers, timeout=10)
soup = BeautifulSoup(r.text, "html.parser")
el = soup.select_one(selector)
return el.get_text(strip=True) if el else None
price = get_price("https://example.com/product-123", ".price")
print(price)
Всегда устанавливайте User-Agent — голые запросы без него сразу блокируются большинством сайтов.
Сайты с рендерингом JavaScript: используйте Playwright
from playwright.sync_api import sync_playwright
def get_price_js(url, selector):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(url, wait_until="networkidle")
el = page.query_selector(selector)
price = el.inner_text() if el else None
browser.close()
return price
Playwright запускает настоящий браузер — JavaScript выполняется, появляется лениво загруженный контент, загружаются динамические цены.
Почему сайты блокируют скраперы (и как с этим справляться)
Сайты обнаруживают скраперов по нескольким сигналам:
1. Скорость запросов — 100 запросов за 2 секунды — это очевидно не человек. Добавьте задержки:
import time, random
time.sleep(random.uniform(1.5, 4.0)) # случайная задержка между запросами
2. Отсутствие заголовков браузера — настоящие браузеры отправляют Accept, Accept-Language, Referer и т.д. Скопируйте полный набор заголовков из DevTools.
3. Снятие отпечатков (fingerprinting) — сайты проверяют Canvas, WebGL, шрифты, разрешение экрана. Playwright с плагином stealth рандомизирует эти параметры.
4. Репутация IP — IP-адреса дата-центров часто помечаются. Решают проблему резидентные прокси, но они стоят денег ($10–50/мес).
5. CAPTCHA — если вы регулярно сталкиваетесь с ними, нужен сервис решения CAPTCHA (2captcha, AntiCaptcha) или замедление запросов с прокси.
Запуск по расписанию
Для ежедневного мониторинга цен используйте cron-задачи на VPS:
# Запуск ежедневно в 8:00
0 8 * * * /usr/bin/python3 /home/user/scraper/prices.py >> /home/user/scraper/prices.log 2>&1
Или используйте библиотеку schedule в Python, если хотите, чтобы скрипт работал непрерывно:
import schedule, time
def job():
prices = scrape_all_products()
save_to_db(prices)
check_alerts(prices)
schedule.every().day.at("08:00").do(job)
while True:
schedule.run_pending()
time.sleep(60)
Сохранение результатов и отправка уведомлений
Для мониторинга цен нужно отслеживать изменения во времени:
import sqlite3
from datetime import datetime
def save_price(product_id, price, source):
conn = sqlite3.connect("prices.db")
conn.execute(
"INSERT INTO prices (product_id, price, source, checked_at) VALUES (?, ?, ?, ?)",
(product_id, price, source, datetime.now().isoformat())
)
conn.commit()
conn.close()
def check_price_drop(product_id, threshold_pct=5):
conn = sqlite3.connect("prices.db")
rows = conn.execute(
"SELECT price FROM prices WHERE product_id = ? ORDER BY checked_at DESC LIMIT 2",
(product_id,)
).fetchall()
conn.close()
if len(rows) == 2:
current, previous = rows[0][0], rows[1][0]
drop = (previous - current) / previous * 100
if drop >= threshold_pct:
send_telegram_alert(f"Цена упала на {drop:.1f}% на товар {product_id}")
Если вы попробовали эти шаги, но самостоятельно настроить мониторинг цен не удалось, или вы не хотите тратить время — обратитесь к нам, и мы поможем с реализацией.