Как парсить eBay на Python: пошаговая инструкция 2025 | Elevium
RU | EN
HOME PARSING EBAY

Parsing eBay: Step-by-Step Guide

Hello everyone. Today we'll learn eBay parsing. Let's start simple and see how the site works from the inside.

What you'll learn from this article:

  • How to analyze eBay request structure using DevTools
  • Parsing HTML with Python and BeautifulSoup
  • Bypassing bot protection with requests-html
  • Scaling parser to multiple pages
  • Saving results to CSV format

Exploring the Main Page

Главная страница eBay с каталогом товаров и поисковой строкой

We see the main page. Nothing special here. Let's go to search.

Analyzing Search Requests

We'll work with laptops as an example.

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

Wrote the query. Now open the console. Press F12, go to Network. Find the request in the list:

Вкладка Network в DevTools Chrome с запросами к серверу eBay

Response Structure Analysis

Moving forward. Looking at the response structure. We see a mess of symbols and tags:

HTML код ответа сервера eBay с товарами в DevTools

Hard to parse manually. In this case, we can use page search. Take a product characteristic, say "500 gb", and search in the code:

Результат поиска текста 500gb в HTML коде страницы eBay

Found matches. Format is not the most convenient to read. Let's try to extract data using Python.

Parsing HTML with Python

First, save the page HTML to a file. Let's call it response.html. Now let's write parsing code.

What we'll do:

  • Read HTML file into memory
  • Create BeautifulSoup object to work with DOM
  • Find all product links (they contain /itm/ in URL)
  • Extract product name and ID
  • Remove duplicates
  • Save results to CSV file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Простой парсер eBay - извлекает названия товаров и ссылки из HTML файла
"""

import csv
from bs4 import BeautifulSoup
import re

def parse_ebay_html(html_file):
    """Извлекает названия товаров и ссылки из HTML файла eBay"""

    with open(html_file, 'r', encoding='utf-8', errors='ignore') as f:
        html = f.read()

    soup = BeautifulSoup(html, 'html.parser')
    products = []

    print(f"Анализирую файл размером {len(html)} символов...")

    # Ищем ссылки на товары
    for link in soup.find_all('a', href=True):
        href = link.get('href').strip()
        text = link.get_text().strip()

        # Проверяем, что это ссылка на товар eBay
        if '/itm/' in href and text and len(text) > 5:
            # Извлекаем ID товара
            item_id_match = re.search(r'/itm/(\d+)', href)
            item_id = item_id_match.group(1) if item_id_match else ""

            products.append({
                'id': item_id,
                'title': text,
                'url': href
            })

    # Убираем дубликаты по названию
    seen = set()
    unique_products = []
    for product in products:
        title_key = product['title'].lower()
        if title_key not in seen:
            seen.add(title_key)
            unique_products.append(product)

    print(f"Найдено товаров: {len(unique_products)}")
    return unique_products

def save_to_csv(products, filename):
    """Сохраняет товары в CSV файл"""
    with open(filename, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=['id', 'title', 'url'])
        writer.writeheader()
        writer.writerows(products)

def main():
    html_file = "response.html"
    csv_file = "products.csv"

    products = parse_ebay_html(html_file)
    save_to_csv(products, csv_file)

    print(f"Сохранено в: {csv_file}")
    print("\nВСЕ ТОВАРЫ:")
    for i, product in enumerate(products, start=1):
        print(f"{i}. {product['title']}")
        if product['id']:
            print(f"   ID: {product['id']}")
        print(f"   Ссылка: {product['url']}\n")

if __name__ == "__main__":
    main()

Result looks like this:

Консольный вывод Python скрипта с названиями и ID товаров eBay

Got a list of products with names, IDs and links. Ready to work with.

Scaling: Parsing Multiple Pages

Moving forward. Parsing one page is useful, but usually you need more data. So we'll need to scale the code to multiple pages.

First, let's figure out how eBay switches pages. Back to the browser. Click on the second page and look at the request:

HTTP запрос ко второй странице поиска eBay в DevTools

Right-click and copy the request:

Опция Copy as cURL в контекстном меню DevTools Chrome

Open POSTMAN or INSOMNIA. Paste the request and look at parameters:

Интерфейс Postman с импортированным cURL запросом и cookies

Too much here. Let's try removing cookies:

Postman после удаления cookies с чистыми параметрами запроса

Better. We see parameter &_pgn=3. It controls the page number. That's all we need.

Important Note About eBay Protection

Warning: Warning: eBay is protected from bots. This complicates parsing.

With regular requests module, you can make about 8-12 requests. Then eBay will start blocking. Protection checks for JavaScript presence in browser.

Simple solution: use requests-html instead of regular requests. This library can execute JavaScript.

Final Code

Now putting it all together. Code below parses multiple pages and saves results to CSV:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
  
import csv
import time
import random
from requests_html import HTMLSession
import re
from urllib.parse import urlsplit, urlunsplit

class SimpleEbayParser:
    def __init__(self):
        self.session = HTMLSession()
        self.base_url = "https://www.ebay.com/sch/i.html"
        self.params = {
            "_nkw": "notebook",
            "_sacat": "0",
            "_from": "R40",
            "_pgn": 1
        }
        self.all_products = []
  
    def parse_page(self, page_num):
        """Loads and parses a single page"""
        self.params['_pgn'] = page_num
  
        try:
            print(f"Loading page {page_num}...")
            r = self.session.get(self.base_url, params=self.params)
            print(f"Status: {r.status_code}, Size: {len(r.html.html)} characters")
            
            if 'Checking your browser' in r.html.html:
                print("BLOCKING DETECTED - trying JavaScript render...")
                r.html.render(timeout=15, wait=2, sleep=1)
                print(f"After rendering: {len(r.html.html)} characters")

            with open(f'debug_{page_num}.html', 'w', encoding='utf-8') as f:
                f.write(r.html.html)

            products = self.extract_products(r, page_num)
            return products

        except Exception as e:
            print(f"Error on page {page_num}: {e}")
            return []
  

    def extract_products(self, r, page_num):
        """Extracts products from HTML"""
        products = []
        links = r.html.find('a[href*="/itm/"]')
        print(f"Found product links: {len(links)}")

        for link in links:
            try:
                href = link.attrs.get('href', '')
                title = link.text.strip()
                if not href or not title or len(title) < 5:
                    continue

                item_id = ''
                match = re.search(r'/itm/(\d+)', href)
                if match:
                    item_id = match.group(1)

                if href.startswith('/'):
                    href = 'https://www.ebay.com' + href
                clean_url = href.split('?')[0]

                price = "Price not found"
                parent = link.element.getparent()
                for _ in range(3):
                    if parent is not None:
                        parent_text = parent.text_content() if hasattr(parent, 'text_content') else ''
                        price_match = re.search(r'[\$€£₽][\d,]+\.?\d*', parent_text)
                        if price_match:
                            price = price_match.group(0)
                            break
                        parent = parent.getparent()
                    else:
                        break
  
                products.append({
                    'page': page_num,
                    'id': item_id,
                    'title': title,
                    'url': clean_url,
                    'price': price
                })
  
            except Exception as e:
                print(f"Error processing link: {e}")
                continue

        seen_titles = set()
        unique_products = []
        for product in products:
            title_key = product['title'].lower()
            if title_key not in seen_titles and len(product['title']) > 10:
                seen_titles.add(title_key)
                unique_products.append(product)
  
        print(f"Unique products: {len(unique_products)}")
        return unique_products


    def parse_multiple_pages(self, start=1, end=3):
        """Parses multiple pages"""
        print(f"Starting to parse pages {start}-{end}")

        for page_num in range(start, end + 1):
            products = self.parse_page(page_num)
            self.all_products.extend(products)
            print(f"Page {page_num}: {len(products)} products")
            
            if page_num < end:
                pause = random.uniform(3, 7)
                print(f"Pause {pause:.1f} sec...")
                time.sleep(pause)

        print(f"Total found: {len(self.all_products)} products")


    def save_results(self, filename='simple_results.csv'):
        """Saves results to CSV"""
        if not self.all_products:
            print("No data to save")
            return

        with open(filename, 'w', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=['page', 'id', 'title', 'url', 'price'])
            writer.writeheader()
            writer.writerows(self.all_products)

        print(f"Saved to {filename}")


    def print_results(self):
        """Shows results"""
        if not self.all_products:
            print("No results")
            return

        print("\nResults:")
        for i, product in enumerate(self.all_products[:10], 1):
            print(f"{i:2d}. {product['title'][:60]}...")
            print(f"    Price: {product['price']} | ID: {product['id']}")
            print()

  
def main():
    parser = SimpleEbayParser()
    try:
        parser.parse_multiple_pages(1, 3)
        parser.save_results()
        parser.print_results()

    except KeyboardInterrupt:
        print("Interrupted")
    except Exception as e:
        print(f"Error: {e}")
    finally:
        parser.session.close()

if __name__ == "__main__":
    main()

In the end we get a CSV file with data:

Таблица CSV с результатами парсинга eBay: названия, ID, цены товаров

Conclusion

Covered the complete eBay parsing process. From analyzing site structure to working parser with protection bypass. What's important to remember:

  • DevTools helps understand request structure
  • BeautifulSoup handles HTML parsing excellently
  • Requests-html bypasses eBay protection via JavaScript
  • Pauses between requests reduce ban risk
  • CSV is good for storing results

This approach can be applied to other marketplaces. The key is understanding site structure and accounting for its protection.

Frequently Asked Questions (FAQ)

Is eBay parsing legal?

Parsing public eBay data is technically possible, but you need to comply with site usage rules. Check eBay's terms of use and robots.txt. For commercial data use, it's recommended to use eBay's official API.

Why does requests get blocked after a few requests?

eBay uses bot protection that checks for JavaScript in the browser. Regular requests doesn't execute JS, so it gets blocked quickly. Solution: use requests-html, which can render JavaScript.

Can you parse eBay without saving HTML file?

Yes. The example with saving HTML is for learning. In real code, you make a request directly through requests-html and immediately parse the result with BeautifulSoup.

How many pages can you parse at once?

With requests-html and 3-7 second pauses between requests, you can parse dozens of pages without blocking. But for large volumes it's better to use proxies and distributed requests.

What are the alternatives to BeautifulSoup?

Alternatives: lxml (faster, but more complex), Scrapy (framework for large projects), Selenium (if you need complex site interaction). For simple tasks BeautifulSoup is optimal.

How to update data automatically?

Use task schedulers: cron (Linux/Mac) or Task Scheduler (Windows). You can set up automatic script execution every day or hour to update product database.