Логотип «2Captcha»Перейти на главную страницу
Туториалы по обходу капчи

Эта статья была полезной?

Как парсить Lidl

Грегори Фишер
Грегори Фишер

Технический специалист

Скрепинг данных о продуктах из интернет-магазинов имеет свои особенности, особенно когда речь идет о крупных сетях, таких как Lidl US. В отличие от многих других сайтов, эффективный скрепер для Lidl не должен полагаться на угадывание структуры URL или использование платных API без крайней необходимости.

В этом руководстве мы рассмотрим, как надежно и быстро извлекать данные с помощью проекта с открытым исходным кодом 2scraper/lidl-scraper, минимизируя накладные расходы и корректно обрабатывая региональные особенности ценообразования.

Что мы будем создавать

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

  1. Поиск по запросу: извлечение товаров по ключевому слову (например, цельное молоко).
  2. Категории и акции: получение списка товаров из конкретных разделов или еженедельных акций.
  3. Конкретный товар: сбор точных метаданных по прямой ссылке на страницу продукта.

Извлекаемые данные включают: sku (идентификатор товара), source, category, title, brand, price, currency, price_source, product_url, image_url, scraped_at, unit_price, unit_price_per, unit_size, original_price, discount_pct, is_weekly_deal, lidl_plus_price, deal_valid_from, deal_valid_until, store_id и zip_code.

Важно: цены на сайте Lidl зависят от выбранного магазина, который определяется через cookies. Без указания магазина возвращаются цены по умолчанию. Скрепер позволяет задать конкретный магазин или почтовый индекс для получения актуальных региональных цен.

Требования

  • Python: версия 3.9 или новее.
  • Движок браузера: по умолчанию используется playwright (chromium). Также поддерживаются selenium и puppeteer (pyppeteer) для обеспечения совместимости.
  • Окружение: рекомендуется использовать отдельное виртуальное окружение для каждого движка, так как их зависимости могут конфликтовать.
  • Опционально: ключ API 2Captcha (TWOCAPTCHA_KEY) и прокси для масштабирования, хотя для базового использования они не требуются.

Настройка проекта

  1. Клонируйте репозиторий и создайте виртуальное окружение:

    bash Copy
    git clone https://github.com/2scraper/lidl-scraper.git
    cd lidl-scraper
    python3 -m venv .venv
    source .venv/bin/activate  # Для Windows: .venv\Scripts\activate
  2. Установите зависимости для выбранного движка. Не устанавливайте несколько движков в одно окружение:

    bash Copy
    # Основной вариант (рекомендуется)
    pip install -r requirements-playwright.txt
    playwright install chromium
    
    # Альтернативные варианты (только если необходимо)
    # pip install -r requirements-selenium.txt
    # pip install -r requirements-puppeteer.txt
  3. Скопируйте файл конфигурации окружения:

    bash Copy
    cp .env.example .env

    Оставьте его пустым для локального запуска без прокси и ключей.

Быстрый старт

Основной точкой входа является скрипт playwright_scraper.py. Вот примеры запуска для разных сценариев:

bash Copy
# 1. Поиск товаров по запросу с сохранением в json
python3 playwright_scraper.py --query "whole milk" --format json --out lidl_results.json

# 2. Поиск с привязкой к конкретному магазину (по ID или почтовому индексу)
python3 playwright_scraper.py --query "sourdough bread" --store-id US01449 --max-results 20
python3 playwright_scraper.py --query "sourdough bread" --zip 07086

# 3. Сортировка результатов (например, по возрастанию цены)
python3 playwright_scraper.py --query milk --sort price_asc

# 4. Извлечение данных из категории или раздела акций
python3 playwright_scraper.py --category h/cheese-dairy-eggs/h10095761 --max-results 200
python3 playwright_scraper.py --category aw42-fresh-deals/a10104750

# 5. Сканирование конкретной страницы товара
python3 playwright_scraper.py --url "https://www.lidl.com/p/whole-milk/p11675058"

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

Как работает скрепер

Архитектура проекта разделяет логику исполнения и парсинга, что обеспечивает гибкость и надежность:

  • Реальный источник данных: основной массив данных извлекается из атрибута data-gridbox-impression каждой карточки товара, который содержит закодированный json с чистыми данными об идентификаторе, названии, бренде, категории и цене.
  • Технология сайта: сайт работает на Nuxt.js. Ранее предполагалось использование Next.js, но тесты подтвердили, что данные о продуктах на странице поиска отсутствуют в JSON-LD, а извлекаются непосредственно из DOM-атрибутов.
  • Региональное ценообразование: при использовании флагов --store-id или --zip скрепер устанавливает четыре специфических cookie (st, wh, zn, ar), что заставляет сайт отображать цены, актуальные для выбранного региона.

Извлечение данных

1. Режим поиска и категорий

Скрепер эмулирует поведение реального пользователя: загружает страницу, прокручивает ее, дожидается стабилизации контента и извлекает данные из каждого видимого элемента. Единицы измерения и цена за единицу рассчитываются на основе видимого текста рядом с основной ценой.

2. Режим отдельного товара

При передаче прямой ссылки на товар скрепер переходит на страницу продукта, где данные структурированы еще более подробно, включая наличие схем разметки schema.org Product.

3. Контракт вывода

Каждая строка результата строго типизирована. Поле price_source указывает, откуда были взяты данные (embedded_json или dom), что исключает догадки. Идентификатор sku берется напрямую с сайта Lidl или генерируется детерминированно на основе URL, если прямой идентификатор недоступен.

Работа с движками и прокси

Проект поддерживает три движка, но имеет строгие технические ограничения, о которых важно знать:

Движок Особенности Ограничения
playwright Основной и рекомендуемый вариант. Поддерживает все функции, включая удаленные CDP-эндпоинты. Нет существенных ограничений.
selenium Альтернативный вариант для специфических сред. Не поддерживает аутентифицированные удаленные CDP-эндпоинты и прокси с логином и паролем.
puppeteer (pyppeteer) Предоставлен только для совместимости. Фактически не поддерживается сообществом, может конфликтовать зависимостями с другими библиотеками.

Важные детали работы с прокси:

  • Скрепер не поддерживает аутентификацию в SOCKS-прокси на уровне chromium. Используйте HTTP-прокси или прокси без аутентификации.
  • При использовании прокси с учетными данными в selenium они будут автоматически удалены с предупреждением, чтобы избежать тихих сбоев.

Продвинутые техники и отладка

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

  • --dump-html: При возникновении ошибки парсинга скрепер сохранит исходный HTML-код страницы в файл. Это бесценный инструмент для отладки: если Lidl изменит верстку или название атрибута data-gridbox-impression, вы сразу увидите, что именно вернул сервер, без необходимости запускать браузер вручную.
  • --allow-empty: По умолчанию скрепер защищает ваши данные. Если новый запуск вернул 0 товаров (например, из-за временного сбоя сети или капчи), он не перезапишет ваш предыдущий успешный файл результатов. Этот флаг отключает такую защиту, если вам нужно именно пустое состояние.
  • --stall-rounds и --max-scrolls: Сайт использует виртуализированную сетку товаров. Скрепер прокручивает страницу порциями. Если в течение --stall-rounds (по умолчанию несколько циклов) новые товары не появляются, скрепер корректно завершает работу, а не зависает в бесконечном цикле.

Интеграция в автоматизацию: коды выхода

Понимание кодов выхода (exit codes) критически важно для настройки Cron-задач или CI/CD пайплайнов. Скрепер всегда возвращает четкий статус:

Код Значение Действие для автоматизации
0 Успешно (complete) Данные собраны, файл обновлен.
2 Ошибка использования (bad usage) Проверьте аргументы. Например, указан несуществующий store-id или некорректный формат прокси.
3 Заблокировано (blocked) Обнаружена капча или страница блокировки. Требуется вмешательство или смена прокси/CDP-эндпоинта.
4 Товаров не найдено Запрос выполнен успешно, но в выбранном магазине или категории действительно нет товаров.
5 Контент не получен Сбой навигации, ошибка удаленного API или CDP-соединения.
6 Частичный успех (partial) Товары собраны, но их количество меньше, чем обещал счетчик на сайте (например, из-за преждевременного обрыва связи). Файл .meta.json укажет причину (stop_reason).

Каждый успешный или частичный запуск создает файл-спутник <имя_файла>.meta.json, содержащий метаданные: статус, причину остановки, количество обработанных страниц и процент подтвержденных цен (price_confirmed_pct).

Масштабирование и ограничения

  1. Отсутствие параллелизма на одной странице: так как результаты поиска представляют собой единую прокручиваемую страницу, параметры параллельного выполнения (--concurrency) и ротации прокси на каждую страницу не применяются. Параллелизм имеет смысл только при запуске нескольких независимых поисковых запросов.
  2. Безопасный csv: при экспорте в csv любые значения, начинающиеся с =, +, -, @, табуляции или переноса строки, автоматически экранируются ведущим символом кавычки, чтобы предотвратить атаки внедрения формул в таблицах.
  3. Отсутствие капчи: на момент последних тестов сайт Lidl US не использовал активные защиты типа PerimeterX или Cloudflare для обычных поисковых запросов, что делает локальный запуск без дополнительных сервисов вполне надежным.

Устранение неполадок

Проблема Возможная причина Решение
ModuleNotFoundError при запуске Попытка использовать движок без установки его зависимостей Установите requirements-playwright.txt (или аналогичный файл) в чистом виртуальном окружении.
Возвращается 0 товаров, хотя они есть Указан магазин, в котором товар отсутствует или недоступен Проверьте --store-id или --zip. Некоторые магазины могут возвращать страницу "нет совпадений". Попробуйте запустить без указания магазина.
Ошибка выхода 2 при использовании --cdp-endpoint в selenium selenium не поддерживает аутентифицированные WebSocket-соединения Переключитесь на движок playwright для работы с удаленными CDP-эндпоинтами.
Конфликт зависимостей pyee или urllib3 Установка нескольких движков в одно окружение Используйте отдельное виртуальное окружение для каждого движка.

Пример вывода

Фрагмент реального вывода в формате json (данные получены при живом тестировании):

json Copy
{
  "sku": "p11675058",
  "source": "lidl.com",
  "category": "Dairy & Eggs",
  "title": "Lactaid whole milk",
  "brand": "LACTAID",
  "price": 6.38,
  "currency": "USD",
  "price_source": "dom",
  "product_url": "https://www.lidl.com/p/lactaid-whole-milk/p11675058",
  "image_url": "https://www.lidl.com/media/product/0/1/1/6/7/5/0/8/lactaid-whole-milk.jpg",
  "scraped_at": "2026-10-18T14:22:00Z",
  "unit_price": 0.07,
  "unit_price_per": "fl.oz.",
  "unit_size": "96 fl.oz.",
  "store_id": "US01449",
  "zip_code": "07086"
}

Пример программной обработки результатов

Поскольку скрепер выдает структурированный JSON, его легко интегрировать в любые аналитические пайплайны. Ниже приведен пример скрипта, который читает результаты, фильтрует только товары со скидкой и выводит самые выгодные предложения:

python Copy
import json
from pathlib import Path

def analyze_lidl_deals(json_file: str):
    path = Path(json_file)
    if not path.exists():
        print(f"Файл {json_file} не найден.")
        return

    with open(path, "r", encoding="utf-8") as f:
        data = json.load(f)

    # Проверяем метаданные выполнения
    meta_file = path.with_suffix(".meta.json")
    if meta_file.exists():
        with open(meta_file, "r", encoding="utf-8") as mf:
            meta = json.load(mf)
            print(f"Статус сбора: {meta.get('status')} (собрано товаров: {len(data)})")

    # Фильтруем только товары с активной скидкой
    discounted_items = [
        item for item in data 
        if item.get("discount_pct", 0) > 0
    ]

    if not discounted_items:
        print("Товаров со скидкой не найдено.")
        return

    # Сортируем по максимальной процентной скидке
    discounted_items.sort(key=lambda x: x.get("discount_pct", 0), reverse=True)

    print("\n--- Лучшие предложения со скидкой ---")
    for item in discounted_items[:5]:
        title = item.get("title", "Без названия")
        brand = item.get("brand", "Нет бренда")
        price = item.get("price", 0.0)
        original = item.get("original_price", 0.0)
        discount = item.get("discount_pct", 0)
        
        print(f"• {brand} {title}")
        print(f"  Цена: ${price} (было: ${original}) | Скидка: {discount}%")
        print(f"  Ссылка: {item.get('product_url')}\n")

if __name__ == "__main__":
    analyze_lidl_deals("lidl_results.json")

Архитектурная особенность: честность фингерпринтинга

В семействе скреперов 2scraper есть строгое правило, реализованное и здесь: модуль fingerprint_client.py извлекает и применяет из API отпечатков только User-Agent.

Скрепер намеренно не подменяет часовой пояс или локаль браузера, даже если API отдает эти данные. Авторы репозитория придерживаются принципа: честное отсутствие сигнала лучше, чем догадка, которая может быть вычислена как аномалия. Если вы не можете подтвердить точный механизм применения конкретной метрики в целевом браузере, она не внедряется в код, чтобы не создавать ложного чувства безопасности и не усложнять поддержку.

Заключение

Надежный скрепинг данных с Lidl US не требует сложных обходов защит или постоянных платных сервисов. Понимание того, что сайт отдает данные через специфические DOM-атрибуты и управляет ценами через cookies, позволяет использовать быстрый и стабильный локальный запуск на базе playwright.

Движки selenium и puppeteer предоставлены для обеспечения совместимости, но playwright остается оптимальным выбором для продакшена. Всегда указывайте регион (--store-id или --zip) для получения реальных цен, используйте коды выхода для построения надежной автоматизации и проверяйте метаданные выполнения для контроля качества собранных данных.