How to Build a Simple Price Tracker With Python
pythonprice-trackingweb-scrapingautomationdata-extraction

How to Build a Simple Price Tracker With Python

WWebscraper.uk Editorial
2026-06-13
10 min read

Build a simple Python price tracker that scrapes product pages, stores price history, and alerts you when a target threshold is met.

A simple price tracker is one of the most useful beginner-friendly scraping projects because it solves a real problem and teaches habits that carry over to larger data extraction work. In this guide, you will build a small Python price tracker that fetches a product page, extracts the current price, stores a history, and alerts you when the price changes or drops below a threshold. The project is intentionally modest: it is easy to run on your laptop, easy to revisit when selectors break, and easy to extend later with scheduling, alerts, or browser automation for more dynamic pages.

Overview

This article shows how to build a practical python price tracker using a small set of dependable tools. The core workflow is straightforward:

  1. Request the product page.
  2. Parse the HTML and locate the price.
  3. Normalise the price into a comparable number.
  4. Save the result with a timestamp.
  5. Compare the latest price with previous records.
  6. Send an alert if your condition is met.

That is enough to create a useful price monitoring Python project without introducing unnecessary infrastructure too early.

For many product pages, a stack based on requests and BeautifulSoup is enough. It is fast, simple, and easy to debug. If the site renders prices with JavaScript after page load, you may need a browser automation tool instead. In that case, a headless browser such as Playwright is a better fit than trying to force a static parser to read content that is not in the initial HTML.

Before you scrape any site, check whether you are allowed to access it in the way you intend. Review robots guidance, terms, and your intended use. If you need a refresher, see Robots.txt and Web Scraping: What Developers Should Check Before Crawling.

For this tutorial, we will assume a single product page where the price appears in the HTML response. That keeps the project readable while still covering the decisions that matter in a real product tracker scraper.

How to estimate

The easiest way to think about a price tracker is as a repeating decision loop. On each run, your script asks four questions:

  1. Can I fetch the page reliably?
  2. Can I extract a valid price?
  3. Has the price changed in a meaningful way?
  4. Should I notify someone?

That makes this a useful calculator-style project too, because you can estimate whether a tracker is worth running before you build a larger version. Use these repeatable inputs:

  • Number of products tracked: one item is trivial; dozens require better storage and scheduling.
  • Run frequency: hourly, daily, or weekly.
  • Expected volatility: fast-changing prices may justify more frequent checks.
  • Site complexity: static HTML is low effort; JavaScript-rendered pages raise maintenance time.
  • Alert sensitivity: notify on any change, a percentage drop, or a price threshold.

A lightweight estimate looks like this:

runs_per_day = products * checks_per_day
stored_rows_per_month = runs_per_day * 30
alerts_per_month = runs_per_month * probability_of_change

You do not need exact numbers to benefit from this. The goal is to make design choices early. If you are tracking five products once a day, a CSV file and one cron job are enough. If you are tracking hundreds of product pages several times per hour, you will quickly need rate limiting, a database, retry logic, and perhaps proxies.

Below is a simple implementation in Python for a single page:

import csv
import re
from datetime import datetime
from pathlib import Path

import requests
from bs4 import BeautifulSoup

URL = "https://example.com/product-page"
CSV_FILE = Path("price_history.csv")
TARGET_PRICE = 99.99
HEADERS = {
    "User-Agent": "Mozilla/5.0 (compatible; PriceTracker/1.0)"
}


def fetch_html(url: str) -> str:
    response = requests.get(url, headers=HEADERS, timeout=20)
    response.raise_for_status()
    return response.text


def extract_price(html: str) -> float:
    soup = BeautifulSoup(html, "html.parser")

    # Replace this selector with one that matches your target site
    node = soup.select_one(".price, [data-price], .product-price")
    if not node:
        raise ValueError("Price element not found")

    text = node.get_text(" ", strip=True)
    match = re.search(r"(\d+[\d,]*\.?\d*)", text)
    if not match:
        raise ValueError(f"Could not parse price from: {text}")

    numeric = match.group(1).replace(",", "")
    return float(numeric)


def read_last_price(path: Path):
    if not path.exists():
        return None

    with path.open("r", newline="", encoding="utf-8") as f:
        rows = list(csv.DictReader(f))
        if not rows:
            return None
        return float(rows[-1]["price"])


def append_price(path: Path, url: str, price: float):
    file_exists = path.exists()
    with path.open("a", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=["timestamp", "url", "price"])
        if not file_exists:
            writer.writeheader()
        writer.writerow({
            "timestamp": datetime.utcnow().isoformat(),
            "url": url,
            "price": price,
        })


def should_alert(current_price: float, last_price, target_price: float) -> bool:
    if current_price <= target_price:
        return True
    if last_price is not None and current_price < last_price:
        return True
    return False


def main():
    html = fetch_html(URL)
    current_price = extract_price(html)
    last_price = read_last_price(CSV_FILE)
    append_price(CSV_FILE, URL, current_price)

    print(f"Current price: {current_price}")
    if should_alert(current_price, last_price, TARGET_PRICE):
        print("Alert: price condition met")


if __name__ == "__main__":
    main()

This script is intentionally plain. It does not include email delivery, database storage, retries, or browser automation yet. That is a strength, not a weakness. When you first build price tracker tools, simplicity makes failures easier to understand.

Inputs and assumptions

The quality of a price tracker depends less on code volume and more on a few important assumptions. If you define these carefully, your tracker will be easier to maintain.

1. Your selector must be stable

The hardest part of most small scraping projects is not fetching HTML. It is selecting the right element reliably. Avoid selectors tied to fragile front-end details such as auto-generated class names. Prefer:

  • semantic attributes like itemprop or data-*
  • stable wrapper elements around pricing blocks
  • embedded structured data such as JSON-LD when available

If the site exposes product data in script tags or JSON blobs, parsing that can be more stable than scraping visible text. This is often a better route when you need to scrape prices from website pages that change their markup frequently.

2. Price text needs normalisation

The value shown on a page is often not ready for direct comparison. You may need to handle:

  • currency symbols
  • thousand separators
  • sale labels such as “Now” or “From”
  • ranges such as “£20–£25”
  • tax or shipping notes

Choose one normalisation rule and use it consistently. For example, you might track the first numeric value found in the primary price block, stored as a decimal number. If a page shows both original and discounted prices, decide which one matters before you write your alert logic.

3. Storage should match the size of the job

For one product or a short experiment, CSV is fine. It is transparent, easy to inspect, and integrates well with spreadsheets. Once you track multiple products or want simple querying, SQLite is often the best next step. If you want a fuller comparison, read Store Scraped Data in CSV, JSON, SQLite, or Postgres: What to Choose.

A useful minimum schema looks like this:

  • timestamp
  • product identifier or URL
  • raw price text
  • normalised numeric price
  • status code or fetch outcome

Saving both raw and cleaned values is a practical habit. It helps you debug parsing errors later.

4. Alerts should be meaningful

If you notify on every price fluctuation, you may create noise. Better rules include:

  • alert only when price falls below your target
  • alert only when the drop exceeds a fixed amount
  • alert once per day for the same product
  • alert only when the page was fetched successfully and parsing passed validation

If you later clean and validate larger datasets, the same principles apply. See How to Clean Scraped Data: Deduplication, Normalisation, and Validation.

5. Responsible access matters

Even a single-product tracker can become noisy if scheduled too aggressively. Start with a conservative interval, use sensible timeouts, and back off after failures. For practical guidance, see Rate Limiting for Web Scrapers: How to Crawl Responsibly Without Getting Blocked.

As a rule of thumb, frequency should reflect actual need. A product whose price changes weekly does not need minute-level checks.

Worked examples

Here are three common ways to adapt the same project based on scale and page complexity.

Example 1: One static product page

This is the version shown above. You track one item from one URL, parse one selector, and append the price to a CSV file. A daily run is enough for many use cases.

Best for: learning, personal buying decisions, small monitoring tasks.

Good choices:

  • requests + BeautifulSoup
  • CSV storage
  • terminal output or a basic email alert

Main risk: selector breakage after a site redesign.

Example 2: Several products from one retailer

Suppose you want to monitor ten related products from the same website. The structure is still manageable, but a few design choices change:

  • store products in a list or config file
  • save product names alongside URLs
  • move from CSV to SQLite if you want easier comparison queries
  • sleep between requests and log failures

Your loop might look like this:

products = [
    {"name": "Product A", "url": "https://example.com/a"},
    {"name": "Product B", "url": "https://example.com/b"},
]

for product in products:
    try:
        html = fetch_html(product["url"])
        price = extract_price(html)
        append_price(CSV_FILE, product["url"], price)
        print(product["name"], price)
    except Exception as exc:
        print(f"Failed for {product['url']}: {exc}")

At this point, error handling becomes part of the real project, not an optional improvement. A useful reference is Web Scraping Error Handling Checklist: Retries, Timeouts, and Fallbacks.

Example 3: A dynamic product page

Some retailers load pricing via JavaScript, lazy components, or an API request made in the browser. If requests returns HTML without the actual price, inspect the page in developer tools first. Often you will find one of these situations:

  • the price is in embedded JSON inside the page
  • the price is fetched from a background API request
  • the price appears only after client-side rendering

For the first two, you may still avoid browser automation. For the last case, consider Playwright. The structure stays similar, but fetching changes from “download HTML” to “open page, wait for selector, then extract text”.

That is outside the scope of this basic guide, but it is the right next step when a static parser cannot see the data you need. If your scraping tasks move in that direction, browser automation becomes part of the same learning path.

Adding a simple email alert

Once your tracker can detect a threshold or drop, the next obvious improvement is notification. Keep the first version minimal. For example, send an email only when the price is below your target and only once per product per day. That avoids alert fatigue.

You can also log alerts to a file before wiring up email or chat integrations. This is often a better first step because it proves that your logic is sound before you add delivery complexity.

When to recalculate

A price tracker is not a write-once script. It is a small system with moving parts, so revisiting it periodically is part of the job. In practice, there are a few clear triggers for review.

Recalculate when pricing inputs change

If your target price changes, your alert threshold should change too. This sounds obvious, but it is one of the main reasons people stop trusting their own tracker. Keep the threshold in a config variable or environment file so it is easy to update without editing core logic.

Recalculate when page structure changes

If the selector stops working, do not just patch the regex and move on. Re-check the page structure, confirm whether you are scraping the right element, and store a sample of the raw content for future debugging. A broken selector is often a sign that your assumptions were too brittle.

Recalculate when your tracking scope grows

Moving from one URL to twenty changes the operational shape of the project. You may need:

  • better storage
  • structured logging
  • deduplication rules
  • scheduling and backoff
  • per-site request pacing

For scheduling options, see Schedule a Web Scraper With Cron, GitHub Actions, and Cloud Functions.

Recalculate when benchmarks or rates move

If the site responds more slowly than before, starts rate limiting, or introduces anti-bot controls, your original request frequency may no longer be appropriate. Slow down before you scale up. If you eventually need IP management, approach it as an infrastructure decision rather than a quick fix. This guide on How to Use Proxies for Web Scraping: Rotation, Sessions, and Common Pitfalls covers the trade-offs.

A practical upgrade path

If you want this project to stay useful over time, improve it in this order:

  1. Make selector and parsing logic reliable.
  2. Add logging and error handling.
  3. Store raw and normalised values.
  4. Schedule regular runs.
  5. Add alerts with sensible deduplication.
  6. Move to browser automation only when the page truly requires it.

That order keeps maintenance under control. It also mirrors how many larger data extraction systems evolve in real use.

If you build your first tracker this way, you get more than a one-off script. You get a reusable template for ecommerce price scraping, competitor monitoring, and small internal data collection tasks. Start with one product, one reliable selector, and one sensible alert rule. Then revisit the tracker whenever your target price, page structure, or monitoring frequency changes. That habit is what turns a simple scraper into a dependable tool.

Related Topics

#python#price-tracking#web-scraping#automation#data-extraction
W

Webscraper.uk Editorial

Senior SEO Editor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.

2026-06-13T04:03:48.662Z