1. 2scraper
  2. E-commerce & retail
  3. Bershka
Open source · MITFree to run · paid add-ons optional

Bershka Scraper

Every SKU in a Bershka category — price, colour, size and stock — straight from the store's own catalogue API.

  • Plain HTTP
  • Playwright
  • Selenium
  • Puppeteer
  • Scraping Browser API
  • Scraper API
  • JSON
  • CSV
bershka-scraper
# Category
$ python3 api_scraper.py --category "WOMEN / Accessories / Bags and purses"
✓ 131 sample rows · 37 columns in sample_output.json
507category grids on the gb menu (Sep 2026)
122locales in the sitemap index
36columns per SKU row
5engines, one row schema
Available Bershka scrapers

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 URL

Reads 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-locale

Prints 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 de

Resolves that storefront's store id from the site itself and scrapes it in the local currency and language.

Quick start

From zero to Bershka data in 3 steps

  1. 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
  2. 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"
  3. Get structured data

    Rows land in JSON or CSV (--format), with a .meta.json sidecar recording status, pages fetched and why the run stopped.

Output

Bershka data fields

37 columns per row in a predictable schema. The most useful ones:

FieldWhat it holds
skuThe size-level SKU — the only id unique per row
titleProduct name as the store shows it
priceCurrent price as a number, scaled from minor units
currencyISO currency from the store configuration
original_priceThe struck-through price, when there is one
discount_pctDiscount percentage, stated by the API or computed
color_nameColour of this SKU
size_nameSize label (not always unique within a colour)
availabilityInStock, OutOfStock or BackSoon
urlThe product page URL
image_urlProduct image
product_idThe product id the URL carries
referenceBershka's internal product reference
categoriesEvery menu trail that lists this SKU
country_of_originCountry of manufacture per size
localeStorefront 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.

Built for Bershka

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.

Everything built in

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.

Use cases

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.

Access & pricing

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

2scraperTypical hosted API
CodeOpen source, MIT — read and patch itClosed
CostFree; optional services billed only when usedSubscription or per request / per result
Runs onYour machine, server or CIThe vendor's infrastructure
OutputJSON/CSV files + run metadataAPI responses
When the site changesFix it yourself or open an issue / PRWait for the vendor
FAQ

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.