$ -weight: 500;">pip -weight: 500;">install requests beautifulsoup4
-weight: 500;">pip -weight: 500;">install requests beautifulsoup4
-weight: 500;">pip -weight: 500;">install requests beautifulsoup4
import requests
from bs4 import BeautifulSoup HEADERS = { "User-Agent": ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/124.0.0.0 Safari/537.36" ), "Accept-Language": "en-IN,en;q=0.9",
} def get_price(url: str) -> tuple[str, int]: r = requests.get(url, headers=HEADERS, timeout=20) r.raise_for_status() soup = BeautifulSoup(r.text, "html.parser") title = soup.select_one("#productTitle").get_text(strip=True) price_el = soup.select_one(".a-price .a-offscreen") if not price_el: raise RuntimeError("Price element not found — page layout changed.") # "₹45,999.00" -> 45999 raw = price_el.get_text(strip=True).replace("₹", "").replace(",", "") rupees = int(float(raw)) return title, rupees
import requests
from bs4 import BeautifulSoup HEADERS = { "User-Agent": ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/124.0.0.0 Safari/537.36" ), "Accept-Language": "en-IN,en;q=0.9",
} def get_price(url: str) -> tuple[str, int]: r = requests.get(url, headers=HEADERS, timeout=20) r.raise_for_status() soup = BeautifulSoup(r.text, "html.parser") title = soup.select_one("#productTitle").get_text(strip=True) price_el = soup.select_one(".a-price .a-offscreen") if not price_el: raise RuntimeError("Price element not found — page layout changed.") # "₹45,999.00" -> 45999 raw = price_el.get_text(strip=True).replace("₹", "").replace(",", "") rupees = int(float(raw)) return title, rupees
import requests
from bs4 import BeautifulSoup HEADERS = { "User-Agent": ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/124.0.0.0 Safari/537.36" ), "Accept-Language": "en-IN,en;q=0.9",
} def get_price(url: str) -> tuple[str, int]: r = requests.get(url, headers=HEADERS, timeout=20) r.raise_for_status() soup = BeautifulSoup(r.text, "html.parser") title = soup.select_one("#productTitle").get_text(strip=True) price_el = soup.select_one(".a-price .a-offscreen") if not price_el: raise RuntimeError("Price element not found — page layout changed.") # "₹45,999.00" -> 45999 raw = price_el.get_text(strip=True).replace("₹", "").replace(",", "") rupees = int(float(raw)) return title, rupees
import csv
from datetime import datetime
from pathlib import Path LOG = Path("prices.csv") def log_price(title: str, rupees: int) -> None: new = not LOG.exists() with LOG.open("a", newline="", encoding="utf-8") as f: w = csv.writer(f) if new: w.writerow(["timestamp", "title", "rupees"]) w.writerow([ datetime.now().isoformat(timespec="seconds"), title, rupees, ])
import csv
from datetime import datetime
from pathlib import Path LOG = Path("prices.csv") def log_price(title: str, rupees: int) -> None: new = not LOG.exists() with LOG.open("a", newline="", encoding="utf-8") as f: w = csv.writer(f) if new: w.writerow(["timestamp", "title", "rupees"]) w.writerow([ datetime.now().isoformat(timespec="seconds"), title, rupees, ])
import csv
from datetime import datetime
from pathlib import Path LOG = Path("prices.csv") def log_price(title: str, rupees: int) -> None: new = not LOG.exists() with LOG.open("a", newline="", encoding="utf-8") as f: w = csv.writer(f) if new: w.writerow(["timestamp", "title", "rupees"]) w.writerow([ datetime.now().isoformat(timespec="seconds"), title, rupees, ])
import os BOT_TOKEN = os.environ["TG_BOT_TOKEN"]
CHAT_ID = os.environ["TG_CHAT_ID"] def ping(msg: str) -> None: requests.post( f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage", data={"chat_id": CHAT_ID, "text": msg}, timeout=10, )
import os BOT_TOKEN = os.environ["TG_BOT_TOKEN"]
CHAT_ID = os.environ["TG_CHAT_ID"] def ping(msg: str) -> None: requests.post( f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage", data={"chat_id": CHAT_ID, "text": msg}, timeout=10, )
import os BOT_TOKEN = os.environ["TG_BOT_TOKEN"]
CHAT_ID = os.environ["TG_CHAT_ID"] def ping(msg: str) -> None: requests.post( f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage", data={"chat_id": CHAT_ID, "text": msg}, timeout=10, )
PRODUCTS = [ ("https://www.amazon.in/dp/B0CHX1W1XY", 45000), # (url, target_rupees) ("https://www.amazon.in/dp/B0BDHWDR12", 12000),
] def main(): for url, target in PRODUCTS: try: title, price = get_price(url) log_price(title, price) if price <= target: ping(f"💸 {title[:60]} is ₹{price:,} (target ₹{target:,})\n{url}") print(f"OK ₹{price:,} — {title[:60]}") except Exception as e: print(f"FAIL {url[:60]} — {e}") if __name__ == "__main__": main()
PRODUCTS = [ ("https://www.amazon.in/dp/B0CHX1W1XY", 45000), # (url, target_rupees) ("https://www.amazon.in/dp/B0BDHWDR12", 12000),
] def main(): for url, target in PRODUCTS: try: title, price = get_price(url) log_price(title, price) if price <= target: ping(f"💸 {title[:60]} is ₹{price:,} (target ₹{target:,})\n{url}") print(f"OK ₹{price:,} — {title[:60]}") except Exception as e: print(f"FAIL {url[:60]} — {e}") if __name__ == "__main__": main()
PRODUCTS = [ ("https://www.amazon.in/dp/B0CHX1W1XY", 45000), # (url, target_rupees) ("https://www.amazon.in/dp/B0BDHWDR12", 12000),
] def main(): for url, target in PRODUCTS: try: title, price = get_price(url) log_price(title, price) if price <= target: ping(f"💸 {title[:60]} is ₹{price:,} (target ₹{target:,})\n{url}") print(f"OK ₹{price:,} — {title[:60]}") except Exception as e: print(f"FAIL {url[:60]} — {e}") if __name__ == "__main__": main()
0 */6 * * * cd /home/you/tracker && /usr/bin/python3 price_tracker.py >> tracker.log 2>&1
0 */6 * * * cd /home/you/tracker && /usr/bin/python3 price_tracker.py >> tracker.log 2>&1
0 */6 * * * cd /home/you/tracker && /usr/bin/python3 price_tracker.py >> tracker.log 2>&1 - Visits an Amazon India product URL.
- Scrapes the current price.
- Logs it to a CSV with a timestamp.
- Pings you on Telegram if the price drops below a target. - The CSS selectors (#productTitle, .a-price .a-offscreen) are stable on Amazon India as of 2026, but Amazon rotates layouts. If your script breaks, right-click the price → Inspect → copy a fresh selector.
- Always check raise_for_status(). A 503 usually means you hit Amazon's rate limit — back off, don't hammer it. - Amazon India prices move daily, not seasonally. Same laptop: ₹45,999 on Tuesday, ₹43,499 on Saturday, ₹46,499 on Monday.
- "Deals of the Day" aren't usually the lowest price that month. The real dip often happens the week after a sale ends.
- Pin codes matter. A product shown at ₹1,299 in Mumbai can be ₹1,399 in a Tier-2 pin code — the script above uses whatever pin code Amazon defaults to. Add a ?pincode=110001 variant if you want consistency. - Flipkart support — different selectors, same pattern. You now track both in one CSV.
- 7-day rolling min/max — load the CSV with pandas, alert only when price hits a new 7-day low.
- Chart generator — a weekly email with a matplotlib PNG of every product you're tracking.