Как парсить eBay на Python: рабочий код, который действительно работает

Последнее обновление: April 16, 2026

Большинство руководств по парсингу eBay живут примерно три месяца. Я знаю это не понаслышке: команда Thunderbit не раз наблюдала, как разработчики перебирают сломанные фрагменты кода, устаревшие CSS-селекторы и «рабочие» GitHub-репозитории, которые перестали работать ещё после второго редизайна eBay.

eBay хранит — это крупнейшая общедоступная long-tail база цен в открытом интернете после Amazon. Эти данные лежат в основе всего: от ценообразования для реселлеров до конкурентной разведки. Но получить их программно — задача не из простых: React-фронтенд eBay постоянно меняет CSS-классы, A/B-тесты показывают разным пользователям разную структуру DOM, а между вами и HTML стоит Akamai Bot Manager. В этом руководстве вы получите Python-код, который работает сегодня, поймёте, почему ломаются парсеры и как строить устойчивые решения, разберётесь в честном сравнении eBay API и парсинга, а также увидите no-code альтернативу на случай, если Python не стоит таких усилий.

Что значит парсить eBay на Python?

Парсинг eBay на Python — это написание скриптов, которые автоматически загружают страницы eBay, разбирают HTML (или скрытый JSON) и извлекают структурированные данные — заголовки, цены, информацию о продавце, даты продаж, варианты товара — в удобный формат, например CSV, таблицу или базу данных.

На eBay можно парсить несколько типов страниц:

  • Страницы результатов поиска (например, все объявления по запросу «AirPods Pro»)
  • Страницы товаров (полные характеристики, изображения, данные продавца)
  • Проданные / завершённые объявления (реальные цены и даты сделок)
  • Профили продавцов и отзывы

Для этой задачи Python — язык по умолчанию. Его экосистема — Requests, BeautifulSoup, lxml, pandas — позволяет легко скачивать страницы, разбирать HTML и преобразовывать данные. Но важно понимать разницу между парсингом HTML сайта и использованием официального API eBay — об этом поговорим дальше.

Зачем парсить eBay? Реальные кейсы для бизнеса

Если вы читаете эту статью, у вас, скорее всего, уже есть своя причина. Но полезно опираться на конкретную бизнес-ценность, потому что ROI данных eBay действительно впечатляет. Bain выяснила, что для тысяч компаний. McKinsey связывает динамическое ценообразование в ритейле с .

Чаще всего eBay используют для таких задач:

СценарийКакие данные нужныБизнес-результат
Мониторинг цен и репрайсингАктивные цены, доставка, состояние товараКонкурентное ценообразование, защита маржи
Анализ конкурентовАссортимент, акции, условия доставкиСтратегическое позиционирование, поиск пробелов в ассортименте
Исследование рынка и трендовСкорость появления объявлений, тренды категорий, спросПоиск новых товаров, прогнозирование спроса
Реселлерское ценообразование / оценкаЦены продаж, даты продаж, состояние товараРыночная стоимость, решение по покупке
Анализ отзывов и настроенийОтзывы, рейтинги, политика возвратаПонимание качества товара и удовлетворённости клиентов
ЛидогенерацияПрофили продавцов, данные магазина, контактная информацияB2B-выход на продавцов с высоким GMV

ebayscraping_stats_77c7da1611.png

Общий знаменатель один: данные в eBay есть, но они заперты внутри веб-страниц.

Парсинг — это способ превратить их в конкурентное преимущество.

Официальный API eBay или Python-парсинг: что выбрать?

Это тот вопрос, на который я хотел бы чаще видеть честный ответ в руководствах. У eBay есть официальные API — в первую очередь — и многие пользователи не понимают, что лучше: API или прямой парсинг. Ответ зависит только от того, какие данные вам нужны.

КритерийeBay Browse/Finding APIПарсинг eBay на Python
Проданные / завершённые объявленияОграниченно — Marketplace Insights API существует, но доступ часто отклоняютПолный доступ через параметры URL LH_Sold=1&LH_Complete=1
Лимиты запросов5 000 запросов в день на базовом тарифеУправляется вами самостоятельно (зависит от прокси)
Поля данныхЗаранее определённые (название, цена, категория, базовые данные продавца)Всё, что видно на странице (отзывы, полные характеристики, матрица вариантов)
Сложность настройкиOAuth 2.0, регистрация приложения, API-ключиpip install + код
СтабильностьСтабильные эндпоинтыЛомается при изменении HTML
СтоимостьЕсть бесплатный тариф, платно при больших объёмахКод бесплатный, но на масштабе нужны прокси
Данные по вариантам/MSKUЧастично — во многих случаях только родительский SKUПолные данные (через разбор скрытого JSON)
Глубина пагинацииЖёсткий предел в 10 000 товаровТеоретически без ограничений

Короткое уточнение: старый Finding API (включая findCompletedItems) . Если вы используете ebaysdk-python или любую библиотеку, которая обращается к Finding module, в продакшене она сейчас сломана.

Моя рекомендация: используйте Browse API для стабильных, умеренных по объёму структурированных запросов по активным объявлениям. Используйте парсинг Python, когда вам нужны цены продаж, отзывы, данные по вариантам или любые поля, которых нет в API. Многие команды совмещают оба подхода.

Какие инструменты и библиотеки нужны для парсинга eBay на Python

Прежде чем писать код, соберём стек. Для большинства страниц eBay вам не нужен headless-браузер — данные уже встроены в HTML, который отдаёт сервер.

БиблиотекаНазначение
requests или httpxHTTP-клиент для загрузки страниц eBay
curl_cffiHTTP-клиент с TLS-фингерпринтом как у реального браузера (критично для обхода Akamai)
beautifulsoup4HTML-парсер для извлечения через CSS-селекторы
lxmlБыстрый backend-парсер для BeautifulSoup
jmespathЯзык запросов для разбора вложенного JSON
pandasОбработка данных и экспорт в CSV/Excel
gspreadИнтеграция с Google Sheets

Установите всё одной командой:

1pip install requests httpx curl_cffi beautifulsoup4 lxml jmespath pandas gspread

Используйте Python 3.11+ — pandas 3.0 требует 3.10+, а 3.11 даёт прирост скорости 10–60% на I/O-зависимых задачах.

Отдельно стоит выделить curl_cffi: это, пожалуй, самое полезное улучшение, которое может сделать eBay-скрейпер в 2026 году. eBay использует , а основной способ обнаружения у Akamai — TLS-фингерпринт. Обычный requests отправляет JA3-подпись, характерную для Python, и мгновенно попадает под фильтр. curl_cffi имитирует TLS-рукопожатие настоящего Chrome, что помогает обходить примерно 90% защищённых Akamai-целей без headless-браузера.

Пошагово: как парсить результаты поиска eBay на Python

Это основное руководство. Мы будем парсить страницы результатов поиска eBay и извлекать карточки товаров.

  • Сложность: начальный–средний уровень
  • Время: около 30 минут до первого рабочего парсинга
  • Что понадобится: Python 3.11+, библиотеки выше, терминал и URL поиска на eBay

Шаг 1: Подготовьте Python-проект

Создайте папку проекта и установите зависимости:

1mkdir ebay-scraper && cd ebay-scraper
2python -m venv venv
3source venv/bin/activate  # Windows: venv\Scripts\activate
4pip install requests curl_cffi beautifulsoup4 lxml pandas

Создайте файл scrape_ebay.py. Это будет ваша рабочая область.

Шаг 2: Сформируйте URL поиска eBay

Структура поискового URL у eBay довольно простая. Главный параметр — _nkw (ключевое слово):

1import urllib.parse
2keyword = "airpods pro"
3base_url = "https://www.ebay.com/sch/i.html"
4params = {
5    "_nkw": keyword,
6    "_ipg": "120",    # товаров на странице: 60, 120 или 240 (240 может вызвать подозрение у антибота)
7    "_pgn": "1",      # номер страницы
8}
9url = f"{base_url}?{urllib.parse.urlencode(params)}"
10print(url)
11# https://www.ebay.com/sch/i.html?_nkw=airpods+pro&_ipg=120&_pgn=1

Другие полезные параметры:

  • LH_BIN=1 — только Buy It Now
  • _sacat=175673 — конкретная категория
  • _sop=12 — сортировка по best match (10 = самая низкая цена+доставка, 13 = недавно добавленные)
  • LH_Complete=1&LH_Sold=1 — проданные / завершённые объявления (об этом ниже есть отдельный раздел)

Шаг 3: Отправьте запрос и обработайте ответ

Здесь curl_cffi действительно оправдывает себя. Обычный requests.get() часто возвращает 403 от Akamai. С curl_cffi мы имитируем реальный Chrome-браузер:

1from curl_cffi import requests as cffi_requests
2import random, time
3USER_AGENTS = [
4    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
5    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
6    "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0",
7]
8HEADERS = {
9    "User-Agent": random.choice(USER_AGENTS),
10    "Accept-Language": "en-US,en;q=0.9",
11    "Accept-Encoding": "gzip, deflate, br",
12}
13def fetch_page(url, max_retries=5):
14    delay = 2
15    for attempt in range(max_retries):
16        try:
17            r = cffi_requests.get(url, impersonate="chrome124", headers=HEADERS, timeout=30)
18            if r.status_code == 200:
19                return r.text
20            if r.status_code in (403, 429, 503):
21                retry_after = r.headers.get("Retry-After")
22                sleep_for = float(retry_after) if retry_after else delay + random.uniform(0, 1)
23                print(f"  Статус {r.status_code}, повтор через {sleep_for:.1f} сек...")
24                time.sleep(sleep_for)
25                delay *= 2
26                continue
27            r.raise_for_status()
28        except Exception as e:
29            print(f"  Ошибка запроса: {e}, повторяем...")
30            time.sleep(delay)
31            delay *= 2
32    raise RuntimeError(f"Не удалось получить страницу после {max_retries} попыток: {url}")

Экспоненциальная задержка с разбросом по времени важна — фиксированные интервалы сами по себе являются бот-фингерпринтом.

Шаг 4: Разберите карточки товаров на странице поиска

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

ПолеСтарый макетНовый макет
Контейнер карточкиli.s-itemli.s-card или div.su-card-container
Заголовок.s-item__title.s-card__title
Ссылкаa.s-item__link[href]a.su-link[href]
Ценаspan.s-item__price.s-card__price

Код разбора, который поддерживает оба варианта:

1from bs4 import BeautifulSoup
2def parse_search_results(html):
3    soup = BeautifulSoup(html, "lxml")
4    cards = soup.select("li.s-item, li.s-card, div.su-card-container")
5    results = []
6    for card in cards:
7        # Заголовок — пробуем оба макета
8        title_el = card.select_one(".s-item__title, .s-card__title")
9        title = title_el.get_text(strip=True) if title_el else None
10        # Пропускаем фальшивую карточку-заглушку «Shop on eBay»
11        if not title or "Shop on eBay" in title:
12            continue
13        # Цена
14        price_el = card.select_one("span.s-item__price, .s-card__price")
15        price = price_el.get_text(strip=True) if price_el else None
16        # URL
17        link_el = card.select_one("a.s-item__link[href], a.su-link[href]")
18        url = link_el["href"].split("?")[0] if link_el else None
19        # Изображение
20        img_el = card.select_one("img.s-item__image-img, .s-card__image img")
21        image = None
22        if img_el:
23            image = img_el.get("src") or img_el.get("data-src")
24        # Доставка
25        ship_el = card.select_one("span.s-item__shipping, span.s-item__logisticsCost, .s-card__attribute-row")
26        shipping = ship_el.get_text(strip=True) if ship_el else None
27        results.append({
28            "title": title,
29            "price": price,
30            "url": url,
31            "image": image,
32            "shipping": shipping,
33        })
34    return results

Ловушка с первой карточкой — классическая ошибка. Первый li.s-item на многих страницах поиска eBay — это скрытая заглушка с заголовком «Shop on eBay» и без реальной цены. Обязательно фильтруйте её.

Шаг 5: Обработайте пагинацию и соберите несколько страниц

eBay использует параметр _pgn для пагинации. Ссылка на следующую страницу обычно имеет вид a.pagination__next:

1import urllib.parse
2def scrape_ebay_search(keyword, max_pages=5):
3    all_results = []
4    for page_num in range(1, max_pages + 1):
5        params = {"_nkw": keyword, "_ipg": "120", "_pgn": str(page_num)}
6        url = f"https://www.ebay.com/sch/i.html?{urllib.parse.urlencode(params)}"
7        print(f"Парсим страницу {page_num}: {url}")
8        html = fetch_page(url)
9        results = parse_search_results(html)
10        if not results:
11            print(f"  На странице {page_num} нет результатов, останавливаемся.")
12            break
13        all_results.extend(results)
14        print(f"  Найдено {len(results)} объявлений (всего: {len(all_results)})")
15        # Вежливая пауза — 3–8 секунд со случайным разбросом
16        time.sleep(random.uniform(3, 8))
17    return all_results

Случайная пауза 3–8 секунд — не опция, а необходимость.

eBay и Akamai отслеживают устойчивый поток запросов выше 1 req/s с одного IP.

Шаг 6: Экспортируйте данные в CSV или JSON

1import pandas as pd
2results = scrape_ebay_search("airpods pro", max_pages=3)
3df = pd.DataFrame(results)
4df.to_csv("ebay_airpods.csv", index=False)
5df.to_json("ebay_airpods.json", orient="records", indent=2)
6print(f"Экспортировано {len(df)} объявлений в CSV и JSON.")

Теперь у вас должна быть аккуратная таблица с объявлениями eBay. На моей машине парсинг 3 страниц (360 объявлений) занял около 45 секунд с учётом задержек.

Как парсить страницы товаров eBay на Python

Поиск даёт сводку. А страницы товара содержат самое ценное: полное описание, рейтинг продавца, характеристики товара, карусели изображений и данные по вариантам.

Разбор страницы конкретного объявления

Страницы товаров eBay находятся по адресу /itm/<ITEM_ID>. Самый надёжный способ извлечения данных — JSON-LD: eBay вставляет блок схемы Product, который переживает почти любые изменения CSS:

1import json
2def parse_item_page(html):
3    soup = BeautifulSoup(html, "lxml")
4    item = {}
5    # 1. JSON-LD — самый стабильный способ извлечения
6    for tag in soup.find_all("script", type="application/ld+json"):
7        try:
8            data = json.loads(tag.string or "")
9        except (json.JSONDecodeError, TypeError):
10            continue
11        if isinstance(data, dict) and data.get("@type") == "Product":
12            item["title"] = data.get("name")
13            item["brand"] = (data.get("brand") or {}).get("name")
14            item["images"] = data.get("image")
15            offers = data.get("offers") or {}
16            item["price"] = offers.get("price")
17            item["currency"] = offers.get("priceCurrency")
18            break
19    # 2. CSS-резервные селекторы для полей, которых нет в JSON-LD
20    def first_text(selectors):
21        for sel in selectors:
22            el = soup.select_one(sel)
23            if el and el.get_text(strip=True):
24                return el.get_text(strip=True)
25        return None
26    item.setdefault("title", first_text([
27        "h1.x-item-title__mainTitle",
28        "h1.x-item-title__mainTitle .ux-textspans--BOLD",
29    ]))
30    item["condition"] = first_text([
31        ".x-item-condition-text .ux-textspans",
32    ])
33    item["seller"] = first_text([
34        ".x-sellercard-atf__info__about-seller a .ux-textspans",
35    ])
36    item["shipping"] = first_text([
37        "div.ux-labels-values--shipping .ux-textspans--BOLD",
38    ])
39    # 3. Характеристики товара
40    specifics = {}
41    for dl in soup.select(".ux-layout-section-evo__item--table-view dl.ux-labels-values"):
42        k = dl.select_one(".ux-labels-values__labels-content .ux-textspans")
43        v = dl.select_one(".ux-labels-values__values-content .ux-textspans")
44        if k and v:
45            specifics[k.get_text(strip=True).rstrip(":")] = v.get_text(strip=True)
46    item["specifics"] = specifics
47    return item

Здесь главное правило — сначала JSON-LD, потом CSS-резервные варианты. Именно это помогает строить парсеры, которые не ломаются каждый квартал. Ниже поясню подробнее.

Как парсить варианты товаров eBay (данные MSKU)

У некоторых объявлений eBay есть несколько вариантов — разные цвета, размеры, объёмы памяти. Визуальный DOM показывает только диапазон цен вроде «$899 to $1,099», пока пользователь не выберет вариант. Реальные цены по каждому варианту хранятся в скрытом JavaScript-объекте MSKU.

Это как раз тот случай, где API eBay отдаёт только частичные данные (родительский SKU), поэтому парсинг оказывается лучше.

1import re, json
2def extract_variants(html):
3    # Негрубое сопоставление критично — жадный .+ захватит всю страницу
4    m = re.search(r'"MSKU"\s*:\s*(\{.+?\})\s*,\s*"QUANTITY"', html, re.DOTALL)
5    if not m:
6        return []
7    try:
8        msku = json.loads(m.group(1))
9    except json.JSONDecodeError:
10        return []
11    item_labels = {str(k): v["displayLabel"] for k, v in msku.get("menuItemMap", {}).items()}
12    skus = []
13    for combo_key, variation_id in msku.get("variationCombinations", {}).items():
14        option_ids = combo_key.split("_")
15        options = [item_labels.get(oid, oid) for oid in option_ids]
16        var = msku.get("variationsMap", {}).get(str(variation_id), {})
17        bin_model = var.get("binModel", {})
18        price_spans = bin_model.get("price", {}).get("textSpans", [{}])
19        price = price_spans[0].get("text") if price_spans else None
20        qty = var.get("quantity")
21        skus.append({
22            "options": options,
23            "price": price,
24            "quantity_available": qty,
25            "variation_id": variation_id,
26        })
27    return skus

Именно этот негрубый (.+?) в регулярном выражении ломает большинство парсеров eBay. Жадный .+ захватывает всё до последнего "QUANTITY" на странице и в итоге даёт битый JSON. Я видел эту ошибку минимум в трёх «рабочих» руководствах.

Как парсить проданные и завершённые объявления eBay на Python

Это тот сценарий, ради которого парсинг часто и нужен вместо API. Данные о проданных товарах — что реально купили, по какой цене и в какой день — это золотой стандарт для исследования рынка, оценки ресейла и appraisal. В Browse API eBay этого нет. Marketplace Insights API , но доступ к нему относится к категории Limited Release и .

Нужные параметры URL: LH_Complete=1 (завершённые объявления) и LH_Sold=1 (только реально проданные). Нужно передавать оба параметра. Если указать только LH_Sold=1, в некоторых категориях eBay тихо вернёт активные объявления — это ошибка, на которую чаще всего попадаются.

1def scrape_sold_listings(keyword, max_pages=3):
2    all_sold = []
3    for page_num in range(1, max_pages + 1):
4        params = {
5            "_nkw": keyword,
6            "_ipg": "120",
7            "_pgn": str(page_num),
8            "LH_Complete": "1",
9            "LH_Sold": "1",
10        }
11        url = f"https://www.ebay.com/sch/i.html?{urllib.parse.urlencode(params)}"
12        print(f"Парсим страницу проданных товаров {page_num}...")
13        html = fetch_page(url)
14        soup = BeautifulSoup(html, "lxml")
15        cards = soup.select("li.s-item")
16        for card in cards:
17            title_el = card.select_one(".s-item__title")
18            title = title_el.get_text(strip=True) if title_el else None
19            if not title or "Shop on eBay" in title:
20                continue
21            # Включаем только реально проданные товары (зелёная метка POSITIVE)
22            sold_tag = card.select_one(
23                ".s-item__title--tag .POSITIVE, .s-item__caption--signal.POSITIVE"
24            )
25            if sold_tag is None:
26                continue  # Завершённое, но не проданное объявление — пропускаем
27            price_el = card.select_one("span.s-item__price")
28            price = price_el.get_text(strip=True) if price_el else None
29            # Разбираем дату продажи
30            sold_date = None
31            import re, datetime as dt
32            card_text = card.get_text()
33            m = re.search(r"Sold\s+([A-Z][a-z]{2}\s+\d{1,2},\s*\d{4})", card_text)
34            if m:
35                sold_date = dt.datetime.strptime(m.group(1), "%b %d, %Y").strftime("%Y-%m-%d")
36            link_el = card.select_one("a.s-item__link[href]")
37            url = link_el["href"].split("?")[0] if link_el else None
38            all_sold.append({
39                "title": title,
40                "sold_price": price,
41                "sold_date": sold_date,
42                "url": url,
43            })
44        if not cards:
45            break
46        time.sleep(random.uniform(3, 8))
47    return all_sold

Ключевое отличие в HTML: у реально проданных товаров цена выделена зелёным (в обёртке .POSITIVE), а у завершённых, но не проданных — красная и перечёркнутая. Всегда фильтруйте по классу .POSITIVE.

Почему парсеры eBay ломаются и как строить устойчивые решения

Если ваш парсер eBay перестал работать, вы не одиноки. Это главная боль в любой ветке форума по парсингу eBay, которую я читал. Вопрос не в том, сломается ли ваш парсер, а в том, когда.

Почему это происходит:

  • eBay использует React-рендеринг с динамически генерируемыми CSS-классами, которые меняются при деплое
  • A/B-тесты показывают разную структуру DOM разным пользователям (двойной макет s-item / s-card — живой пример прямо сейчас)
  • Периодические редизайны меняют вложенность HTML, даже если сами данные остаются прежними
  • Старые селекторы вроде #itemTitle и #prcIsum убрали много лет назад, но они до сих пор встречаются в учебниках

Как отмечает : «Главная сложность парсинга eBay — это работа с изменениями CSS-селекторов. eBay регулярно обновляет фронтенд, ломая парсеры, которые завязаны на конкретные названия классов».

ebayscraping_section_50be5cc410.png

Как сделать парсер eBay долговечным

Четыре стратегии, которые переживают квартальные перестройки eBay:

1. Ставьте JSON-LD выше CSS-селекторов. eBay встраивает структурированные данные Product на каждой странице товара. Слой данных меняется намного реже, чем слой отображения: дизайнеры переписывают CSS-классы каждый квартал, а backend-поля вроде price, name и seller привязаны к внутренним API и почти не переименовываются.

2. Используйте каскадные fallback-селекторы. Никогда не полагайтесь на один CSS-селектор. Всегда давайте альтернативы:

1def first_text(soup, selectors):
2    for sel in selectors:
3        el = soup.select_one(sel)
4        if el and el.get_text(strip=True):
5            return el.get_text(strip=True)
6    return None
7title = first_text(soup, [
8    "h1.x-item-title__mainTitle",
9    "h1.x-item-title__mainTitle .ux-textspans--BOLD",
10    "[data-testid='x-item-title'] h1",
11])

3. Разбирайте скрытые JSON-блоки. Объект вариантов MSKU и встроенные JavaScript-данные переживают изменения CSS, потому что формируются на стороне сервера. Регулярное извлечение из <script> требует больше работы на старте, но заметно снижает затраты на поддержку.

4. Логируйте ошибки селекторов. Добавьте мониторинг, чтобы понимать когда селектор перестал совпадать, а не просто получать пустой набор данных:

1if title is None:
2    print(f"WARNING: title selector failed for {url}")

5. Используйте curl_cffi с имитацией браузера. Это помогает обходить TLS-фингерпринт Akamai без headless-браузера.

Альтернатива на базе ИИ: без поддержки селекторов

Если вам надоело каждые несколько месяцев править селекторы, существует принципиально другой подход. Инструменты вроде используют ИИ, чтобы заново читать страницу при каждом запуске и автоматически строить логику извлечения. Исследование McGill University сравнило ИИ и селекторные парсеры на 3 000 страницах и показало, что , а отраслевые бенчмарки говорят о .

ПодходЛомается ли при изменении HTML на eBay?Затраты на поддержку
Жёстко прописанные CSS-селекторыДа, каждый кварталВысокие — постоянные правки
Извлечение скрытого JSON / JSON-LDРедкоНизкие
Парсинг на базе ИИ (Thunderbit)Нет — ИИ переопределяет селекторы при каждом запускеПрактически нет

О рабочем процессе Thunderbit я расскажу подробнее ниже. Пока главное: если вы строите парсер, который должен работать месяцами, делайте упор на JSON-first извлечение и fallback-селекторы. Если же вы не хотите вообще поддерживать селекторы, ИИ-подход точно стоит посмотреть.

Как автоматизировать регулярный парсинг eBay для мониторинга цен

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

Вариант 1: Cron в Linux/macOS или Task Scheduler в Windows

Самый простой способ — завернуть Python-скрипт в cron. Всегда используйте абсолютный путь к Python из вашего virtualenv — cron запускается в минимальном окружении:

1crontab -e
2# Каждый день в 08:15
315 8 * * * /Users/me/ebay/venv/bin/python /Users/me/ebay/scrape_ebay.py >> /Users/me/ebay/scrape.log 2>&1

В Windows используйте PowerShell:

1$A = New-ScheduledTaskAction -Execute "C:\Users\me\ebay\venv\Scripts\python.exe" -Argument "C:\Users\me\ebay\scrape_ebay.py"
2$T = New-ScheduledTaskTrigger -Daily -At 8:15am
3Register-ScheduledTask -TaskName "eBayScraper" -Action $A -Trigger $T

Для этого нужен всегда включённый компьютер, а прокси и антибот-защиту вы настраиваете сами.

Вариант 2: Cloud Functions (serverless)

AWS Lambda или Google Cloud Functions позволяют запускать парсинг без отдельного сервера. Но настройка сложнее: нужно упаковать зависимости, учитывать таймауты (у Lambda максимум 15 минут) и всё равно управлять прокси. Зато сервер не нужно обслуживать.

Вариант 3: No-code расписание в Thunderbit

Функция позволяет описать интервал простыми словами, например «каждый день в 8 утра», указать URL eBay и нажать Schedule. Всё работает в облаке, а антибот-защита встроена.

ПодходСложность настройкиНужен сервер?Есть антибот-защита?
Cron + Python-скриптСредняяДа (машина должна быть включена постоянно)Прокси на вашей стороне
Cloud function (Lambda)ВысокаяНет (serverless)Прокси на вашей стороне
Thunderbit Scheduled ScraperНизкая (всё описывается словами)Нет (облако)Встроена

Для хранения данных повторяющихся парсингов локальная база SQLite — отличный выбор для истории цен. Используйте ON CONFLICT ... DO UPDATE (а не INSERT OR REPLACE, который ):

1CREATE TABLE IF NOT EXISTS listings (
2    item_id       TEXT PRIMARY KEY,
3    title         TEXT NOT NULL,
4    price         REAL,
5    last_price    REAL,
6    first_seen_at TEXT DEFAULT (datetime('now')),
7    last_seen_at  TEXT DEFAULT (datetime('now'))
8);
9CREATE TABLE IF NOT EXISTS price_history (
10    item_id     TEXT NOT NULL,
11    observed_at TEXT NOT NULL DEFAULT (datetime('now')),
12    price       REAL NOT NULL,
13    PRIMARY KEY (item_id, observed_at)
14);

Не хотите кодить? Как парсить eBay за 2 минуты с Thunderbit

Я уже подробно разобрал Python-код. Теперь хочу честно сказать, когда он вам не нужен.

Если вы бизнес-пользователь и делаете разовое исследование рынка, реселлер сравнивает цены или ecommerce-команде нужны данные сегодня, без спринта у разработчиков, Python — это перебор. Настройка, поддержка селекторов, прокси — всё это слишком много накладных расходов ради задачи «мне просто нужны эти 200 объявлений в таблице».

Как Thunderbit парсит eBay (пошагово)

  1. Установите — карта не нужна.
  2. Откройте любую страницу результатов поиска или товара на eBay в Chrome.
  3. Нажмите “AI Suggest Fields” в боковой панели Thunderbit. ИИ прочитает страницу и предложит столбцы: Title, Price, Condition, Shipping, Seller, Rating.
  4. Нажмите “Scrape”. Расширение пройдёт по пагинации и заполнит таблицу данных. Для eBay у Thunderbit есть , которые запускаются в один клик.
  5. Экспортируйте в Google Sheets, Airtable, Notion, CSV, JSON или Excel — бесплатно.

Весь процесс занимает меньше 2 минут.

Я засёк.

Обогащение подстраниц: данные со страницы товара без лишнего кода

После парсинга страницы результатов Thunderbit может перейти на страницу каждого объявления и добавить дополнительные поля — полные характеристики, данные продавца, описание, все изображения. Это заменяет более чем 20 строк Python-кода для парсинга подстраниц, который мы писали выше, одним кликом.

Когда Python всё же лучше

Python выигрывает, когда вам нужно:

  • Масштабное парсирование (десятки тысяч страниц за один запуск)
  • Глубоко кастомная логика разбора или трансформация данных
  • Интеграция в существующие data pipeline (Airflow, dbt, Kafka)
  • Тонкий контроль TLS/сессий для продвинутой антибот-защиты
  • Экономика на больших объёмах — на миллионах строк поддерживаемый стек выгоднее SaaS по подписке или кредитам

Для большинства разовых или средних по масштабу задач Thunderbit быстрее и проще. Для production-пайплайнов на больших объёмах Python даёт полный контроль.

Как не попасть под блокировку при парсинге eBay на Python

eBay и Akamai — это реальность. Что действительно работает на практике:

  • Используйте curl_cffi с impersonate="chrome124" — это самое заметное улучшение по сравнению с обычным requests
  • Ротируйте User-Agent из списка актуальных браузеров (Chrome 143, Firefox 124, Safari 26)
  • Добавляйте случайные паузы по — фиксированные интервалы легко распознаются
  • Используйте residential или rotating proxies для всего, что превышает несколько десятков страниц. Data center IP (AWS, GCP, DigitalOcean) Akamai быстро помечает как подозрительные.
  • Соблюдайте robots.txt — большинство фильтрованных browse URL там явно запрещены; страницы товаров (/itm/<id>) — нет
  • Обрабатывайте CAPTCHA корректно — распознавайте её и повторяйте попытку с другим IP либо используйте сервис решения CAPTCHA
  • Не атакуйте сервер слишком часто. Прецедент показывает, что doctrine trespass to chattels применяется, если парсинг действительно ухудшает работу серверов. Держитесь примерно на уровне 1 req/s на IP — и вы будете далеко от этой границы.

Для коммерческого использования с высокими объёмами лучше сочетать Browse API для активных объявлений и точечный парсинг только для sold comps и тех данных, которых нет в API. Такой гибридный подход чище и технически, и юридически.

Законно ли парсить eBay на Python?

Я не юрист, и эта статья не является юридической консультацией. Поэтому коротко.

Правовая ситуация в целом стала более благоприятной для сбора публично доступных данных. Основные прецеденты:

  • (9-й округ, 2022): парсинг общедоступных данных не нарушает CFAA
  • Van Buren v. United States (Верховный суд США, 2021): сузил трактовку положения CFAA «превышение разрешённого доступа»
  • (Северный округ Калифорнии, 2024): парсинг без входа в аккаунт не нарушает условия платформы, потому что парсер не является «пользователем»

При этом обновление прямо запрещает «buy-for-me agents, LLM-driven bots или любые end-to-end процессы, которые пытаются оформить заказ без участия человека». Граница понятна: чтение публичных страниц — это одно; автоматизация checkout — совсем другое.

Лучшие практики: собирайте только публично видимые данные. Не создавайте фейковые аккаунты и не обходите login wall. Не перепродавайте массово защищённые авторским правом изображения объявлений. А для коммерческих проектов обязательно консультируйтесь с юристами.

Выводы и главные тезисы

Python — самый гибкий способ парсить eBay, но он требует постоянной поддержки, потому что HTML сайта меняется. Логика выбора такая:

  • Используйте eBay Browse API для стабильных структурированных запросов средней интенсивности по активным объявлениям
  • Используйте Python-парсинг для проданных объявлений, отзывов, данных по вариантам и всего, чего нет в API
  • Используйте , если хотите получать данные eBay без написания и поддержки кода

Код в этом руководстве построен на устойчивости: сначала JSON-LD, затем каскадные CSS-fallback-селекторы, а для вариантов — разбор скрытого JSON. Такой многоуровневый подход означает, что парсер не умрёт в тот же день, когда фронтенд-команда eBay выпустит очередной редизайн.

Если хотите попробовать no-code путь, позволяет протестировать его на страницах eBay прямо сейчас. А если хотите посмотреть, как работает , до него всего один клик.

Больше материалов по инструментам веб-парсинга ищите в наших гайдах: , и . Также можно посмотреть обучающие видео на .

Попробовать Thunderbit для парсинга eBay

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

1. Можно ли бесплатно парсить eBay на Python?

Да. Все библиотеки (Requests, BeautifulSoup, curl_cffi, pandas) бесплатны и с открытым исходным кодом. Расходы появляются на масштабе — residential proxies для большого объёма обычно стоят $50–500 в месяц в зависимости от трафика. Для небольших проектов (несколько сотен страниц) можно парсить даже со своего домашнего IP, если аккуратно ограничивать частоту запросов.

2. Как парсить проданные и завершённые объявления eBay на Python?

Добавьте LH_Complete=1&LH_Sold=1 к параметрам поискового URL. Нужно указать оба параметра — LH_Sold=1 по отдельности в некоторых категориях молча возвращает активные объявления. Отфильтруйте результаты по CSS-классу .POSITIVE у элемента цены: он означает реальную продажу, а не просто завершённое, но не проданное объявление.

3. Блокирует ли eBay веб-парсинг?

eBay использует Akamai Bot Manager, который определяет парсеры в первую очередь через TLS-фингерпринт и поведенческий анализ. Обычные запросы requests часто получают 403. Использование curl_cffi с имитацией браузера, ротацией User-Agent и случайными паузами 3–8 секунд между запросами помогает обойти большую часть блокировок. На масштабе помогают residential proxies.

4. Что выбрать: API eBay или веб-парсинг?

Используйте Browse API для стабильных запросов средней частоты по активным объявлениям (до 5 000 запросов в день). Используйте парсинг, когда нужны история цен продаж, полные данные по вариантам/MSKU, отзывы или любые поля, которых нет в API. Marketplace Insights API формально предоставляет данные о продажах, но доступ к нему ограничен и .

5. Как проще всего парсить eBay без кода?

использует ИИ, чтобы читать страницы eBay, предлагать столбцы данных и извлекать объявления в один клик. Оно умеет работать с пагинацией, обогащением подстраниц и экспортом в Google Sheets, Excel, Airtable или Notion. Готовые делают задачу ещё быстрее для типовых сценариев.

Подробнее

Содержание

Попробуй Thunderbit

Собирай лиды и другие данные всего в 2 клика. На базе AI.

Получить Thunderbit Это бесплатно
Извлекай данные с помощью AI
Легко передавай данные в Google Sheets, Airtable или Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week