Skip to main content

Webparsers.com

Ebay stands as the world’s largest peer-to-peer e-commerce marketplace, making it a compelling target for public data collection!

This comprehensive guide will walk you through scraping Ebay search and product listing pages to extract various details including pricing information, variant data, product features, and descriptions.

We’ll leverage Python alongside several community packages and implement sophisticated parsing techniques. Let’s dive in!

Key Takeaways

Master ebay scraper development using Python with httpx and parsel, extracting product data from hidden web data and handling anti-bot measures for comprehensive e-commerce data collection.

  • Reverse engineer eBay’s search API endpoints by intercepting browser network requests and analyzing JSON response structures
  • Parse dynamic JSON data embedded in HTML using XPath selectors for product details and variants
  • Bypass eBay’s anti-scraping measures with realistic headers, user agents, and request spacing
  • Extract structured product data including titles, prices, descriptions, and seller information
  • Implement exponential backoff retry logic with 403 status code detection for rate limiting
  • Handle multi-variant products and dynamic pricing through advanced JSON parsing and data extraction techniques

Why Scrape Ebay?

Ebay represents one of the world’s largest product marketplaces, particularly for niche and rare items. This positioning makes Ebay an excellent target for e-commerce data analytics.

Scraping Ebay data enables various use cases, including:

  • Competitor analysis by gathering data on competitors’ sales and reviews.
  • Market research by tracking product prices for hot deals or trends.
  • Empowered navigation through automated search patterns and custom alerts.

For further details, refer to our introduction on web scraping use cases.

Setup

Web scraping Ebay requires utilizing several Python community packages:

In this tutorial, we’ll be working with Python alongside two essential community libraries:

  • webparsers-sdk: A Python SDK for WebParsers, a web scraping API that bypasses web scraping blocking
  • jmespath: For refining and parsing JSON datasets
  • nested-lookup: To find nested keys in the Ebay JSON datasets

The above packages can be installed using the below pip command:

$ pip install webparsers-sdk jmespath nested-lookup

Scraping Ebay Listings

Let’s begin by scraping Ebay for individual listing pages. Ebay listings come in two main types:

  • Single variant listings with fixed selections
  • Multiple variant listings with different selections, like tech devices

First, we’ll focus on scraping single variants since they present a more straightforward extraction process.

We’ll be utilizing single variants since they offer more straightforward extraction. Let’s examine this product as an example, where we’ll extract data from the following fields:

We’ll capture the most essential fields: pricing, description and product and seller details

In the image above we marked our target fields and to construct CSS selectors for these fields we can utilize the Browser Developer Tools (F12 key or right click → inspect option).

To scrape the above Ebay listing data, we’ll employ CSS and XPath selectors:

import json
import os
import re
import asyncio

from typing import Dict, List
from nested_lookup import nested_lookup
from webparsers import ScrapeApiResponse, ScrapeConfig, WebparsersClient, WebparsersScrapeError

BASE_CONFIG = {
    "asp": True,
    "country": "US",
    "lang": ["en-US"]
}

WEBPARSERS = WebparsersClient(key=os.environ["API_KEY"])

def parse_product(result: ScrapeApiResponse):
    """Parse Ebay's product listing page for core product data"""
    sel = result.selector
    css_join = lambda css: "".join(sel.css(css).getall()).strip()  # join all selected elements
    css = lambda css: sel.css(css).get("").strip()  # take first selected element and strip of leading/trailing spaces

    item = {}
    item["url"] = css('link[rel="canonical"]::attr(href)')
    item["id"] = item["url"].split("/itm/")[1].split("?")[0]  # we can take ID from the URL
    item["price_original"] = css(".x-price-primary>span::text")
    item["price_converted"] = css(".x-price-approx__price ::text")  # ebay automatically converts price for some regions

    item["name"] = css_join("h1 span::text")
    item["seller_name"] = sel.xpath("//div[contains(@class,'info__about-seller')]/a/span/text()").get()
    item["seller_url"] = sel.xpath("//div[contains(@class,'info__about-seller')]/a/@href").get().split("?")[0]
    item["photos"] = sel.css('.ux-image-filmstrip-carousel-item.image img::attr("src")').getall()  # carousel images
    item["photos"].extend(sel.css('.ux-image-carousel-item.image img::attr("src")').getall())  # main image
    # description is an iframe (independant page). We can keep it as an URL or scrape it later.
    item["description_url"] = css("iframe#desc_ifr::attr(src)")
    # feature details from the description table:
    feature_table = sel.css("div.ux-layout-section--features")
    features = {}
    for feature in feature_table.css("dl.ux-labels-values"):
        # iterate through each label of the table and select first sibling for value:
        label = "".join(feature.css(".ux-labels-values__labels-content > div > span::text").getall()).strip(":\n ")
        value = "".join(feature.css(".ux-labels-values__values-content > div > span *::text").getall()).strip(":\n ")
        features[label] = value
    item["features"] = features
    return item


async def scrape_product(url: str) -> Dict:
    """Scrape ebay.com product listing page for product data"""
    print(f"scraping product: {url}")
    page = await WEBPARSERS.async_scrape(ScrapeConfig(url, **BASE_CONFIG))
    product = parse_product(page)
    return product


async def main():
    product_data = await scrape_product("https://www.ebay.com/itm/332562282948")

    # save the results to a json file
    with open("product_data.json", "w", encoding="utf-8") as f:
        json.dump(product_data, f, indent=2, ensure_ascii=False)


if __name__ == "__main__":
    asyncio.run(main())

Let’s break down the above Ebay scraping code. We begin by defining a new Webparsers client and define two functions:

  • parse_product: to parse the product HTML pages using CSS and XPath selectors
  • scrape_product: To request Ebay product pages using Webparsers to bypass its antibot and retrieve the HTML

Below is example output of the Ebay data retrieved

Next, for products with variants we’ll need to go a bit further and extract the page’s hidden web data. It might seem like a complex process, though we’ll cover it step-by-step!

Scraping Ebay Listing Variant Data

Ebay’s listings can contain multiple products through a feature called variants. For example, let’s examine this iPhone listing:

Listings with variants have multiple selection options

We can observe several variant options: model, storage capacity, and color. These options get updated using JavaScript each time we select one.

Ebay utilizes JavaScript to update the page with different pricing every time we choose a different option. This means that the variant data exists in a JavaScript variable. Extracting these data is commonly referred to as hidden web data.

We’ll briefly mention the hidden web data extraction in this guide. For the complete details, refer to our dedicated tutorial.

How to Scrape Hidden Web Data

The visible HTML doesn’t always represent the complete dataset available on the page. In this article, we’ll be examining the scraping of hidden web data. What is it and how can we scrape it using Python?

To scrape the product variant data, we’ll extract them as JSON under hidden script tags:

import json
import os
import re
import asyncio

from typing import Dict, List
from collections import defaultdict
from nested_lookup import nested_lookup
from webparsers import ScrapeApiResponse, ScrapeConfig, WebparsersClient

BASE_CONFIG = {
    "asp": True,
    "country": "US",
    "lang": ["en-US"]
}

WEBPARSERS = WebparsersClient(key=os.environ["API_KEY"])

def _find_json_objects(text: str, decoder=json.JSONDecoder()):
    """Find JSON objects in text, and generate decoded JSON data"""
    pos = 0
    while True:
        match = text.find("{", pos)
        if match == -1:
            break
        try:
            result, index = decoder.raw_decode(text[match:])
            yield result
            pos = match + index
        except ValueError:
            pos = match + 1

def parse_variants(result: ScrapeApiResponse) -> dict:
    """
    Parse variant data from Ebay's listing page of a product with variants.
    This data is located in a js variable MSKU hidden in a