Scrape eBay Pythonilla: Koodi, joka oikeasti toimii

Viimeksi päivitetty April 16, 2026
Scrape eBay Pythonilla: Koodi, joka oikeasti toimii

Suurin osa eBayn scraping-oppaista vanhenee noin kolmessa kuukaudessa. Tiedän tämän, koska Thunderbitillä olemme seuranneet, kuinka kehittäjät kiertävät vuodesta toiseen rikkinäisten koodipätkien, vanhentuneiden CSS-selectorien ja “toimivien” GitHub-repojen välillä, jotka lakkasivat hiljaisesti toimimasta jo kaksi eBayn käyttöliittymäuudistusta sitten.

eBayssa on noin 2,5 miljardia aktiivista ilmoitusta — Amazonin jälkeen suurin avoimen verkon pitkä häntä -hinnoitteludatan lähde. Tuota dataa hyödynnetään jälleenmyyjien hinnoittelusta kilpailija-analytiikkaan. Sen saaminen ohjelmallisesti on kuitenkin jatkuvasti liikkuva maali: eBayn React-pohjainen käyttöliittymä vaihtaa CSS-luokkia, A/B-testit näyttävät eri käyttäjille eri DOM-rakenteita, ja Akamai Bot Manager seisoo sinun ja HTML:n välissä. Tämä opas antaa sinulle Python-koodin, joka toimii tänään, selittää miksi scraperit hajoavat, jotta voit rakentaa kestävämpiä ratkaisuja, käy rehellisesti läpi eBay API:n ja scrapingin välisen valinnan, ja näyttää myös no-code-vaihtoehdon tilanteisiin, joissa Python ei ole vaivan arvoinen.

Mitä eBayn scraping Pythonilla tarkoittaa?

eBayn scrappaaminen Pythonilla tarkoittaa sitä, että kirjoitat skriptejä, jotka lataavat eBayn sivuja ohjelmallisesti, jäsentävät HTML:n (tai piilotetun JSON:n) ja poimivat rakenteista dataa — kuten otsikoita, hintoja, myyjätietoja, myyntipäiviä ja varianttitietoja — muotoon, jota voit oikeasti käyttää, esimerkiksi CSV:nä, taulukkona tai tietokantana.

Voit scrapata useita eBay-sivutyyppejä:

  • Hakutulossivut (esim. kaikki “AirPods Pro” -ilmoitukset)
  • Yksittäiset tuotesivut (täydet tiedot, kuvat, myyjätiedot)
  • Myydyt / päättyneet ilmoitukset (todelliset kauppahinnat ja päivämäärät)
  • Myyjäprofiilit ja arvostelut

Python on tähän työskentelyyn luonteva valinta. Sen ekosysteemi — Requests, BeautifulSoup, lxml, pandas — tekee sivujen hakemisesta, HTML:n jäsentämisestä ja datan käsittelystä suoraviivaista. On kuitenkin tärkeä ero sen välillä, scrapaatko sivun HTML:ää vai käytätkö eBayn virallista API:a — palataan siihen hetken päästä.

Miksi eBaytä kannattaa scrapata? Käytännön käyttötapauksia liiketoiminnalle

Jos luet tätä, sinulla on todennäköisesti jo jokin syy mielessäsi. On silti hyödyllistä ankkuroida keskustelu konkreettiseen liiketoiminta-arvoon, koska eBay-datan ROI on aidosti vaikuttava. Bainin mukaan 1 % parannus toteutuneessa hinnassa kasvattaa voittoa 11,1 % tuhansissa yrityksissä. McKinseyn mukaan dynaaminen hinnoittelu voi kasvattaa vähittäiskaupan myyntiä jopa 5 % ja katetta 2–7 %.

Yleisimmät käyttötapaukset, joihin törmään:

KäyttötapausTarvittava dataLiiketoimintahyöty
Hintaseuranta ja uudelleenhinnotteluAktiivisten ilmoitusten hinnat, toimitus, kuntoKilpailukykyinen hinnoittelu, marginaalin suojaaminen
Kilpailija-analyysiTuotevalikoima, kampanjat, toimitusehdotStrateginen positiointi, valikoimavajeiden tunnistaminen
Markkinatutkimus ja trendien tunnistusIlmoitusten määrä, kategoriatrendit, kysyntämallitUusien tuotteiden löytäminen, kysynnän ennustaminen
Jälleenmyyntihinnoittelu / arviointiMyyntihinnat, myyntipäivät, kuntoKäypä markkina-arvo, ostopäätökset
SentimenttianalyysiArvostelut, tähtiarviot, palautuskäytäntöTuotelaadun ja asiakastyytyväisyyden ymmärtäminen
Liidien generointiMyyjäprofiilit, kauppatiedot, yhteystiedotB2B-yhteydenotto korkean GMV:n myyjiin

ebayscraping_stats_77c7da1611.png

Yhteinen nimittäjä on tämä: eBaylla on data, mutta se on lukittu verkkosivuille.

Scraping muuttaa sen kilpailueduksi.

eBayn virallinen API vai Python-web scraping: kumpi kannattaa valita?

Tämän kysymyksen toivoisin useampien oppaiden käsittelevän rehellisesti. eBay tarjoaa virallisia API-rajapintoja — etenkin Browse API:n — ja moni pohtii, kannattaako niitä käyttää vai scrapata suoraan. Vastaus riippuu täysin siitä, millaista dataa tarvitset.

KriteerieBay Browse/Finding APIPython-web scraping
Myydyt / päättyneet ilmoituksetRajoitetusti — Marketplace Insights API on olemassa, mutta käyttö estyy useinTäysi pääsy LH_Sold=1&LH_Complete=1 URL-parametreilla
Rajoitukset kutsumäärissä5 000 kutsua/päivä perusportaassaItse hallittu (riippuu proxysta)
Datan kentätEnnaltamääritellyt (otsikko, hinta, kategoria, myyjän perustiedot)Kaikki sivulla näkyvä (arvostelut, täydet speksit, varianttitaulukko)
Käyttöönoton monimutkaisuusOAuth 2.0, sovelluksen rekisteröinti, API-avaimetpip install + koodi
VakavuusVakaat päätepisteetHajoaa, kun HTML muuttuu
KustannusIlmainen taso, maksullinen käytön mukaanKoodi on ilmaista, mutta proxyt maksavat skaalassa
Variantti-/MSKU-dataOsittainen — usein vain parent SKUTäydellinen (piilotetusta JSON:sta parsittuna)
Sivutuksen syvyys10 000 kohteen kovakattoTeoriassa rajaton

Pieni huomio: vanha Finding API (jossa oli findCompletedItems) poistettiin käytöstä kokonaan helmikuussa 2025. Jos käytät ebaysdk-python-kirjastoa tai mitä tahansa kirjastoa, joka osuu Finding-moduuliin, se on tällä hetkellä tuotannossa rikki.

Suositukseni: käytä Browse API:a, kun tarvitset vakaata, keskivolyymistä ja rakenteista katalogikyselyä aktiivisista ilmoituksista. Käytä Python-scrapingia, kun tarvitset myyntihintoja, arvosteluja, varianttidataa tai muuta kenttää, jota API ei tarjoa. Monet tiimit käyttävät molempia.

Tarvittavat työkalut ja kirjastot eBayn scrappaamiseen Pythonilla

Ennen kuin kirjoitamme yhtään koodia, tässä on työkalupakki. Et tarvitse headless-selainta useimpiin eBay-sivuihin — data on upotettuna palvelimella renderöityyn HTML:ään.

KirjastoTarkoitus
requests tai httpxHTTP-asiakas eBay-sivujen lataamiseen
curl_cffiHTTP-asiakas, joka käyttää oikean selaimen TLS-fingerpinttiä (kriittinen Akamain ohittamiseen)
beautifulsoup4HTML-parseri CSS-selector-poimintaan
lxmlNopea parser-tausta BeautifulSoupille
jmespathKyselykieli sisäkkäisen JSON-massan jäsentämiseen
pandasDatan käsittely ja CSV/Excel-vienti
gspreadGoogle Sheets -integraatio

Asenna kaikki yhdellä komennolla:

pip install requests httpx curl_cffi beautifulsoup4 lxml jmespath pandas gspread

Käytä Python 3.11+:aa — pandas 3.0 vaatii vähintään 3.10:n, ja 3.11 tuo 10–60 % nopeushyötyjä I/O-painotteiseen työhön.

Yksi kirjasto ansaitsee erityismaininnan: curl_cffi on yksittäinen tärkein parannus, jonka vuoden 2026 eBay-scraper voi tehdä. eBay käyttää Akamai Bot Manageria, ja Akamain tärkein tunnistusmenetelmä on TLS-fingerprinting. Tavallinen requests lähettää Pythonin näköisen JA3-fingerprin tin, joka hylätään heti. curl_cffi matkii aidon Chrome-selaimen TLS-kättelyä, ja sillä pääsee noin 90 %:iin Akamain suojaamista kohteista ilman headless-selainta.

Kokeile Thunderbitia mille tahansa sivustolle

Vaihe vaiheelta: näin scrappaat eBayn hakutulokset Pythonilla

Tämä on oppaan ydin. Scrappaamme eBayn hakutulossivuja tuotelistauksia varten.

  • Vaativuustaso: aloittelija–keskitaso
  • Aika: noin 30 minuuttia ensimmäiseen toimivaan scrapaukseen
  • Tarvitset: Python 3.11+, yllä mainitut kirjastot, terminaalin ja kohde-eBay-hakua varten URL:n

Vaihe 1: Luo Python-projekti

Luo projektikansio ja asenna riippuvuudet:

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

Luo tiedosto nimeltä scrape_ebay.py. Se on työtilasi.

Vaihe 2: Rakenna eBay-haku-URL

eBayn hakurakenne on suoraviivainen. Tärkein parametri on _nkw (keyword):

import urllib.parse

keyword = "airpods pro"
base_url = "https://www.ebay.com/sch/i.html"
params = {
    "_nkw": keyword,
    "_ipg": "120",    # kohteita per sivu: 60, 120 tai 240 (240 voi laukaista bot-hälytyksiä)
    "_pgn": "1",      # sivunumero
}
url = f"{base_url}?{urllib.parse.urlencode(params)}"
print(url)
# https://www.ebay.com/sch/i.html?_nkw=airpods+pro&_ipg=120&_pgn=1

Muita hyödyllisiä parametreja:

  • LH_BIN=1 — vain Buy It Now
  • _sacat=175673 — tietty kategoria
  • _sop=12 — järjestä paras osuma -logiikalla (10 = halvin hinta + toimitus, 13 = uusin ilmoitus)
  • LH_Complete=1&LH_Sold=1 — myydyt/päättyneet ilmoitukset (käsitellään omassa osiossaan alla)

Vaihe 3: Lähetä pyyntö ja käsittele vastaus

Tässä curl_cffi todella maksaa itsensä takaisin. Tavallinen requests.get() palauttaa usein Akamailta 403:n. curl_cffi-kirjastolla esiinnytään oikeana Chrome-selaimena:

from curl_cffi import requests as cffi_requests
import random, time

USER_AGENTS = [
    "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",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0",
]

HEADERS = {
    "User-Agent": random.choice(USER_AGENTS),
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
}

def fetch_page(url, max_retries=5):
    delay = 2
    for attempt in range(max_retries):
        try:
            r = cffi_requests.get(url, impersonate="chrome124", headers=HEADERS, timeout=30)
            if r.status_code == 200:
                return r.text
            if r.status_code in (403, 429, 503):
                retry_after = r.headers.get("Retry-After")
                sleep_for = float(retry_after) if retry_after else delay + random.uniform(0, 1)
                print(f"  Tilanne {r.status_code}, yritetään uudelleen {sleep_for:.1f} sekunnin kuluttua...")
                time.sleep(sleep_for)
                delay *= 2
                continue
            r.raise_for_status()
        except Exception as e:
            print(f"  Pyyntövirhe: {e}, yritetään uudelleen...")
            time.sleep(delay)
            delay *= 2
    raise RuntimeError(f"Epäonnistui {max_retries} yrityksen jälkeen: {url}")

Eksponentiaalinen backoff satunnaisella jitterillä on tärkeä — kiinteät odotusvälitkin ovat botin tunnusmerkki.

Vaihe 4: Jäsennä tuotelistaukset hakusivulta

eBay on parhaillaan siirtymässä kahden hakutulossivun asettelun välillä. Kestävän scraperin täytyy tukea molempia:

KenttäVanha asetteluUusi asettelu
Kortin säiliöli.s-itemli.s-card tai div.su-card-container
Otsikko.s-item__title.s-card__title
URLa.s-item__link[href]a.su-link[href]
Hintaspan.s-item__price.s-card__price

Koodi, joka tukee molempia asetteluja:

from bs4 import BeautifulSoup

def parse_search_results(html):
    soup = BeautifulSoup(html, "lxml")
    cards = soup.select("li.s-item, li.s-card, div.su-card-container")
    results = []

    for card in cards:
        # Otsikko — kokeile molempia asetteluja
        title_el = card.select_one(".s-item__title, .s-card__title")
        title = title_el.get_text(strip=True) if title_el else None

        # Ohita eBayn "Shop on eBay" -paikkamerkkikortti
        if not title or "Shop on eBay" in title:
            continue

        # Hinta
        price_el = card.select_one("span.s-item__price, .s-card__price")
        price = price_el.get_text(strip=True) if price_el else None

        # URL
        link_el = card.select_one("a.s-item__link[href], a.su-link[href]")
        url = link_el["href"].split("?")[0] if link_el else None

        # Kuva
        img_el = card.select_one("img.s-item__image-img, .s-card__image img")
        image = None
        if img_el:
            image = img_el.get("src") or img_el.get("data-src")

        # Toimitus
        ship_el = card.select_one("span.s-item__shipping, span.s-item__logisticsCost, .s-card__attribute-row")
        shipping = ship_el.get_text(strip=True) if ship_el else None

        results.append({
            "title": title,
            "price": price,
            "url": url,
            "image": image,
            "shipping": shipping,
        })

    return results

Se ensimmäinen haamutulos on klassinen kompastuskivi. Monilla eBayn hakusivuilla ensimmäinen li.s-item on piilotettu paikkamerkki “Shop on eBay”, eikä sillä ole oikeaa hintaa. Suodata se aina pois.

Vaihe 5: Käsittele sivutus useamman sivun scrapaukseen

eBayn sivutus toimii _pgn-parametrilla. Seuraava sivu löytyy linkistä a.pagination__next:

import urllib.parse

def scrape_ebay_search(keyword, max_pages=5):
    all_results = []

    for page_num in range(1, max_pages + 1):
        params = {"_nkw": keyword, "_ipg": "120", "_pgn": str(page_num)}
        url = f"https://www.ebay.com/sch/i.html?{urllib.parse.urlencode(params)}"
        print(f"Scrapataan sivu {page_num}: {url}")

        html = fetch_page(url)
        results = parse_search_results(html)

        if not results:
            print(f"  Ei tuloksia sivulla {page_num}, lopetetaan.")
            break

        all_results.extend(results)
        print(f"  Löytyi {len(results)} ilmoitusta (yhteensä: {len(all_results)})")

        # Hidas, kohtelias odotus — 3–8 sekuntia satunnaisella vaihtelulla
        time.sleep(random.uniform(3, 8))

    return all_results

3–8 sekunnin satunnainen viive ei ole vapaaehtoinen.

eBayn Akamai-kerros havaitsee yli 1 pyynnön sekunnissa yhdestä IP-osoitteesta.

Vaihe 6: Vie scraped data CSV- tai JSON-muotoon

import pandas as pd

results = scrape_ebay_search("airpods pro", max_pages=3)
df = pd.DataFrame(results)
df.to_csv("ebay_airpods.csv", index=False)
df.to_json("ebay_airpods.json", orient="records", indent=2)
print(f"Viety {len(df)} ilmoitusta CSV- ja JSON-muotoon.")

Nyt sinulla pitäisi olla siisti taulukko eBay-ilmoituksista. Omalla koneellani 3 sivun scraping (360 ilmoitusta) kesti noin 45 sekuntia viiveet mukaan lukien.

Näin scrappaat eBayn tuotesivut Pythonilla

Hakutulokset antavat yhteenvedon. Tuotesivuilla on varsinainen hyötydata: täydelliset kuvaukset, myyjän palautearviot, tuotekohtaiset tiedot, kuvakarusellit ja varianttidata.

Yksittäisen tuotesivun jäsentäminen

eBayn tuotesivut löytyvät osoitteesta /itm/<ITEM_ID>. Vakain poimintatapa on JSON-LD — eBay upottaa Product-schema-lohkon, joka säilyy lähes kaikissa CSS-muutoksissa:

import json

def parse_item_page(html):
    soup = BeautifulSoup(html, "lxml")
    item = {}

    # 1. JSON-LD — vakain poimintatapa
    for tag in soup.find_all("script", type="application/ld+json"):
        try:
            data = json.loads(tag.string or "")
        except (json.JSONDecodeError, TypeError):
            continue
        if isinstance(data, dict) and data.get("@type") == "Product":
            item["title"] = data.get("name")
            item["brand"] = (data.get("brand") or {}).get("name")
            item["images"] = data.get("image")
            offers = data.get("offers") or {}
            item["price"] = offers.get("price")
            item["currency"] = offers.get("priceCurrency")
            break

    # 2. CSS-varmistukset kentille, joita JSON-LD ei sisällä
    def first_text(selectors):
        for sel in selectors:
            el = soup.select_one(sel)
            if el and el.get_text(strip=True):
                return el.get_text(strip=True)
        return None

    item.setdefault("title", first_text([
        "h1.x-item-title__mainTitle",
        "h1.x-item-title__mainTitle .ux-textspans--BOLD",
    ]))
    item["condition"] = first_text([
        ".x-item-condition-text .ux-textspans",
    ])
    item["seller"] = first_text([
        ".x-sellercard-atf__info__about-seller a .ux-textspans",
    ])
    item["shipping"] = first_text([
        "div.ux-labels-values--shipping .ux-textspans--BOLD",
    ])

    # 3. Tuotekohtaiset tiedot
    specifics = {}
    for dl in soup.select(".ux-layout-section-evo__item--table-view dl.ux-labels-values"):
        k = dl.select_one(".ux-labels-values__labels-content .ux-textspans")
        v = dl.select_one(".ux-labels-values__values-content .ux-textspans")
        if k and v:
            specifics[k.get_text(strip=True).rstrip(":")] = v.get_text(strip=True)
    item["specifics"] = specifics

    return item

Tässä kaava on tämä: ensin JSON-LD, sitten CSS-varmistukset. Se on avain scraperiin, joka ei hajoa joka vuosineljännes.

eBayn tuotevarianttien (MSKU-datan) scrappaaminen

Joissakin eBay-ilmoituksissa on useita variantteja — eri värejä, kokoja tai tallennuskapasiteetteja. Näkyvä DOM näyttää vain hintahaarukan, kuten “$899 to $1,099”, kunnes käyttäjä klikkaa vaihtoehtoa. Todellinen varianttikohtainen hinnoittelu elää piilotetussa JavaScript-objektissa nimeltä MSKU.

Tämä on yksi niistä tilanteista, joissa eBay API antaa vain osittaisen tiedon (parent SKU), ja scraping on parempi ratkaisu.

import re, json

def extract_variants(html):
    # Ei-ahnas match on kriittinen — ahne .+ nielee koko sivun
    m = re.search(r'"MSKU"\s*:\s*(\{.+?\})\s*,\s*"QUANTITY"', html, re.DOTALL)
    if not m:
        return []

    try:
        msku = json.loads(m.group(1))
    except json.JSONDecodeError:
        return []

    item_labels = {str(k): v["displayLabel"] for k, v in msku.get("menuItemMap", {}).items()}
    skus = []

    for combo_key, variation_id in msku.get("variationCombinations", {}).items():
        option_ids = combo_key.split("_")
        options = [item_labels.get(oid, oid) for oid in option_ids]
        var = msku.get("variationsMap", {}).get(str(variation_id), {})
        bin_model = var.get("binModel", {})
        price_spans = bin_model.get("price", {}).get("textSpans", [{}])
        price = price_spans[0].get("text") if price_spans else None
        qty = var.get("quantity")

        skus.append({
            "options": options,
            "price": price,
            "quantity_available": qty,
            "variation_id": variation_id,
        })

    return skus

Juuri tuo ei-ahnas (.+?) regexissä kaataa jokaisen eBay-scraperin jossain vaiheessa. Ahne .+ imee kaiken viimeiseen "QUANTITY"-kohtaan asti ja tuottaa rikkonaisen JSON:n. Olen nähnyt tämän bugina ainakin kolmessa “toimivassa” oppaassa.

Näin scrappaat eBayn myydyt ja päättyneet ilmoitukset Pythonilla

Tämä on se käyttötapaus, joka todella perustelee scrapingin API:n sijaan. Myyty data — mitä oikeasti kauppautui, millä hinnalla ja minä päivänä — on kultainen standardi markkinatutkimuksessa, jälleenmyyntihinnoittelussa ja arvioinnissa. eBayn Browse API ei tarjoa tätä lainkaan. Marketplace Insights API tarjoaa teknisesti tämän, mutta käyttö on “Limited Release” -tasolla ja hylätään usein.

Tarvitsemasi URL-parametrit ovat LH_Complete=1 (päättyneet ilmoitukset) ja LH_Sold=1 (vain oikeasti myydyt). Molemmat on annettava. Pelkkä LH_Sold=1 palaa joissakin kategorioissa hiljaisesti aktiivisiin ilmoituksiin — tämä on yleisin yhteisön kompastuskivi.

def scrape_sold_listings(keyword, max_pages=3):
    all_sold = []

    for page_num in range(1, max_pages + 1):
        params = {
            "_nkw": keyword,
            "_ipg": "120",
            "_pgn": str(page_num),
            "LH_Complete": "1",
            "LH_Sold": "1",
        }
        url = f"https://www.ebay.com/sch/i.html?{urllib.parse.urlencode(params)}"
        print(f"Scrapataan myytyjen sivu {page_num}...")

        html = fetch_page(url)
        soup = BeautifulSoup(html, "lxml")
        cards = soup.select("li.s-item")

        for card in cards:
            title_el = card.select_one(".s-item__title")
            title = title_el.get_text(strip=True) if title_el else None
            if not title or "Shop on eBay" in title:
                continue

            # Vain todella myydyt ilmoitukset mukaan (vihreä POSITIVE-hinta)
            sold_tag = card.select_one(
                ".s-item__title--tag .POSITIVE, .s-item__caption--signal.POSITIVE"
            )
            if sold_tag is None:
                continue  # Myymättä jäänyt päättynyt ilmoitus — ohita

            price_el = card.select_one("span.s-item__price")
            price = price_el.get_text(strip=True) if price_el else None

            # Poimi myyntipäivä
            sold_date = None
            import re, datetime as dt
            card_text = card.get_text()
            m = re.search(r"Sold\s+([A-Z][a-z]{2}\s+\d{1,2},\s*\d{4})", card_text)
            if m:
                sold_date = dt.datetime.strptime(m.group(1), "%b %d, %Y").strftime("%Y-%m-%d")

            link_el = card.select_one("a.s-item__link[href]")
            url = link_el["href"].split("?")[0] if link_el else None

            all_sold.append({
                "title": title,
                "sold_price": price,
                "sold_date": sold_date,
                "url": url,
            })

        if not cards:
            break
        time.sleep(random.uniform(3, 8))

    return all_sold

HTML:ssa tärkeä ero on tämä: myydyt kohteet näyttävät hinnan vihreänä (.POSITIVE-wrapperin sisällä), kun taas myymättä jääneet päättyneet ilmoitukset näyttävät hinnan punaisena ja yliviivattuna. Suodata aina .POSITIVE-luokan perusteella.

Miksi eBay-scraperit hajoavat (ja miten rakennat kestäviä)

Jos eBay-scraperisi lakkasi toimimasta, et ole yksin. Tämä on lukemieni eBay-scraping-ketjujen ykköskipu. Kysymys ei ole siitä hajoaako scraperisi — vaan milloin.

Miksi näin tapahtuu:

  • eBay käyttää React-pohjaista renderöintiä ja dynaamisesti luotuja luokkia, jotka muuttuvat julkaisuissa
  • A/B-testit näyttävät eri DOM-rakenteita eri käyttäjille (nyt nähtävä s-item / s-card -kaksoisasettelu on tästä elävä esimerkki)
  • Säännölliset käyttöliittymäuudistukset muuttavat HTML:n sisäkkäisyyttä, vaikka data pysyy samana
  • Vanhat selectorit kuten #itemTitle ja #prcIsum poistettiin vuosia sitten, mutta ne elävät yhä opetusmateriaaleissa

Kuten Scrapflyn vuoden 2026 oppaassa todetaan: “The real challenge with eBay web scraping is handling eBay's CSS selector changes. eBay updates their frontend regularly, breaking scrapers that rely on specific class names.”

ebayscraping_section_50be5cc410.png

Puolustusstrategiat pitkäikäisille eBay-scrapereille

Neljä strategiaa, jotka kestävät eBayn neljännesvuosittaiset muutokset:

1. Priorisoi JSON-LD CSS-selectoreiden sijaan. eBay upottaa jokaiselle tuotesivulle rakenteista Product-schema-dataa. Datakerros muuttuu paljon harvemmin kuin esityskerros — suunnittelijat uusivat CSS-luokkia neljännesvuosittain, mutta taustajärjestelmän kentät kuten price, name ja seller viittaavat sisäisiin API-rajapintoihin ja nimet muuttuvat harvoin.

2. Käytä peräkkäisiä varaselectoreita. Älä koskaan luota vain yhteen CSS-selectoriin. Anna aina vaihtoehtoja:

def first_text(soup, selectors):
    for sel in selectors:
        el = soup.select_one(sel)
        if el and el.get_text(strip=True):
            return el.get_text(strip=True)
    return None

title = first_text(soup, [
    "h1.x-item-title__mainTitle",
    "h1.x-item-title__mainTitle .ux-textspans--BOLD",
    "[data-testid='x-item-title'] h1",
])

3. Parsii piilotetut JSON-lohkot. MSKU-varianttiobjekti ja upotettu JavaScript-data kestävät CSS-muutoksia, koska ne generoidaan palvelimella. Regex-poiminta <script>-tageista vaatii aluksi enemmän työtä, mutta vähentää ylläpitoa selvästi.

4. Lokita selector-virheet. Lisää monitorointi, jotta tiedät milloin selector lakkaa osumasta, etkä vain sitä, että data on tyhjää:

if title is None:
    print(f"VAROITUS: otsikkoselector epäonnistui URL:ssa {url}")

5. Käytä curl_cffi:tä selaimen impersonoinnilla. Tämä auttaa Akamain TLS-fingerprintingiin ilman headless-selainta.

AI-pohjainen vaihtoehto: ei selectorien ylläpitoa

Jos kyllästyt paikkaamaan selectoreita muutaman kuukauden välein, on olemassa täysin erilainen lähestymistapa. Työkalut kuten Thunderbit käyttävät tekoälyä lukemaan sivun joka kerta tuoreesti ja päättelemään poimintalogiikan lennossa. McGillin yliopiston tutkimus testasi AI- ja selector-pohjaisia scrapereita 3 000 sivulla ja havaitsi, että AI-menetelmät säilyttivät 98,4 %:n tarkkuuden myös asettelumuutosten jälkeen, ja alan vertailuissa on raportoitu 60–80 % vähemmän scraperin ylläpitotyötä.

LähestymistapaHajoaako, kun eBay muuttaa HTML:ää?Ylläpitotarve
Kovakoodatut CSS-selectoritKyllä, neljännesvuosittainKorkea — jatkuvia korjauksia
Piilotettu JSON / JSON-LD -poimintaHarvoinMatala
AI-pohjainen scraping (Thunderbit)Ei — AI päättelee selectorit uudelleen joka ajollaEi käytännössä lainkaan

Scrapaa eBay-dataa AI:lla Get Started Free

Käsittelen Thunderbit-työnkulun myöhemmin tarkemmin. Tärkein oppi tässä vaiheessa: jos rakennat scraperia, jota aiot ajaa kuukausia, panosta JSON-ensimmäiseen poimintaan ja varaselectoreihin. Jos taas et halua ylläpitää selectoreita lainkaan, AI-lähestymistapa on ehdottomasti tutustumisen arvoinen.

Toistuvien eBay-scrapien automatisointi hintaseurantaa varten

Kertaluontoinen scrape on hyödyllinen. Mutta hintaseuranta, varaston seuranta ja kilpailija-analyysi vaativat toistuvaa tiedonkeruuta. Jokainen lukemani kilpailija-artikkeli mainitsee hintaseurannan käyttötapauksena, mutta lähes yksikään ei näytä, miten se oikeasti automatisoidaan.

Vaihtoehto 1: Cron-jobit (Linux/macOS) tai Task Scheduler (Windows)

Yksinkertaisin tapa. Kääri Python-skriptisi cron-työhön. Käytä aina virtuaaliympäristön Pythonin absoluuttista polkua — cron toimii hyvin minimaalisessa ympäristössä:

crontab -e
# Päivittäin klo 08:15
15 8 * * * /Users/me/ebay/venv/bin/python /Users/me/ebay/scrape_ebay.py >> /Users/me/ebay/scrape.log 2>&1

Windowsissa voit käyttää PowerShelliä:

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

Tämä vaatii aina päällä olevan koneen, ja hallitset proxyt sekä anti-bot-toimet itse.

Vaihtoehto 2: Pilvifunktiot (serverless)

AWS Lambda tai Google Cloud Functions mahdollistavat scrappauksen ilman omaa palvelinta. Käyttöönotto on työläämpää — riippuvuudet pitää paketoida, aikakatkaisuihin pitää varautua (Lambda rajaa ajon 15 minuuttiin), ja proxyjen hallinta jää silti sinulle. Mutta palvelimen ylläpitoa ei ole.

Vaihtoehto 3: No-code-aikataulutus Thunderbitillä

Thunderbitin Scheduled Scraper -ominaisuudella voit kuvata aikavälin luonnollisella kielellä (esim. “joka päivä klo 8”), syöttää eBay-URL:t ja klikata Schedule. Se ajaa pilvessä sisäänrakennetulla anti-bot-käsittelyllä.

LähestymistapaKäyttöönoton vaivaTarvitseeko palvelimen?Käsittelee anti-botin?
Cron + Python-skriptiKeskitasoKyllä (aina päällä oleva kone)Proxyt sinun hallinnassasi
Pilvifunktio (Lambda)KorkeaEi (serverless)Proxyt sinun hallinnassasi
Thunderbit Scheduled ScraperMatala (kuvataan sanoin)Ei (pilvipohjainen)Sisäänrakennettu

Toistuvan scrapen datan tallennukseen paikallinen SQLite-tietokanta on paras ratkaisu hintahistorialle. Käytä ON CONFLICT ... DO UPDATE -rakennetta (ei INSERT OR REPLACE -komentoa, joka rikkoo vierasavaimet ja pyyhkii sarakkeita):

CREATE TABLE IF NOT EXISTS listings (
    item_id       TEXT PRIMARY KEY,
    title         TEXT NOT NULL,
    price         REAL,
    last_price    REAL,
    first_seen_at TEXT DEFAULT (datetime('now')),
    last_seen_at   TEXT DEFAULT (datetime('now'))
);

CREATE TABLE IF NOT EXISTS price_history (
    item_id     TEXT NOT NULL,
    observed_at TEXT NOT NULL DEFAULT (datetime('now')),
    price       REAL NOT NULL,
    PRIMARY KEY (item_id, observed_at)
);

Kokeile Thunderbit Scheduled Scraperia

Etkö halua koodata? Näin scrappaat eBayn 2 minuutissa Thunderbitillä

Olen käyttänyt 2 000 sanaa Python-koodiin. Nyt haluan olla rehellinen siitä, milloin sitä ei tarvitse.

Jos olet liiketoimintakäyttäjä, joka tekee kertaluonteista markkinatutkimusta, jälleenmyyjä, joka tarkistaa vertailukohteita, tai verkkokauppatiimi, joka tarvitsee datan tänään ilman dev-sprinttiä, Python on usein liikaa. Käyttöönotto, selectorien ylläpito ja proxyjen hallinta — kaikki tämä on paljon overheadia siihen nähden, että “tarvitsen vain nämä 200 ilmoitusta taulukkoon”.

Näin Thunderbit scrapaa eBayn (vaihe vaiheelta)

  1. Asenna Thunderbit Chrome -laajennus — luottokorttia ei tarvita.
  2. Siirry Chrome-selaimessa mille tahansa eBayn hakutulos- tai tuotesivulle.
  3. Klikkaa Thunderbit-sivupalkista “AI Suggest Fields”. AI lukee sivun ja ehdottaa sarakkeita: Title, Price, Condition, Shipping, Seller, Rating.
  4. Klikkaa “Scrape.” Laajennus käy läpi sivutuksen ja täyttää datataulukon. Juuri eBaytä varten Thunderbitillä on valmiita instant scraper -templatemalleja, jotka toimivat yhdellä klikkauksella.
  5. Vie data Google Sheetsiin, Airtableen, Notioniin, CSV:ksi, JSON:ksi tai Exceliin — ilmaiseksi.

Koko prosessi kestää alle 2 minuuttia.

Testasin sen.

Alasivujen rikastus: hae lisätiedot tuotesivuilta ilman lisäkoodia

Kun hakutulossivu on scrapattu, Thunderbit voi avata jokaisen ilmoituksen tuotesivun ja lisätä taulukkoon lisäkenttiä — täydet speksit, myyjätiedot, kuvauksen, kaikki kuvat. Tämä korvaa aiemmin kirjoittamamme yli 20 rivin Pythonin alasivujen scraping-koodin yhdellä klikkauksella.

Milloin Python on silti parempi

Python voittaa, kun tarvitset:

  • Suuren mittakaavan scrapingia (kymmeniä tuhansia sivuja ajokertaa kohden)
  • Syvästi räätälöityä jäsentelylogiikkaa tai datan muokkausta
  • Integraatiota olemassa oleviin datavirtoihin (Airflow, dbt, Kafka)
  • Tarkkaa TLS-/istuntokontrollia edistyneeseen anti-bot-työhön
  • Yksikkötaloudellisuutta — miljoonien rivien mittakaavassa ylläpidetty oma stack voittaa krediittipohjaisen SaaS-mallin

Useimpiin kertaluonteisiin tai keskisuuriin projekteihin Thunderbit on nopeampi ja helpompi. Tuotantoputkissa skaalassa Python antaa sinulle täyden kontrollin.

Vinkkejä, joilla vältät estot eBaytä scrapatessasi Pythonilla

eBayn Akamai-kerros on todellinen. Käytännössä toimivat keinot:

  • Käytä curl_cffi:tä ja impersonate="chrome124" — tämä on suurin yksittäinen parannus verrattuna tavalliseen requests-kirjastoon
  • Vaihtele User-Agent-stringejä nykyisten selaimien versiolistoista (Chrome 143, Firefox 124, Safari 26)
  • Lisää satunnaiset viiveet 3–8 sekuntia pyyntöjen väliin — kiinteät välit ovat tunnistettava kuvio
  • Käytä residential- tai rotaatioproxyjä kaikkeen muuta kuin muutaman kymmenen sivun työhön. Datacenter-IP:t (AWS, GCP, DigitalOcean) Akamai tunnistaa nopeasti.
  • Kunnioita robots.txt:ää — useimmat suodatetut browse-URL:t on nimenomaisesti kielletty; tuotesivut (/itm/<id>) eivät ole
  • Käsittele CAPTCHA:t siististi — tunnista ne ja yritä uudelleen eri IP:llä tai käytä CAPTCHA-ratkaisupalvelua
  • Älä pommita palvelinta. eBay v. Bidder's Edge -ennakkotapaus osoittaa, että trespass to chattels -periaate voi tulla sovellettavaksi, jos scraping oikeasti kuormittaa palvelimia. Pysyminen noin 1 pyynnössä sekunnissa per IP pitää sinut kaukana tuosta rajasta.

Suurivolyymiseen kaupalliseen käyttöön kannattaa harkita Browse API:a aktiivisiin ilmoituksiin ja kohdennettua scrapingia vain myytyihin vertailukohteisiin ja dataan, jota API ei tarjoa. Tuo hybridimalli on siistimpi sekä teknisesti että juridisesti.

Onko eBayn scrappaaminen Pythonilla laillista?

En ole lakimies, eikä tämä blogikirjoitus ole juridista neuvontaa. Pysytellään siis lyhyenä.

Oikeudellinen maisema on muuttunut julkisesti saatavilla olevan datan scrappaajille suotuisammaksi. Keskeiset ennakkopäätökset:

  • hiQ v. LinkedIn (9. piiri, 2022): julkisesti saatavilla olevan datan scrappaaminen ei riko CFAA:ta
  • Van Buren v. United States (Yhdysvaltain korkein oikeus, 2021): kavensi CFAA:n “exceeds authorized access” -kohtaa
  • Meta v. Bright Data (N.D. Cal., 2024): uloskirjautuneena tehty scraping ei riko alustan käyttöehtoja, koska scraper ei ole “käyttäjä”

Tämä sanottuna eBayn helmikuun 2026 käyttöehtopäivitys kieltää nimenomaisesti “buy-for-me-agentit, LLM-ohjatut botit tai minkä tahansa end-to-end-prosessin, joka yrittää tehdä tilauksen ilman ihmisen tarkistusta”. Raja on selvä: vain luku -tyyppinen julkisten sivujen scraping on turvallisella pohjalla; kassavirran automatisointi ei ole.

Parhaat käytännöt: scrapaa vain julkisesti näkyvää dataa. Älä luo feikkitilejä tai kierrä kirjautumismuureja. Älä jälleenmyy tekijänoikeudella suojattuja ilmoituskuvia massana. Ja kysy tarvittaessa lakineuvontaa kaupallisissa hankkeissa.

Yhteenveto ja tärkeimmät opit

Python on joustavin tapa scrapata eBaytä, mutta se vaatii jatkuvaa ylläpitoa sivuston HTML:n muuttuessa. Päätösmalli:

  • Käytä eBay Browse API:a vakaaseen, keskivolyymiseen ja rakenteiseen kyselyyn aktiivisista ilmoituksista
  • Käytä Python-scrapingia myytyihin ilmoituksiin, arvosteluihin, varianttidataan ja kaikkeen, mitä API ei paljasta
  • Käytä Thunderbitia, jos haluat eBay-dataa ilman koodin kirjoittamista tai ylläpitoa

Tämän oppaan koodi painottaa kestävyyttä: ensin JSON-LD-poiminta, sitten peräkkäiset CSS-varmistukset, ja varianttien kohdalla piilotetun JSON:n jäsentäminen. Tämä kerroksittainen lähestymistapa tarkoittaa, ettei scraperisi kuole seuraavalla kerralla, kun eBayn frontend-tiimi julkaisee uudistuksen.

Jos haluat kokeilla no-code-polkuja, Thunderbitin ilmainen taso antaa sinun testata sitä eBay-sivuilla heti. Ja jos haluat nähdä, miten eBay-scraper-template toimii, se on yhden klikkauksen päässä.

Lisää web scraping -työkaluista löydät oppaistamme parhaista automatisoiduista web scraping -työkaluista, datan scrappaamisesta verkkosivuilta Exceliin ja parhaista Python-web scraping -työkaluista. Voit myös katsoa opetusvideoita Thunderbitin YouTube-kanavalta.

Kokeile Thunderbitia eBayn scrappaamiseen Get Started Free

UKK

1. Voinko scrapata eBaytä ilmaiseksi Pythonilla?

Kyllä. Kaikki kirjastot (Requests, BeautifulSoup, curl_cffi, pandas) ovat ilmaisia ja avoimen lähdekoodin. Kustannukset tulevat esiin skaalassa — residential-proxyt suurivolyymiseen scrapingiin maksavat yleensä 50–500 dollaria kuukaudessa kaistanleveydestä riippuen. Pienissä projekteissa (muutama sata sivua) voit scrapata omasta kotiosoitteestasi, kunhan rajoitat pyyntötahtia huolellisesti.

2. Miten scrappaan eBayn myydyt ja päättyneet ilmoitukset Pythonilla?

Lisää hakusanasi URL-parametreihin LH_Complete=1&LH_Sold=1. Molemmat pitää antaa — pelkkä LH_Sold=1 palaa joissakin kategorioissa hiljaisesti aktiivisiin ilmoituksiin. Suodata tulokset tarkistamalla hintaelementin .POSITIVE CSS-luokka, joka kertoo oikeasta myynnistä eikä myymättä päättyneestä ilmoituksesta.

3. Estääkö eBay web scrapinga?

eBay käyttää Akamai Bot Manageria, joka tunnistaa scrapaajat pääasiassa TLS-fingerprintingin ja käyttäytymisanalyysin avulla. Tavalliset requests-kutsut saavat usein 403-vastauksia. curl_cffi:n käyttö selaimen impersonoinnilla, User-Agentien vaihtelu ja 3–8 sekunnin satunnaiset viiveet pyyntöjen välillä ratkaisevat suurimman osan estoista. Residential-proxyt auttavat skaalassa.

4. Kannattaako käyttää eBay API:a vai web scrapingia?

Käytä Browse API:a, kun tarvitset vakaata, keskivolyymistä kyselyä aktiivisista ilmoituksista (enintään 5 000 kutsua päivässä). Käytä scrapingia, kun tarvitset myyntihintahistoriaa, täyttä variantti-/MSKU-dataa, arvosteluja tai muuta kenttää, jota API ei näytä. Marketplace Insights API tarjoaa teknisesti myyntidataa, mutta käyttö on rajattua ja hylätään usein.

5. Mikä on helpoin tapa scrapata eBaytä ilman koodausta?

Thunderbit Chrome -laajennus käyttää tekoälyä eBay-sivujen lukemiseen, datakenttien ehdottamiseen ja ilmoitusten poimimiseen yhdellä klikkauksella. Se hoitaa sivutuksen, alasivujen rikastamisen ja viennin Google Sheetsiin, Exceliin, Airtableen tai Notioniin. Valmiit eBay scraper -mallit nopeuttavat tavallisimpia käyttötapauksia entisestään.

Lisätietoja

Ke
Ke
Thunderbitin CTO | Senior Data Scientist & ML-asiantuntija Lähes vuosikymmenen kokemuksella koneoppimisesta ja data science -työstä Ke Shen on Columbia Universityn alumni ja entinen Senior Data Scientist Walmart Labsilla. Hänellä on syvällistä, alan kollegoiden tunnustamaa asiantuntemusta Pythonista, R:stä, Javasta ja tilastotieteestä, ja hän jakaa käytännössä koeteltuja oivalluksia siitä, miten monimutkaiset tekoälyalgoritmit viedään teoriasta tuotantokäyttöön sopivaksi arkkitehtuuriksi.

Kokeile Thunderbitia

Poimi liidejä ja muuta dataa vain kahdella klikkauksella. AI:n voimin.

Hanki Thunderbit Se on ilmainen
Poimi dataa AI:n avulla
Siirrä data helposti Google Sheetsiin, Airtableen tai Notioniin
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week