If you run a small online store, you already know the problem: keeping up with competitor prices is a manual grind. Open ten tabs, compare SKUs, scribble notes, repeat. By the time you finish, half the prices have changed. A dedicated competitor price tracker built in Python costs nothing to run and does the job automatically while you sleep.
What you’ll build
By the end of this guide you’ll have:
- A Python script that scrapes product prices from 2-3 competitor sites daily
- A local CSV database that tracks price history over time
- A simple alert system that flags products where you’re 10%+ above the competitor
- A scheduling setup so it runs unattended every morning
Total build time: about an hour. Running cost: $0 (your existing laptop or VPS).
Step 1: Structure your data first
Before writing a single scraper, know what you’re looking for. Create a targets.csv that maps your products to competitor URLs:
product_name,your_price,competitor_url,competitor_name
Blue Denim Jacket,45.00,https://competitor-a.com/products/jacket-denim,CompetitorA
Canvas Sneakers White,32.00,https://competitor-b.com/sneakers/white-canvas,CompetitorB
Wool Beanie Black,15.00,https://competitor-a.com/products/beanie-wool,CompetitorA
Keep this file small — 20 to 50 SKUs. Anything beyond that and you’ll want a proper database, but for most small retailers this CSV is all you need.
Step 2: Build the scraper
Here’s a scraper that reads your targets, fetches prices, and saves history. It’s designed to be polite: one request at a time with a 3-second delay.
import csv
import requests
from bs4 import BeautifulSoup
from datetime import datetime
import time
import os
HISTORY_FILE = "price_history.csv"
HEADERS = {
"User-Agent": "MyStorePriceChecker/1.0 (contact@mystore.com)"
}
def parse_price(price_text):
"""Clean a price string like '$45.00' or 'Rp 720,000' into a float."""
cleaned = price_text.strip()
if cleaned.startswith("Rp"):
cleaned = cleaned.replace("Rp", "").replace(".", "").replace(",", ".").strip()
return float(cleaned)
return float(cleaned.replace("$", "").replace(",", ""))
def scrape_competitor(url):
try:
resp = requests.get(url, headers=HEADERS, timeout=15)
if resp.status_code != 200:
return None
soup = BeautifulSoup(resp.text, "html.parser")
price_el = soup.select_one(".price, .product-price, [data-price]")
if not price_el:
return None
return parse_price(price_el.text)
except Exception as e:
print(f"Error on {url}: {e}")
return None
def load_targets():
products = {}
with open("targets.csv", "r") as f:
reader = csv.DictReader(f)
for row in reader:
key = row["product_name"]
products.setdefault(key, []).append(row)
return products
def save_history(rows):
write_header = not os.path.exists(HISTORY_FILE)
with open(HISTORY_FILE, "a", newline="") as f:
writer = csv.writer(f)
if write_header:
writer.writerow(["date", "product", "competitor", "your_price", "their_price"])
writer.writerows(rows)
def check_alerts(rows):
for row in rows:
their_price = float(row[4])
your_price = float(row[3])
if your_price > 0 and their_price < your_price * 0.9:
print(f"⚠ ALERT: {row[1]} — You're ${your_price - their_price:.2f} above {row[2]}!")
# Main
products = load_targets()
new_rows = []
today = datetime.now().strftime("%Y-%m-%d")
for product, targets in products.items():
your_price = float(targets[0]["your_price"])
for t in targets:
their_price = scrape_competitor(t["competitor_url"])
if their_price:
new_rows.append([today, product, t["competitor_name"], your_price, their_price])
time.sleep(3)
if new_rows:
save_history(new_rows)
check_alerts(new_rows)
print(f"Done. Checked {len(new_rows)} product-competitor pairs.")
else:
print("No data collected — check your selectors.")
Step 3: Tune the selectors for each site
The line soup.select_one(".price, .product-price, [data-price]") is a guess — every site uses different CSS classes. For each competitor site in your targets.csv, you’ll need to:
- Open the product page in a browser
- Right-click the price → Inspect
- Note the CSS selector (could be
.price,span.amount,[itemprop="price"], etc.) - Update the
select_one()call accordingly
A common pattern: use multiple fallback selectors. select_one() returns the first match, so put the most specific selector first.
Step 4: Run it on a schedule
On Linux / macOS, use cron. Open your crontab with crontab -e and add:
# Run price check every weekday at 7 AM
0 7 * * 1-5 cd /path/to/your/scraper && python3 price_tracker.py >> price_log.txt 2>&1
On Windows, use Task Scheduler with a daily trigger. In both cases, test the script manually first before trusting the automation.
Step 5: Read the data
After a week, open price_history.csv in any spreadsheet app. Each product has a row per day with your price and the competitor’s. You can:
- Find pricing gaps quickly. Filter by date=latest and sort by the difference column.
- Spot trends. If CompetitorB drops prices every Tuesday, you know when to run promotions.
- Identify stockouts. If a competitor’s price field disappears (returns
None), the product might be out of stock — a perfect time to raise your price.
When to upgrade
This CSV-based tracker works well for 50 SKUs across 3 competitors. When you outgrow it, you’ll know because:
- Your
targets.csvhas 200+ rows and the script takes 10+ minutes - You need historical charts or web-based dashboards
- Competitors add CAPTCHAs or JavaScript-rendered prices (then you need Playwright or a paid API like ScrapingBee)
At that point, consider a lightweight tool like PricingBot ($29/month) or building a Django dashboard. But for most small retailers, this 50-line script handles 80% of the job at $0.
Final checklist
-
targets.csvwith your SKUs and competitor URLs - CSS selectors verified for each target site
- Script runs without errors from the command line
- Crontab or Task Scheduler set up for daily runs
- Price history file growing with new rows each day
Start small — five products, one competitor. Once you see the data flow in, scale up. The first morning you open price_history.csv and see a competitor quietly raised prices on two items while you were asleep, the script has already paid for itself.