What the Bershka scraper collects
Walks bershka.com's menu and its own /itxrest/ catalogue API to read whole categories or single products, one row per (product, colour, size) SKU. Output is JSON and CSV with a run sidecar, from a plain HTTPS engine, three browser engines or 2Captcha's Scraper API.
Category
Input: A menu trail such as "WOMEN / Clothes / Jeans"Walks every grid under that menu trail and emits one row per SKU, with every menu trail that lists it in categories.
Product
Input: The product id from a product URLReads one product and emits a row for each colour and size it is sold in, with price, discount and availability.
Category list
Input: Nothing — or a --site-localePrints every grid the site's own menu lists, so you can pick the trail to pass to --category.
Other locales
Input: A storefront locale such as deResolves that storefront's store id from the site itself and scrapes it in the local currency and language.
From zero to Bershka data in 3 steps
Install
Clone the repository and install one browser engine. Python 3.9+.
$ git clone https://github.com/2scraper/bershka-scraper.git && cd bershka-scraper $ pip install -r requirements.txt $ pip install -r requirements-playwright.txt $ cp .env.example .env
Run a scraper
Pick what you need and run it. Every flag is documented in the README.
$ python3 api_scraper.py --category "WOMEN / Accessories / Bags and purses"$ python3 api_scraper.py --mode product --product-id 229723104$ python3 api_scraper.py --list-categories$ python3 api_scraper.py --site-locale de --category "DAMEN / SALE"Get structured data
Rows land in JSON or CSV (
--format), with a.meta.jsonsidecar recording status, pages fetched and why the run stopped.
Bershka data fields
37 columns per row in a predictable schema. The most useful ones:
| Field | What it holds |
|---|---|
sku | The size-level SKU — the only id unique per row |
title | Product name as the store shows it |
price | Current price as a number, scaled from minor units |
currency | ISO currency from the store configuration |
original_price | The struck-through price, when there is one |
discount_pct | Discount percentage, stated by the API or computed |
color_name | Colour of this SKU |
size_name | Size label (not always unique within a colour) |
availability | InStock, OutOfStock or BackSoon |
url | The product page URL |
image_url | Product image |
product_id | The product id the URL carries |
reference | Bershka's internal product reference |
categories | Every menu trail that lists this SKU |
country_of_origin | Country of manufacture per size |
locale | Storefront the row came from, e.g. gb or de |
[
{
"source": "bershka.com",
"scraped_at": "2026-09-22T08:26:56.212914+00:00",
"url": "https://www.bershka.com/gb/printed-lace-midi-dress-c0p229736905.html",
"sku": "229732397",
"title": "Printed lace midi dress",
"image_url": "https://static.bershka.net/4/photos2/2026/I/0/1/p/1250/727/712/1250727712_2_1_0.jpg?ts=…",
"price": 11.49,
"currency": "GBP",
"category": "WOMEN / SALE / Bershka / Dresses and jumpsuits",
"price_source": "api-size",
"locale": "gb",
"store_id": 44009506,
"product_id": 229736905,
"bundle_id": 229732385,
"reference": "01250727-I2026",
"display_reference": "1250/727",
"color_id": "712",
"color_name": "Cream",
"size_name": "XS",
"size_id": "101",
"partnumber": "0125072771201-I2026",
"availability": "InStock",
"is_buyable": true,
"back_soon": false,
"original_price": 22.99,
"discount_pct": 50.0,
"discount_source": "api",
"promotion_id": 39851,
"section": "WOMEN",
"family": "Dresses",
"subfamily": "Dresses",
"grid_id": "87687eba-fdc3-4348-8570-972923c68698",
"related_categories": null,
"categories": [
"WOMEN / SALE / Bershka / Dresses and jumpsuits"
],
"country_of_origin": "CAMBODIA",
"page": 1,
"row_index": 1
},
{
"source": "bershka.com",
"scraped_at": "2026-09-22T08:26:56.212973+00:00",
"url": "https://www.bershka.com/gb/printed-lace-midi-dress-c0p229736905.html",
"sku": "229732396",
"title": "Printed lace midi dress",
"image_url": "https://static.bershka.net/4/photos2/2026/I/0/1/p/1250/727/712/1250727712_2_1_0.jpg?ts=…",
"price": 11.49,
"currency": "GBP",
"category": "WOMEN / SALE / Bershka / Dresses and jumpsuits",
"price_source": "api-size",
"locale": "gb",
"store_id": 44009506,
"product_id": 229736905,
"bundle_id": 229732385,
"reference": "01250727-I2026",
"display_reference": "1250/727",
"color_id": "712",
"color_name": "Cream",
"size_name": "S",
"size_id": "102",
"partnumber": "0125072771202-I2026",
"availability": "InStock",
"is_buyable": true,
"back_soon": false,
"original_price": 22.99,
"discount_pct": 50.0,
"discount_source": "api",
"promotion_id": 39851,
"section": "WOMEN",
"family": "Dresses",
"subfamily": "Dresses",
"grid_id": "87687eba-fdc3-4348-8570-972923c68698",
"related_categories": null,
"categories": [
"WOMEN / SALE / Bershka / Dresses and jumpsuits"
],
"country_of_origin": "CAMBODIA",
"page": 1,
"row_index": 2
}
]First 2 of 131 rows from a real run — sample_output.json. Long values shortened here.
Handles what breaks naive Bershka scrapers
The bundle indirection is resolved
Every visible product is a bundle whose own colour list is empty; prices and sizes hang off a nested summary. Reading the obvious field yields products with no price, so the parser reads the right one and folds shared bundles instead of emitting duplicates.
Errors hidden inside HTTP 200
The products endpoint can return an "Item not found" object inside a successful array with nothing saying which id it answers. It is recognised and dropped rather than parsed as a product.
Akamai's interstitial answered once
A cookie-less client can get a 2 KB arithmetic interstitial instead of the storefront. The plain HTTP engine answers it once and keeps the session; retrying without answering cleared it on 0 of 6 exits.
Edge refusals told apart from empty grids
A 403 from Akamai's edge is a refused address, reported as blocked (exit 3); a "view all" grid that lists nothing is a correct empty answer (exit 4); failed requests make a partial run (exit 6), never a silent short one.
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 Bershka snapshots by id — new, removed and changed rows for monitoring.
What teams do with Bershka data
Price and markdown tracking
Record price, original price and discount per SKU daily and diff the runs to see which items went on sale and by how much.
Cross-market price comparison
The same SKU ids appear in every locale, while prices are set per market — compare the gb and de storefronts SKU by SKU.
Size-level stock monitoring
availability is per size and colour, so you can watch which sizes sell out or come back soon.
Assortment analysis
Walk the whole menu or a subtree to map which products sit in which categories, families and sale sections.
Free to run. Pay only for what the site requires.
Free to run · paid add-ons optional
No account is needed and the catalogue API answers a plain requests call — but Akamai's edge accepts or refuses per address. Over ten exits measured on 2026-09-20, four were refused and six were served; a bare GitHub runner reached the API with no proxy. Residential proxies are what a long run needs to find and keep accepted exits; the Scraping Browser API is an option if you have a profile and no proxy, and the 2Captcha Scraper API runs it with no local browser.
Optional 2Captcha services — captcha solving, the Scraping Browser API, residential proxies and fingerprints — work behind one key.
Good to know
- Without --category a run walks every grid the menu lists; bound it with --max-grids or --max-skus.
- Selenium cannot authenticate a proxy or attach to an authenticated CDP endpoint, so it refuses those setups.
- The sitemap is not the catalogue — coverage comes from walking the menu, not from sitemaps.
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 |
Bershka scraper FAQ
What output formats does it write?
JSON and CSV (or one of them with --format), plus a .meta.json sidecar recording the run's status, scope and any failed requests.
Is a row a product?
No — a row is one SKU, a (product, colour, size) triple. The sample run's 131 rows are 86 products.
Do I need a proxy?
You need an address Bershka's Akamai edge accepts. Some exits are refused outright with a 195-byte 403, others are served; a pool via --proxy-file is how long runs stay on accepted exits.
Is there a captcha?
None was seen across twenty exits — only Akamai's arithmetic interstitial, which the HTTP engine answers itself. The browser engines keep the 2Captcha solver as a fallback, controlled by --solve-captcha.
How do I scrape another country?
Pass --site-locale, e.g. de. gb and de have measured store ids; other locales are resolved from the storefront itself. Prices differ per market even for the same SKU.
Is scraping Bershka legal?
It reads the same public catalogue the storefront calls and enforces robots.txt. It is public data; follow the site's terms and the law that applies to you.