What the MediaMarkt scraper collects
Scrapes MediaMarkt category grids, search results and product pages across the group's ten country sites. Prices come from structured data confirmed against the rendered tile, with the RRP and the EU 30-day lowest price kept in separate columns; product mode adds brand, EAN, description and the full image list. Output is JSON or CSV plus a run-metadata sidecar.
Category grid
Input: A leaf category URLOne row per product, twelve per page: title, price, currency, RRP, discount, 30-day low, rating, review count and stock, paginated with ?page=N.
Search results
Input: A MediaMarkt search URLThe same row schema for search results, which paginate the same way as categories.
Product page
Input: A MediaMarkt product URLOne row with everything from a listing plus brand, EAN, description and the full image list.
From zero to MediaMarkt data in 3 steps
Install
Clone the repository and install one browser engine. Python 3.9+.
$ git clone https://github.com/2scraper/mediamarkt-scraper $ cd mediamarkt-scraper $ pip install -r requirements.txt -r requirements-playwright.txt $ playwright install chromium $ python3 smoke_test.py
Run a scraper
Pick what you need and run it. Every flag is documented in the README.
$ python3 playwright_scraper.py --url "https://www.mediamarkt.de/de/category/grills-116.html" --pages 3$ python3 playwright_scraper.py --url "https://www.mediamarkt.de/de/search.html?query=usb-c+kabel" --pages 5$ python3 playwright_scraper.py --mode product --url "https://www.mediamarkt.de/de/product/_harry-potter-the-complete-collection-dvd-2920911.html"Get structured data
Rows land in JSON or CSV (
--format), with a.meta.jsonsidecar recording status, pages fetched and why the run stopped.
MediaMarkt data fields
22 columns per row in a predictable schema. The most useful ones:
| Field | What it holds |
|---|---|
sku | MediaMarkt article number |
title | Product title |
url | Product page URL |
price | Current price as a number |
currency | ISO currency: EUR, CHF, PLN, HUF or TRY depending on the site |
original_price | Manufacturer's recommended price (UVP/RRP) when shown |
discount_pct | Discount computed from price and original_price; null with no RRP |
lowest_price_30d | The EU-mandated lowest price of the last 30 days, kept apart from the RRP |
rating | Star rating; null for unrated products rather than 0 |
review_count | Number of reviews |
in_stock | Availability |
price_source | jsonld+dom when structured data and the tile agree, jsonld or dom otherwise |
category | Category from the URL |
brand | Brand (--mode product; listings publish none) |
ean | EAN barcode (--mode product) |
images | Full image list (--mode product) |
[
{
"source": "mediamarkt.de",
"scraped_at": "2026-09-09T09:32:33.779503+00:00",
"url": "https://www.mediamarkt.de/de/product/_gorenje-rk518e2s4-kuhlgefrierkombination-e-269-l-…",
"sku": "3047256",
"title": "GORENJE RK518E2S4 Kühlgefrierkombination (E, 269 l, 1800 mm hoch, Silber)",
"brand": null,
"price": 279.0,
"currency": "EUR",
"original_price": 459.0,
"discount_pct": 39.2,
"rating": 4.2,
"review_count": 5,
"in_stock": true,
"image_url": "https://assets.mmsrg.com/isr/166325/c1/-/ASSET_MMS_180626469",
"category": "kühlen-gefrieren",
"price_source": "jsonld+dom",
"page": 2,
"position": 1,
"lowest_price_30d": null,
"ean": null,
"description": null,
"images": null
},
{
"source": "mediamarkt.de",
"scraped_at": "2026-09-09T09:32:29.335391+00:00",
"url": "https://www.mediamarkt.de/de/product/_koenic-kfk-631-1-c-in-kuhlgefrierkombination-c-14…",
"sku": "2882323",
"title": "KOENIC KFK 631-1 C IN Kühl-Kombi statisch (C, 315 l, 185,5 cm hoch, Edelstahl)",
"brand": null,
"price": 349.0,
"currency": "EUR",
"original_price": null,
"discount_pct": null,
"rating": 4.5966,
"review_count": 119,
"in_stock": true,
"image_url": "https://assets.mmsrg.com/isr/166325/c1/-/ASSET_MMS_167152757",
"category": "kühlen-gefrieren",
"price_source": "jsonld+dom",
"page": 1,
"position": 1,
"lowest_price_30d": 299.0,
"ean": null,
"description": null,
"images": null
}
]First 2 of 5 rows from a real run — sample_output.json. Long values shortened here.
Handles what breaks naive MediaMarkt scrapers
RRP and 30-day low told apart
MediaMarkt renders two struck-through prices that mean opposite things: the RRP above the price, and the EU 30-day lowest price usually below it. They get separate columns, so no row reports a negative discount.
German round prices parsed
Prices like "349,– €", percent badges written before the number in Turkish, split euro and cent nodes, and instalment lines inside the price block are all handled, and pinned by offline tests on real captures.
Refusals told apart from empty categories
A 403 refusal is MediaMarkt's own branded error page with no vendor markers; it is detected by status and by the absence of the shop's own assets. A hub category with no grid is exit 4, a correct empty answer, not a block.
Every row confirmed against its tile
Structured prices are checked against the rendered tile and recorded in price_source, and diff_runs.py reports a price difference that comes with a source change as source_changed, not as a price change.
Scraping infrastructure included
The same tested core runs every 2scraper repository, so the hard parts work the same way on every site.
CAPTCHA handling
Detects the challenge a page really serves and, with a 2Captcha key, solves it only when it blocks the data — a capped number of solves per page.
Proxy rotation
Single proxy or a pool from a file, rotated on blocks with a fresh browser per exit. Credentials stay out of argv and logs.
Fingerprints
Optional complete device identity from the 2Captcha Fingerprint API — user agent, client hints, locale and timezone that agree with each other.
Scraping Browser API
Point --cdp-endpoint at a managed Chromium with its own exit country — no browser infrastructure to run yourself.
Honest run metadata
Every run writes a .meta.json sidecar and distinct exit codes: complete, blocked, empty and partial are never confused, and a failed run never overwrites good data.
Diff two runs
diff_runs.py compares two complete MediaMarkt snapshots by id — new, removed and changed rows for monitoring.
What teams do with MediaMarkt data
Consumer electronics price monitoring
Track prices, RRP discounts and 30-day lows for categories or search queries across MediaMarkt's European sites.
Cross-country price comparison
Compare the same products on mediamarkt.de, .at, .nl, .es, mediaworld.it and five more sites, in EUR, CHF, PLN, HUF and TRY.
Product catalogue enrichment
Use --mode product to collect brand, EAN, description and image lists for matching against your own catalogue.
Promotion and compliance checks
Watch how advertised discounts relate to the published 30-day lowest price over time.
Free to run. Pay only for what the site requires.
Needs a residential exit or the Scraping Browser API
MediaMarkt refuses every datacentre and VPN address with HTTP 403 (measured Sep 2026), so a residential exit is required, and it should be in the site's own country: one German address was refused by seven of the ten sites. Residential proxies are therefore the requirement, not an optimisation; the Scraping Browser API gives the same thing as one endpoint with a chosen country. The refusal page carries no captcha, so captcha solving is not what gets you in here. With a residential exit, the 2Captcha Scraper API is the cheapest path at about $0.0005 a page.
Optional 2Captcha services — captcha solving, the Scraping Browser API, residential proxies and fingerprints — work behind one key.
Good to know
- A residential exit in the site's own country is required; datacentre addresses get 403.
- brand and ean are only filled by --mode product.
- --concurrency is available in the Playwright engine only.
2scraper vs. hosted scraper APIs
| 2scraper | Typical hosted API | |
|---|---|---|
| Code | Open source, MIT — read and patch it | Closed |
| Cost | Free; optional services billed only when used | Subscription or per request / per result |
| Runs on | Your machine, server or CI | The vendor's infrastructure |
| Output | JSON/CSV files + run metadata | API responses |
| When the site changes | Fix it yourself or open an issue / PR | Wait for the vendor |
MediaMarkt scraper FAQ
Why do I get exit 3 on every page?
MediaMarkt refuses datacentre, VPS, CI and most VPN addresses. Use a residential proxy or the Scraping Browser API, with an exit in the country of the site you are reading.
Which MediaMarkt countries are supported?
Ten: mediamarkt.de, .at, .ch, .nl, .be, .es, .pl, .hu, .com.tr and mediaworld.it. mediamarkt.lu runs on a different platform and Saturn is unverified, so both are refused with that reason.
Why does a category return zero products?
Some MediaMarkt categories are hubs that list sub-categories and have no product grid. That is exit 4, a correct answer; pick a leaf category.
Why is brand null on listing rows?
MediaMarkt's listing tiles and JSON-LD publish no brand. --mode product reads the brand and the EAN from the detail page.
How does pagination work?
Twelve products per page, addressed directly with ?page=N. The run stops when a page adds no new product, and --concurrency can fetch pages in parallel with a proxy pool.
Is scraping MediaMarkt legal?
It reads publicly rendered product listings and nothing behind an account. Public data scraping is generally lawful, but follow MediaMarkt's terms, robots.txt and the law where you operate.