By the end of this tutorial, you will have a working Python script that extracts catalog listings from Spanish department store El Corte Inglés (elcorteingles.es) into structured JSON: product titles, prices in euros, original prices, discount percentages, brands, GTIN barcodes, and marketplace sellers.
Manual extraction across pages is tedious, while direct, datacenter, and residential requests received an "Access Denied" response in tests conducted on 2026-10-21. Apify Unblocker returned the real category pages.
In this guide, we use Python and the El Corte Inglés Scraper (reventadata/elcorteingles-scraper) Actor on Apify. The sample data cited here comes from a capture dated 2026-10-21 parsed with the Actor's own engine.
⏱️ Time estimate: About 15 minutes.
Prerequisites
To follow this tutorial, you will need:
-
Python 3.11+ installed on your local machine (
apify-client3.x requires Python 3.11 or newer). - An Apify account to run cloud scrapers and access datasets.
- Your Apify API token, which you can find under API & Integrations in Apify Console.
Install the official Apify API client library:
pip install -U "apify-client~=3.0"
Next, export your API token as an environment variable in your terminal session:
export APIFY_API_TOKEN="your_apify_token_here"
What you'll achieve
By completing this tutorial, you will extract structured product records from El Corte Inglés category listings directly into clean JSON datasets. Each record captures:
-
Identifiers: Internal product code (
Aprefix for retail stock,MP_for marketplace items), canonical URL, and GTIN barcode. - Pricing: Current offer price in EUR, original reference price, calculated discount percentage, and discount amount.
- Merchandising: Brand name, breadcrumb categories, colour variant counts, and new arrival flags.
- Vendor identification: Explicit seller name and a boolean flag indicating whether the item is fulfilled by a third-party marketplace seller.
Example use case: Suppose you want to monitor footwear discounts across Spanish retail. By pointing the Actor at the women's shoes category, you can isolate products discounted by 25% or more and differentiate direct retail stock from third-party marketplace partners.
Step 1: Access El Corte Inglés Scraper
Start by heading over to the El Corte Inglés Scraper page on the Apify Store.
From the Actor store page, you can review available parameters, sample output schemas, and documentation. You can test runs directly inside the Apify Console workspace or integrate them programmatically into your Python projects. In this guide, we will automate everything through the Python API client.
Step 2: Configure category URLs
The primary input for this Actor is categoryUrls. You provide one or more category listing URLs from elcorteingles.es, such as https://www.elcorteingles.es/electronica/moviles-y-smartphones/ or https://www.elcorteingles.es/moda-mujer/zapatos/.
The site's robots.txt disallows internal search result paths (/search-nwx/, /*/search) and parameterised filter URLs (such as brand:: or price::). To respect site rules, the Actor refuses query strings and search paths up front. It fetches clean category paths and applies all your brand, price, and discount filters post-fetch.
In your Python script, initialize ApifyClient and define your target category and page limits:
import os
from apify_client import ApifyClient
# 1. Initialize ApifyClient with the token from the environment
client = ApifyClient(os.environ["APIFY_API_TOKEN"])
# 2. Configure El Corte Inglés category URL: 2 pages, up to 24 items
run_input = {
"categoryUrls": [
"https://www.elcorteingles.es/electronica/moviles-y-smartphones/"
],
"maxPagesPerCategory": 2,
"maxItems": 24,
}
Each category page displays 12 products. Setting maxPagesPerCategory: 2 instructs the crawler to process at most two consecutive listing pages (up to 24 products).
Step 3: Configure optional settings and filters
El Corte Inglés Scraper supports comprehensive post-fetch filtering. Because URL query parameters are disallowed by the site, filters are evaluated locally within the Actor:
| Parameter | Type | Default | Description |
|---|---|---|---|
categoryUrls |
array | Required | List of category page URLs on elcorteingles.es. Query strings and search paths are disallowed. |
maxItems |
integer | 100 |
Total products to save across all requested categories before stopping. |
maxPagesPerCategory |
integer | 50 |
Maximum pages to read per category (12 products per page, capped at the site ceiling of 833 pages). |
minPrice & maxPrice
|
number | None |
Price boundaries in EUR. Products without prices are excluded when set. |
onlyDiscounted |
boolean | false |
When set to true, keeps only products where an original price is present. |
minDiscountPercent |
integer | None |
Minimum percentage discount required (at least 1). |
brands |
array | [] |
List of brand names to retain, matched case- and accent-insensitively. |
sellerType |
string | "any" |
Filter by vendor: "any", "elcorteingles" (direct retail), or "marketplace" (third-party). |
includeKeywords |
array | [] |
Retain only items whose name, brand, or category contains all specified words. |
excludeKeywords |
array | [] |
Drop items matching any of these keywords. |
proxyConfiguration |
object | Unblocker | Defaults to Apify Proxy with the Unblocker group to ensure reach through bot mitigation. |
Pro Tip on Budget Caps:
maxItemsacts as an absolute ceiling across your run. Even if you supply multiple categories, the crawler halts immediately oncemaxItemsrecords have been stored in your dataset.
Step 4: Run the Actor
With your configuration dictionary ready, trigger the execution using the Apify Python client:
# 3. Start the Actor run and wait for it to finish
print("Starting El Corte Inglés extraction...")
run = client.actor("reventadata/elcorteingles-scraper").call(run_input=run_input)
if run is None or run.status != "SUCCEEDED":
status = run.status if run else "unknown"
raise SystemExit(f"Run did not succeed (status: {status}).")
# 4. Fetch the saved products
items = client.dataset(run.default_dataset_id).list_items().items
print(f"Retrieved {len(items)} products.\n")
Behind the scenes, the Actor accesses each category URL using Apify Unblocker. Category pages render an inline state object (window.__MOONSHINE_STATE__) holding product data. The Actor parses this state, joins product details with data layer attributes, applies your filters, and streams structured rows into your default dataset.
Step 5: View and download results
Once execution finishes, iterate through your dataset items to display the extracted product information:
for item in items:
print(item["name"])
print(f" Brand: {item.get('brand')} | Seller: {item.get('seller')} | Marketplace: {item.get('isMarketplace')}")
print(f" Price: {item.get('price')} {item.get('currency')} | Original: {item.get('originalPrice')}")
print(f" Discount: {item.get('discountPercent')}% | On sale: {item.get('onSale')}")
print(f" Barcode: {item.get('gtin')} | Status: {item.get('status')}")
print(f" {item['url']}")
print("-" * 40)
The sample record below represents a real product captured from page 16 of /moda-mujer/zapatos/, parsed with the Actor's own engine from a verbatim fixture dated 2026-10-21:
{
"id": "MP_0623819_252BZ131810522",
"name": "Zapatillas deportivas de mujer retro 13 18 con cordones Marrón",
"brand": "Bimba y Lola",
"url": "https://www.elcorteingles.es/moda-mujer/MP_0623819_252BZ131810522-marron-pr-zapatillas-deportivas-de-mujer-retro-13-18-con-cordones/",
"image": "https://dam.elcorteingles.es/producto/www-8445394427095-00.jpg",
"gtin": "8445394427149",
"price": 68.6,
"currency": "EUR",
"originalPrice": 98.0,
"discountPercent": 30,
"discountAmount": 29.4,
"onSale": true,
"category": "Moda mujer > Zapatos > Zapatillas > Zapatillas deportivas",
"categories": [
"Moda mujer",
"Zapatos",
"Zapatillas",
"Zapatillas deportivas"
],
"seller": "BIMBA & LOLA",
"isMarketplace": true,
"colourCount": 1,
"status": "ADD",
"isNew": false,
"categoryPath": "/moda-mujer/zapatos/",
"page": 16,
"position": 1
}
In addition to these fields, records saved during live runs carry a scrapedAt ISO 8601 timestamp added automatically when pushed to the dataset.
Understanding your results
Every item saved in the dataset provides rich commercial attributes extracted directly from the page state:
-
Product identifiers (
id,gtin): Theidclearly reflects product origin. Retail inventory sold directly by El Corte Inglés begins withAfollowed by digits (such asA200977771), whereas marketplace inventory uses the formatMP_<seller>_<sku>(such asMP_0623819_252BZ131810522). Thegtinbarcode allows cross-referencing items against external retail catalogs. -
Accurate price tracking (
price,originalPrice,discountPercent): When an item is discounted,onSaleevaluates totrue, and bothoriginalPriceanddiscountPercentare populated. In our sample shoe capture from page 16, 9 out of 12 items carried discounts ranging from 16% to 31%. Non-discounted items retainnullfor original price and discount values. -
Seller transparency (
seller,isMarketplace): Thesellerfield provides the registered commercial name of the merchant (such as"El Corte Ingles, S.A.","BIMBA & LOLA", or"GIOSEPPO"). SettingsellerTypeto"marketplace"or"elcorteingles"lets you separate direct store stock from independent retailers. -
Taxonomy and availability:
categoriesprovides full hierarchical navigation breadcrumbs, whilestatusindicates ordering availability (such asADDorRESERVE).
Pro tips
Here are five practical recommendations for optimizing your El Corte Inglés scraping workflows:
-
Provide clean category paths: Always supply canonical paths like
/moda-mujer/zapatos/. Do not append query strings, filter parameters, or search paths: the Actor refuses them before starting, androbots.txtdisallows search and filter URLs. -
Understand the 833-page ceiling: El Corte Inglés enforces a hard ceiling of 833 pages per category (12 products per page, yielding 9,996 total products). Requests for page 834 return HTTP 404. For large categories, configure
maxPagesPerCategoryaccordingly. -
Use post-fetch discount filtering: If your goal is deal monitoring, set
onlyDiscounted: trueor defineminDiscountPercent: 20. Rows dropped by filters are discarded before storage, keeping your dataset focused on genuine bargains. -
Leverage GTIN for cross-store price comparison: Use the extracted
gtinvalues to link El Corte Inglés products to competitors across European retail datasets without relying on fuzzy title matching. -
Schedule recurring runs: Automate runs through Apify Schedules (for example, using a cron expression like
0 8 * * *for daily morning sweeps) to monitor price fluctuations over time.
Troubleshooting
When building your extraction pipeline, keep these common issues and solutions in mind:
-
ValueErroron a category URL: The Actor refuses URLs with search segments (like/search/or/search-nwx/), filter parameters (brand::), or query strings (?), each rejected before any request is made. Strip the URL down to the base category path. -
RuntimeError: 0 rows for categories...: The Actor intentionally fails when zero records are saved. Check whether your filter criteria (such as restrictive brand names, high minimum discounts, or narrow price bands) filtered out all candidate items. -
Fewer rows than requested: If the category has fewer items than your
maxItemstarget, the Actor stops at the last page once the product grid is exhausted. -
Checking logs: Inspect logs via Apify Console or retrieve them with
client.log(run.id).get(). Each page logs its product count, and the final line tallies the filter rejections.
Next steps
Now that you have automated El Corte Inglés data extraction, you can integrate your dataset into wider automated workflows. You can configure Apify Webhooks to notify your backend when an extraction completes, or export datasets from Apify Console in JSON, CSV, or Excel.
For current pricing and details, see the Store page.
What categories on El Corte Inglés are you planning to extract? Drop a comment below! 💬
Top comments (0)