Complete Implementation
import requests, csv, json, hashlib
from datetime import datetime
from pathlib import Path
from bs4 import BeautifulSoup
from dataclasses import dataclass, asdict
@dataclass
class Article:
title: str
url: str
source: str
scraped_at: str = ""
def __post_init__(self):
if not self.scraped_at:
self.scraped_at = datetime.now().isoformat()
@property
def id(self):
return hashlib.md5(self.url.encode()).hexdigest()[:8]
SOURCES = {
"Hacker News": {
"url": "https://news.ycombinator.com",
"selector": ".storylink",
"attr": "href",
},
}
def scrape(name, config):
try:
r = requests.get(config["url"], headers={"User-Agent": "NewsBot/1.0"}, timeout=15)
r.raise_for_status()
soup = BeautifulSoup(r.text, "lxml")
return [
Article(title=a.get_text(strip=True), url=a.get(config["attr"], ""), source=name)
for a in soup.select(config["selector"])[:20]
if a.get_text(strip=True)
]
except Exception as e:
print(f"Failed {name}: {e}"); return []
def deduplicate(articles):
seen, unique = set(), []
for a in articles:
if a.id not in seen:
seen.add(a.id); unique.append(a)
return unique
all_articles = []
for name, config in SOURCES.items():
arts = scrape(name, config)
all_articles.extend(arts)
print(f"{name}: {len(arts)} articles")
unique = deduplicate(all_articles)
out = Path("news_data"); out.mkdir(exist_ok=True)
date = datetime.now().strftime("%Y%m%d")
Path(out / f"news_{date}.json").write_text(json.dumps([asdict(a) for a in unique], indent=2))
with open(out / f"news_{date}.csv", "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=["title","url","source","scraped_at"])
w.writeheader(); w.writerows([asdict(a) for a in unique])
print(f"\nSaved {len(unique)} articles to {out}/")
for i, a in enumerate(unique[:5], 1):
print(f" {i}. [{a.source}] {a.title}")pip install requests beautifulsoup4 lxml
python scraper.py🏋️ Practical Exercise
Extend the web scraper:
- Scrape an additional field from each item on the page.
- Follow the “next page” link to scrape multiple pages.
- Save the results to both CSV and JSON.
- Add a polite delay and a custom User-Agent header.
🔥 Challenge Exercise
Build a complete scraping pipeline: crawl a paginated listing site, extract structured records with CSS selectors, deduplicate them, and store them in a CSV or database. Add resilient handling for missing fields and failed requests (retry with backoff), and respect robots.txt and rate limits. Bonus: schedule the scraper to run daily and only append new items.
📋 Summary
- This project builds a scraper that fetches pages and extracts structured data.
requestsretrieves HTML and BeautifulSoup parses it via CSS selectors.- Pagination is handled by following “next” links or page parameters.
- Results are saved to CSV/JSON or a database, with duplicates removed.
- Polite scraping — delays, User-Agent, robots.txt — keeps it ethical.
- Retries and defensive parsing make it resilient to failures and layout changes.
Interview Questions on Building a Web Scraper
- What libraries would you use to build a scraper and why?
- How do you locate and extract specific elements from a page?
- How do you scrape data spread across multiple pages?
- How do you handle pages that render content with JavaScript?
- What ethical and legal practices should a scraper follow?
- How do you make a scraper resilient to failures and page changes?
- How would you store and deduplicate scraped data?
Related Topics
FAQ
Not always. Check the site’s robots.txt and terms of service, avoid collecting personal or copyrighted data you have no right to, throttle your requests, and use an official API when one is available.
A plain request only sees the initial HTML. For JS-rendered content, use a browser-automation tool like Selenium or Playwright that runs the page’s scripts, or look for an underlying API the page calls.
Identify the pagination mechanism — a “next” link or a page-number query parameter — and loop, fetching and parsing each page until there are no more, accumulating the records as you go.
Add delays between requests, set a realistic User-Agent, respect rate limits and robots.txt, and avoid aggressive crawling. Considerate scraping is both ethical and less likely to get your IP banned.
