DEV Community

Cover image for How to Scrape PcComponentes Prices in Spain and Portugal with Python
ReventaData
ReventaData

Posted on Originally published at apify.com

How to Scrape PcComponentes Prices in Spain and Portugal with Python

By the end of this tutorial, you will have a Python script that turns a PcComponentes search into structured JSON: product name, brand, category, offer price in euros, the PVPR reference price, discount percentage, seller, condition (new or reconditioned), manufacturer part number, rating, review count and product URL.

PcComponentes is an electronics retailer with a Spanish store (pccomponentes.com) and a Portuguese one (pccomponentes.pt). Its search pages sit behind a Cloudflare challenge: in a reach test on 2026-10-16, plain, datacenter and residential requests got an HTTP 403 challenge page, while Apify Unblocker returned the full page.

We will use the Apify Actor PcComponentes Scraper (reventadata/pccomponentes-scraper). The sample row below comes from a capture dated 2026-11-02 (a PcComponentes Spain results page for portatil, 37 product cards), parsed with the Actor's own parser; this tutorial quotes no run of its own.

⏱️ Time estimate: one sitting.


Prerequisites

You will need:

  1. Python 3.11+ (apify-client 3.3.0 requires it).
  2. The Apify API client for Python.
  3. An Apify account and its API token, from the API & Integrations page in Apify Console.

Install the client and keep the token in an environment variable, never in the script:

pip install "apify-client~=3.0"
Enter fullscreen mode Exit fullscreen mode
export APIFY_API_TOKEN="your_apify_token_here"
Enter fullscreen mode Exit fullscreen mode

What you'll achieve

You will run the Actor from Python and get one JSON row per product card on PcComponentes search results. Each row carries the price as a number, the originalPrice (the PVPR, the recommended retail price PcComponentes shows next to the offer), discountPercentage, seller (PcComponentes itself or a marketplace merchant), condition, mpn, rating and reviewsCount.

The Actor reads the product data PcComponentes puts on each search card, so it never opens product pages one by one.

💡 Example use case: compare laptop offers across sellers in Spain. Search portatil (Spanish for laptop) with country: "ES", then add condition: "REFURBISHED" to see only reconditioned machines, or maxPrice to stay under a budget.


Step 1: Access PcComponentes Scraper

Open the PcComponentes Scraper page on Apify Store. Its README lists every input field and shows a sample row. Try for free opens the Actor in Apify Console, if you prefer a form. In this guide we call it from Python.


Step 2: Configure the search

The main input is searchQueries: the words you would type in the PcComponentes search box, one search per term. country picks the store: ES (pccomponentes.com, the default) or PT (pccomponentes.pt). maxPages (1 to 50, default 5) bounds result pages per term, and maxItems (1 to 5,000, default 60) caps saved rows across all terms.

This setup searches the Spanish store for portatil, reads one result page and saves at most 5 rows:

import os
from apify_client import ApifyClient

# 1. Initialize ApifyClient with the token from the environment
client = ApifyClient(os.environ["APIFY_API_TOKEN"])

# 2. Search PcComponentes Spain for laptops: one result page, at most 5 rows
run_input = {
    "searchQueries": ["portatil"],
    "country": "ES",
    "maxPages": 1,
    "maxItems": 5,
}
Enter fullscreen mode Exit fullscreen mode

No proxy setting is needed: without one, the Actor uses Apify Unblocker.

💡 Tip: a search term is also a filter. The Actor keeps a card only when every word of the term appears in the product name (case and accents ignored). A portatil search drops a laptop whose title never says "portátil", so search in the words PcComponentes uses in its titles.


Step 3: Add optional filters

Parameter Type Default What it does
sort string DEFAULT DEFAULT, PRICE_ASC, PRICE_DESC, BEST_SELLERS, DISCOUNT, RATING or NEWEST, sent to the site.
condition string ALL ALL, NEW or REFURBISHED.
minPrice, maxPrice number none Offer price bounds in euros.
brand string none Keep cards whose brand or name contains it.
includeKeywords array none Words the name or brand must all contain.
excludeKeywords array none Words that drop a card when the name or brand contains any.
proxyConfiguration object Apify Unblocker Proxy settings.

Three details worth knowing:

  • sort changes what the site returns; every other filter runs on the cards after the page is read, so rows it drops are never saved.
  • REFURBISHED keeps cards PcComponentes marks as reconditioned, plus names containing reacondicionado, recondicionado, refurbished or reaco.
  • A price bound drops cards with no price. With minPrice or maxPrice set, a card without a number cannot be compared, so it is skipped.

sort, country and condition only accept the values listed; anything else is refused before the run starts.


Step 4: Run the Actor

.call() starts the Actor in the Apify cloud and waits until it finishes:

# 3. Start the Actor run and wait for it to finish
print("Starting PcComponentes extraction...")
run = client.actor("reventadata/pccomponentes-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")
Enter fullscreen mode Exit fullscreen mode

Behind the scenes, the Actor builds the search URL (/search/?query=..., plus &page=N from page 2 and &sort=... when set), fetches it through Apify Unblocker, checks the body for a Cloudflare challenge instead of trusting the status code, reads each product card, applies your filters and saves the rows.

⚠️ Tip: for long runs, .start(run_input=run_input) returns the run object at once instead of waiting; read the run back later by its ID.


Step 5: View and download results

Print the fields you need:

for item in items:
    print(item["name"])
    print(f"  Brand: {item.get('brand')} | Seller: {item.get('seller')} | Condition: {item.get('condition')}")
    print(f"  Price: {item.get('price')} {item['currency']} | PVPR: {item.get('originalPrice')}")
    print(f"  Discount: {item.get('discountPercentage')}%")
    print(f"  Rating: {item.get('rating')} from {item.get('reviewsCount')} reviews")
    print(f"  {item['url']}")
    print("-" * 40)
Enter fullscreen mode Exit fullscreen mode

Here is one row, the first of the 37 product cards in a capture dated 2026-11-02 of a PcComponentes Spain results page, parsed with the Actor's own parser for the query portatil. A saved row also carries scrapedAt, added when it is saved:

{
  "id": "11015791",
  "name": "Portátil Alurin Flex Advance AMD Ryzen 5 7430 15.6\" / 16 GB / 512GB / Windows 11 Home",
  "brand": "Alurin",
  "category": "Portátiles",
  "price": 529.99,
  "originalPrice": 599.99,
  "discountPercentage": 11.7,
  "currency": "EUR",
  "seller": "PcComponentes",
  "condition": "new",
  "mpn": "ALURZ5743016512WH",
  "url": "https://www.pccomponentes.com/portatil-alurin-flex-advance-amd-ryzen-5-7430-15-6-16-gb-512gb-windows-11-home",
  "image": "https://thumb.pccomponentes.com/w-150-150/articles/1101/11015791/1796-portatil-alurin-flex-advance-amd-ryzen-5-7430-156-16-gb-512gb-windows-11-home-comprar.jpg",
  "rating": 4.3,
  "reviewsCount": 143,
  "country": "ES",
  "matchedKeyword": "portatil"
}
Enter fullscreen mode Exit fullscreen mode

Apify datasets export as JSON, CSV, XML, Excel, HTML table, RSS or JSONL, from Console or through the API's format parameter.


Understanding your results

The key fields:

  • price: the offer price in euros, as a number.
  • originalPrice: the PVPR or crossed-out price on the card, or null when the card shows none.
  • discountPercentage: the discount PcComponentes states on the card; when it states none but shows a higher PVPR, the Actor computes it from the two prices. In the sample, the card stated 0, so the 11.7 comes from 529.99 against 599.99.
  • seller: PcComponentes or the marketplace merchant's name. In the capture, 36 of the 37 cards were sold by PcComponentes and one, a Lenovo ThinkBook 16 G9, by the marketplace merchant Qk Mobile.
  • condition: new or reconditioned; a card that does not say is recorded as new.
  • mpn: the manufacturer part number, handy for matching the same product in other shops.
  • rating and reviewsCount: the star score out of 5 and the number of opinions.
  • matchedKeyword: the search that found the row.

The search term is also a filter: 13 of the 37 cards in the capture have no "portátil" in the title (the seven Apple MacBook cards, for example), so a portatil search would not save them, while a macbook search would save the MacBooks.


Pro tips

  1. Start small. maxPages: 1 and maxItems: 5 until your code handles the fields, then raise them.
  2. Let the site sort. sort: "DISCOUNT" or "PRICE_ASC" changes the order PcComponentes returns, so the first pages already hold what you want; post-fetch filters only drop rows.
  3. Run several searches at once. Put several terms in searchQueries; a product saved under one term is not saved again under a later one.
  4. Match across shops by mpn. Names differ between retailers; the part number is the safer key.
  5. Compare Spain and Portugal with one script. Both stores use the same card markup, so country: "PT" returns the same fields; keep country in your export to tell rows apart.
  6. Bound every run. maxItems and maxPages are the two limits on how much a run reads and saves; a narrow filter on a broad search reads many pages for few rows.
  7. Automate with webhooks and schedules. A webhook's payload resource is the response of the Get Actor run API endpoint (docs.apify.com/integrations/webhooks/actions), so it carries defaultDatasetId. Save your input as a task and add an Apify schedule: 0 8 * * * runs it every day at 8 AM.

Troubleshooting

  • The run ends as FAILED with "0 results for ...". When no rows were saved, a page that was blocked or could not be read fails the run, because matches may have been missed. A search page the site itself reports as empty, or one whose cards your price, brand or keyword filters all drop, ends SUCCEEDED with an empty dataset and a status message saying so. Check that the search term's words appear in the product names.
  • Input refused before the run starts. country, sort or condition holds a value outside its list, or maxItems / maxPages is out of range.
  • Fewer rows than expected. The query stops at an empty page, at maxItems, at maxPages, or when a page keeps nothing and holds fewer than 5 cards. If a challenge page comes back twice, that query stops and the next one starts.

To see what happened, read the run log from Python with client.log(run.id).get(). For help, use the Actor's Store page.


Pricing and next steps

For current pricing and details, see the Store page.

From here, switch country to PT for the Portuguese store, schedule a daily price check, or feed the rows into a price-tracking sheet keyed by mpn. The Apify Python client documentation covers .start(), pagination and more.

What are you planning to track on PcComponentes? Drop a comment! 💬

Top comments (0)