Office Depot является одним из крупнейших розничных продавцов канцелярских товаров в США. Компания управляет более чем 1 400 магазинами, насчитывает свыше 38 000 сотрудников и владеет брендами OfficeMax и Grand & Toy наряду с основным ассортиментом кресел, столов, канцелярии, принтеров и школьных принадлежностей. Публичный каталог содержит цены, рейтинги, количество отзывов и статус наличия для тысяч артикулов, что делает его ценным источником для отслеживания цен конкурентов, анализа ассортимента и мониторинга запасов.

В этом руководстве показано, как парсить Office Depot на Python надёжным способом. Вы создадите небольшой работающий скрапер, который загружает отрендеренную страницу товара через Crawling API, разбирает её с помощью BeautifulSoup и извлекает чистую запись по каждому товару: название, цена, SKU или артикул модели, рейтинг, количество отзывов, доступность и URL товара. Всё руководство ограничено публичными данными каталога, а раздел о законности в конце не является формальностью, так что прочитайте его прежде, чем запускать парсер в промышленных объёмах.

Что вы создадите

Скрипт на Python, который принимает URL товара в Office Depot, загружает отрендеренную страницу через Crawling API и извлекает структурированную запись. В качестве основного примера используется беспроводной принтер Epson Expression Home XP-4200; извлекаются следующие поля:

  • Название товара заголовок листинга, например «Epson Expression Home XP-4200 Wireless Color Inkjet All-In-One Printer».
  • Цена прейскурантная цена, показанная на странице.
  • SKU / артикул номер позиции, идентифицирующей товар в каталоге Office Depot.
  • Рейтинг средний рейтинг в звёздах, если у товара он есть.
  • Количество отзывов число покупательских отзывов, на основании которых выставлен рейтинг.
  • Доступность имеется ли товар в наличии или нет.
  • URL товара канонический адрес страницы с описанием товара.

Почему обычный запрос не работает на Office Depot

При запросе URL Office Depot с помощью обычного HTTP-клиента вы обычно получаете ответ со статусом 200, но почти без данных о товаре в теле. Два фактора работают против вас. Во-первых, сайт формирует большую часть контента о товарах и ценах на стороне клиента: блок с ценой и ряд других полей заполняются JavaScript после загрузки исходного HTML, поэтому при обычном запросе вместо значений видны заглушки. Во-вторых, Office Depot выявляет автоматизированный трафик. IP-адреса дата-центров и паттерны запросов, не характерные для реального браузера, получают CAPTCHA, ограничение по числу запросов или блокировку ещё до отдачи готовой страницы.

Таким образом, работающий скрапер Office Depot требует двух вещей в одном запросе: браузера, который реально рендерит страницу, и IP-адреса, который платформа воспринимает как адрес настоящего покупателя. Можно собрать это самостоятельно с помощью headless-браузера плюс пула ротирующихся жилых прокси, однако поддержание такой инфраструктуры в рабочем состоянии составляет большую часть усилий. Crawling API объединяет оба компонента в одном вызове: вы передаёте ему URL с JavaScript-токеном, он рендерит страницу за доверенным жилым IP и возвращает готовый HTML для парсинга. Подробнее о том, почему страницы с клиентским рендерингом не поддаются обычному запросу, см. в руководстве по краулингу JavaScript-сайтов.

Зачем нужен JS-токен

Crawlbase предлагает два типа токенов. Обычный токен загружает статичный HTML; JavaScript (JS) токен сначала рендерит страницу в настоящем браузере. Office Depot формирует цену и ряд полей на стороне клиента, поэтому здесь нужен JS-токен. Обычный токен возвращает пре-рендерный каркас, из-за чего поля с ценой и наличием остаются пустыми при разборе BeautifulSoup.

Предварительные требования

Перед написанием кода нужно подготовить несколько вещей. Ни одна из них не займёт много времени.

Базовые знания Python. Вы должны уметь писать и запускать скрипт на Python и устанавливать пакеты с помощью pip. Если вы только знакомитесь с языком, наше руководство по веб-скрапингу на Python закрывает все базовые темы, которые предполагаются в этом туториале.

Python 3.8 или новее. Проверьте версию командой python --version. Если Python не установлен, загрузите его с python.org или воспользуйтесь дистрибутивом, например Anaconda.

Аккаунт Crawlbase и JS-токен. Зарегистрируйтесь, откройте дашборд и скопируйте JavaScript (JS) токен со страницы документации аккаунта. Обращайтесь с токеном как с паролем: он аутентифицирует ваши запросы, поэтому не добавляйте его в систему контроля версий.

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

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

bash
python --version

python -m venv office_depot_env
source office_depot_env/bin/activate

pip install crawlbase beautifulsoup4 pandas

В Windows активируйте окружение командой office_depot_env\Scripts\activate вместо строки с source. Три зависимости выполняют основную работу: crawlbase является официальным клиентом для Crawling API, beautifulsoup4 разбирает возвращаемый HTML, позволяя извлекать каждое поле по CSS-селектору, а pandas записывает данные в CSV-файл в конце.

Структура страницы товара Office Depot

Страница товара Office Depot содержит одни и те же поля, которые вас интересуют: заголовок в заголовочном теге, цена в отдельном элементе, артикул, выполняющий роль SKU, звёздный рейтинг с числом отзывов и сообщение о доставке, указывающее, есть ли товар в наличии. Ниже расположены описание и таблица характеристик.

Прежде чем писать селекторы, откройте страницу товара в браузере, щёлкните правой кнопкой мыши по заголовку и выберите «Просмотреть код». Office Depot предваряет большинство классов префиксом od-, например od-heading для заголовка и od-graphql-price-big-price для цены. Именно эти имена классов нужно использовать в селекторах. Они периодически меняются, поэтому воспринимайте приведённые ниже селекторы как актуальный снимок, а не постоянный договор.

Шаг 1: Загрузка отрендеренной страницы товара

Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его с помощью JS-токена и запросите URL товара. Проверка кода статуса перед разбором даёт возможность явно увидеть ошибки, а не упустить их.

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

if __name__ == "__main__":
    url = "https://www.officedepot.com/a/products/8761287/Epson-Expression-Home-XP-4200-Wireless/"
    html = crawl(url)
    print(html[:500] if html else "No HTML returned")

Оба параметра ожидания важны для цели с клиентским рендерингом. ajax_wait указывает API дождаться завершения загрузки асинхронного контента, а page_wait выдерживает фиксированное количество миллисекунд после загрузки, чтобы поздно отрисовываемые поля с ценой и наличием успели появиться перед сохранением страницы. Пять секунд являются разумным начальным значением; увеличьте его, если поля возвращаются пустыми. Запустите скрипт и вы должны увидеть реальную разметку товара, а не пре-рендерный каркас, который возвращает обычный запрос. Это подтверждает, что рендеринг работает ещё до написания единственного селектора.

Crawlbase Crawling API

Office Depot требует отрендеренной страницы за доверенным IP в одном вызове, чтобы поля с ценой и наличием были действительно заполнены при разборе. Crawling API принимает JS-токен, запускает страницу в настоящем браузере, ротирует жилые IP на стороне сервера и передаёт готовый HTML, избавляя от необходимости управлять парком headless-браузеров и пулом прокси самостоятельно. Начните с одного URL товара на бесплатном тарифе.

Шаг 2: Разбор полей с помощью BeautifulSoup

Получив отрендеренный HTML, загрузите его в BeautifulSoup и извлеките каждое поле по его селектору. Office Depot размещает заголовок в h1.od-heading.sku-heading, цену в span.od-graphql-price-big-price, а сообщение о доставке в span.od-delivery-message-text. Небольшой вспомогательный метод, возвращающий значение по умолчанию, когда элемент отсутствует, защищает извлечение от сбоев при отсутствии поля на конкретной странице.

python
from bs4 import BeautifulSoup

def text_of(soup, selector, default="N/A"):
    el = soup.select_one(selector)
    return el.get_text(strip=True) if el else default

def parse_rating(soup):
    el = soup.select_one(".od-stars-inner")
    if not el or not el.get("style"):
        return None
    # style is like "width: 86%"; map percent of 5 stars
    percent = el["style"].split(":")[-1].strip().rstrip("%")
    try:
        return round(float(percent) / 20, 1)
    except ValueError:
        return None

def extract_product(html, url):
    soup = BeautifulSoup(html, "html.parser")
    reviews = text_of(soup, ".od-reviews-count-number").strip("()")
    sku = text_of(soup, ".od-product-card-region-product-number").split("#")[-1]
    delivery = text_of(soup, "span.od-delivery-message-text", "").lower()
    availability = "In Stock" if "in stock" in delivery else "Out of Stock"
    return {
        "name": text_of(soup, "h1.od-heading.sku-heading"),
        "price": text_of(soup, "span.od-graphql-price-big-price"),
        "sku": sku,
        "rating": parse_rating(soup),
        "review_count": reviews,
        "availability": availability,
        "product_url": url,
    }

Вспомогательная функция text_of возвращает значение по умолчанию при отсутствии элемента, избегая вызова .get_text() для None. SKU берётся из .od-product-card-region-product-number с разделением по символу #, чтобы оставить только номер позиции. Office Depot кодирует рейтинг в звёздах как процентную ширину CSS на элементе .od-stars-inner, поэтому parse_rating читает этот процент и делит на 20 для получения значения из пяти. Число отзывов находится в .od-reviews-count-number и заключено в скобки, которые удаляет strip("()"), а доступность определяется из текста сообщения о доставке. Для справки по выбору элементов см. руководство по BeautifulSoup в Python.

Одна вещь, которую стоит ожидать: имена классов с префиксом od- в Office Depot (элемент цены od-graphql-price-big-price, полоска рейтинга od-stars-inner) меняются без предупреждения, и макет страниц также периодически обновляется. Относитесь к приведённым выше селекторам как к начальному шаблону, а не как к постоянному договору. Если поле возвращает N/A или None на странице, где значение точно есть, повторно проверьте живую страницу в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание селекторов является нормой для любого промышленного скрапера, а не признаком неисправности.

Шаг 3: Сборка всего вместе

Теперь соедините загрузку, парсинг и запись в CSV в один работающий скрипт. Получите отрендеренную страницу, передайте её экстрактору, распечатайте запись и сохраните с помощью pandas.

python
import json
import pandas as pd
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

def text_of(soup, selector, default="N/A"):
    el = soup.select_one(selector)
    return el.get_text(strip=True) if el else default

def parse_rating(soup):
    el = soup.select_one(".od-stars-inner")
    if not el or not el.get("style"):
        return None
    percent = el["style"].split(":")[-1].strip().rstrip("%")
    try:
        return round(float(percent) / 20, 1)
    except ValueError:
        return None

def extract_product(html, url):
    soup = BeautifulSoup(html, "html.parser")
    reviews = text_of(soup, ".od-reviews-count-number").strip("()")
    sku = text_of(soup, ".od-product-card-region-product-number").split("#")[-1]
    delivery = text_of(soup, "span.od-delivery-message-text", "").lower()
    availability = "In Stock" if "in stock" in delivery else "Out of Stock"
    return {
        "name": text_of(soup, "h1.od-heading.sku-heading"),
        "price": text_of(soup, "span.od-graphql-price-big-price"),
        "sku": sku,
        "rating": parse_rating(soup),
        "review_count": reviews,
        "availability": availability,
        "product_url": url,
    }

def main():
    url = "https://www.officedepot.com/a/products/8761287/Epson-Expression-Home-XP-4200-Wireless/"
    html = crawl(url)
    if not html:
        return
    product = extract_product(html, url)
    print(json.dumps(product, indent=2))
    pd.DataFrame([product]).to_csv("office_depot_products.csv", index=False)
    print("Saved to office_depot_products.csv")

if __name__ == "__main__":
    main()

Как выглядит результат

Запустите полный скрипт командой python scraper.py и получите чистую запись, готовую к записи в JSON, CSV или базу данных. Распечатка JSON выглядит следующим образом:

json
{
  "name": "Epson Expression Home XP-4200 Wireless Color Inkjet All-In-One Printer",
  "price": "$99.99",
  "sku": "8761287",
  "rating": 4.3,
  "review_count": "512",
  "availability": "In Stock",
  "product_url": "https://www.officedepot.com/a/products/8761287/Epson-Expression-Home-XP-4200-Wireless/"
}

Масштабирование на страницы поиска и пагинацию

Один товар является демонстрацией; реальная задача предполагает сбор многих. Результаты поиска в Office Depot представляют собой сетку карточек товаров, каждая из которых содержит заголовок, цену, рейтинг, число отзывов, номер позиции и ссылку на страницу товара. Парсинг этой сетки осуществляется аналогично: выберите все карточки, извлеките из каждой те же поля, а затем переходите по ссылкам на страницы товаров, когда нужны более подробные данные. В поиске используется пагинация с параметром &page=, поэтому обход страниц происходит путём увеличения этого параметра с остановкой, когда страница не возвращает карточек.

python
import time

def extract_cards(html):
    soup = BeautifulSoup(html, "html.parser")
    grid = ".od-search-browse-products-vertical-grid-product"
    products = []
    for card in soup.select(grid):
        link_el = card.select_one(".od-product-card-region-description a")
        href = link_el["href"] if link_el else ""
        products.append({
            "name": text_of(card, ".od-product-card-region-description a"),
            "price": text_of(card, ".od-graphql-price-big-price"),
            "review_count": text_of(card, ".od-reviews-count-number").strip("()"),
            "product_url": "https://www.officedepot.com" + href if href else "N/A",
        })
    return products

def scrape_all_pages(base_url, max_pages=5):
    all_products = []
    for page in range(1, max_pages + 1):
        html = crawl(f"{base_url}&page={page}")
        if not html:
            break
        products = extract_cards(html)
        if not products:
            break
        all_products.extend(products)
        print(f"Page {page}: {len(products)} products")
        time.sleep(2)
    return all_products

Ограничение max_pages держит запуск в рамках, чтобы широкий запрос не выполнялся бесконечно, а проверка на пустой результат останавливает обход при исчерпании страниц поиска. Задержка time.sleep(2) между страницами задаёт темп запросов, чтобы не перегружать поиск в быстром цикле, что является верным способом попасть под ограничение. Подробнее о структурировании многостраничных ретейл-краулеров см. в нашем руководстве по веб-скрапингу в электронной коммерции.

Предпочитаете структурированный вывод?

Если вы не хотите поддерживать CSS-селекторы вообще, Crawling API с автоматическим разбором возвращает готовый структурированный JSON для распространённых страниц электронной коммерции, поэтому вы получаете такие поля, как название, цена и рейтинг, без написания кода извлечения. Он хорошо подходит, когда вы хотите, чтобы структура данных обрабатывалась за вас, а смещение селекторов контролировалось на стороне провайдера.

Как оставаться незаблокированным

Даже при отработанном рендеринге Office Depot следит за трафиком, характерным для скраперов. Несколько привычек помогут сохранить работоспособность запуска, и они применимы к любой сложной коммерческой цели.

  • Задавайте темп запросов. Распределяйте запросы с задержкой между страницами и варьируйте поисковые запросы вместо обхода одного термина на полной скорости. Задержка time.sleep в цикле пагинации является нижней границей, а не потолком.
  • Используйте ротацию. Пул жилых IP распределяет запросы по многим реальным пользовательским адресам, чтобы ни один из них не превысил лимит запросов. Crawling API берёт это на себя; если вы собираете собственную инфраструктуру, именно этому аспекту стоит уделить особое внимание.
  • Обращайте внимание на коды статусов. Запуск, который начинает возвращать CAPTCHA или ошибки, сигнализирует о том, что текущей частоты запросов или уровня IP уже недостаточно. Воспринимайте это как сигнал снизить нагрузку, а не как шум, который можно игнорировать.

Подробная инструкция приведена в руководстве по скрапингу без блокировок. Если вы предпочитаете маршрутизировать собственный трафик через ротируемый пул вместо использования управляемого API, Smart AI Proxy (также называемый AI Proxy) предоставляет ту же ротацию жилых IP в качестве подставного прокси-эндпоинта.

Законно ли парсить Office Depot?

Допустимость парсинга Office Depot зависит от условий обслуживания Office Depot, вашей юрисдикции и того, как вы используете данные. Условия Office Depot ограничивают автоматизированный доступ, поэтому парсинг может нарушать эти условия независимо от тщательности вашего инструментария. Ни один из приведённых здесь кодов этого не меняет; он лишь обеспечивает техническую работоспособность. Ознакомьтесь с условиями использования Office Depot и файлом robots.txt, и рассматривайте оба документа как границу для собираемых данных.

Несколько правил, которых стоит придерживаться. Собирайте только данные публичного каталога: названия товаров, цены, SKU, рейтинги, количество отзывов, доступность и ссылки на листинги, которые может видеть любой пользователь без аккаунта. Держите объём запросов достаточно низким, чтобы не создавать нагрузку на серверы Office Depot. Избегайте персональных данных, включая всё, что связано с идентифицируемыми покупателями, рецензентами или сотрудниками магазинов, и не распространяйте охраняемые авторским правом фотографии или описания товаров как свои собственные. Парсинг для внутренних исследований или сравнения цен гораздо легче обосновать, чем парсинг с целью повторной публикации или оптовой перепродажи каталога.

Данное руководство намеренно ограничено публичными страницами товаров и поиска, поскольку это та граница, которая делает работу обоснованной. Оно не охватывает ничего, что находится за авторизацией, данных аккаунтов или заказов, платёжных или оформительских процессов, а также любых попыток обойти аутентификацию. Для лицензированного или высокообъёмного доступа правильный путь состоит в использовании официального фида, API или соглашения о данных с Office Depot, а не в разработке более изощрённого скрапера. Если ваш проект требует гарантированной структуры, больших объёмов или коммерческих прав, сначала запросите санкционированный доступ; управляемый инструмент для краулинга обеспечивает только загрузку, но не разрешение на использование данных.

Итоги

Ключевые выводы

  • Office Depot заполняет ключевые поля на стороне клиента. Обычный запрос возвращает пре-рендерный каркас с пустыми полями цены и наличия, поэтому страницу необходимо отрендерить до разбора.
  • Нужны одновременно рендеринг и доверенный IP. Crawling API с JS-токеном обеспечивает оба условия в одном вызове; параметры ajax_wait и page_wait управляют временем ожидания для поздних полей.
  • BeautifulSoup выполняет извлечение. Сопоставьте название, цену, SKU, рейтинг, количество отзывов, доступность и URL товара с актуальными селекторами od- и ожидайте их смещения.
  • Масштабирование через поиск и пагинацию. Обходите сетку карточек результатов, перебирайте параметр &page= до тех пор, пока страница не вернёт пустой результат, задавайте темп запросов с задержкой и ограничивайте количество страниц.
  • Оставайтесь в рамках публичных данных. Соблюдайте условия обслуживания Office Depot и файл robots.txt, при лицензированных или объёмных данных предпочитайте официальный фид или API, и никогда не обращайтесь к аккаунтам, заказам или персональным данным.

Часто задаваемые вопросы

Почему обычный запрос не возвращает цену с Office Depot?

Потому что Office Depot заполняет цену и ряд других полей на стороне клиента с помощью JavaScript. Исходный HTML является пре-рендерным каркасом, поэтому обычный HTTP-запрос возвращает статус 200 с пустыми полями цены и наличия. Для получения реальных значений необходимо сначала отрендерить страницу, что и делает JS-токен Crawling API.

Нужен ли обычный токен или JS-токен для Office Depot?

JS-токен. Обычный токен загружает статичный HTML, который в Office Depot оставляет поля цены и доступности незаполненными. JS-токен рендерит страницу в настоящем браузере перед возвратом HTML, поэтому эти поля присутствуют при разборе BeautifulSoup.

Какие поля можно извлечь со страницы товара Office Depot?

Публичные поля, отображаемые на странице: название товара, цена, номер позиции, выполняющий функцию SKU или артикула модели, звёздный рейтинг, количество отзывов, доступность и канонический URL товара. Карточки результатов поиска содержат большинство тех же полей на каждый листинг, что позволяет собирать много товаров перед переходом на отдельные страницы.

Как парсить все страницы поиска в Office Depot?

Поиск использует пагинацию с параметром &page= в URL. Увеличивайте его в цикле, парсите каждую страницу тем же обработчиком карточек и останавливайтесь, когда страница возвращает пустой результат. Ограничивайте количество страниц и добавляйте короткую задержку между запросами, чтобы задать темп и избежать ограничений.

Мои селекторы возвращают N/A. Что изменилось?

Почти наверняка разметка Office Depot. Имена классов с префиксом od-, например od-graphql-price-big-price для цены и od-stars-inner для полоски рейтинга, меняются без предупреждения, и макет страницы также периодически обновляется. Повторно проверьте живую страницу в инструментах разработчика браузера и обновите селекторы. Периодическое обслуживание является нормой для любого промышленного скрапера.

Можно ли парсить данные аккаунта или заказов с Office Depot?

Нет, и данное руководство этого не охватывает. Детали аккаунта, история заказов и процессы оформления находятся за авторизацией, поэтому не являются публичными данными. Парсинг контента, защищённого входом в систему, или обход аутентификации для доступа к нему выходят за рамки этого руководства и нарушают условия Office Depot. Для санкционированного доступа к более богатым данным правильный путь состоит в использовании официального фида, API или соглашения о данных.

Начать создавать

Обходите любой сайт в масштабе, без борьбы с инфраструктурой.

Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

Самообслуживание · Звонок отдела продаж не требуется · Доступны корпоративные объёмы краулинга