Advertisement
🕷️ Projects

Python Web Scraper – News Aggregator

Scrape news headlines from multiple sources, deduplicate, save to JSON/CSV, and optionally email a digest. Combines web scraping, data processing, and automation.

⏱️ 20 min read🎯 Projects📅 Updated 2026

Complete Implementation

Python
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}")
Bash
pip install requests beautifulsoup4 lxml
python scraper.py
Tip: Schedule with cron to run daily and build a historical news archive.

🏋️ Practical Exercise

Extend the web scraper:

  1. Scrape an additional field from each item on the page.
  2. Follow the “next page” link to scrape multiple pages.
  3. Save the results to both CSV and JSON.
  4. 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.
  • requests retrieves 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?

FAQ

Is it OK to scrape any website? +

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.

How do I scrape a site that loads data with JavaScript? +

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.

How do I scrape multiple pages? +

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.

How do I avoid getting blocked? +

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.