Python Web Scraper Project

Python project 08

Python Web Scraper Project

Last updated: 31 August 2026

Create a small web-page title and link collector using only Python’s standard library. This project introduces HTTP requests, HTML parsing, URLs, and responsible data collection.

Learn ethical Python web automationView all project ideas

What you will build

The program fetches a public HTML page you are allowed to access, displays its title, and lists up to ten unique links it finds.

Skills practised

  • urllib requests
  • HTMLParser
  • URLs and relative links
  • Network error handling

Requirements

  • Python 3.8 or later
  • A terminal or command prompt
  • No external packages
  • About 30 minutes to build

Full Python code

Save it as web_scraper.py, then run it from a terminal.

"""Fetch and display the title and links from a public web page you are allowed to access."""

from html.parser import HTMLParser
from urllib.error import HTTPError, URLError
from urllib.parse import urljoin, urlparse
from urllib.request import Request, urlopen


class PageSummary(HTMLParser):
    def __init__(self):
        super().__init__()
        self.in_title = False
        self.title_parts = []
        self.links = []

    def handle_starttag(self, tag, attrs):
        attributes = dict(attrs)
        if tag == "title":
            self.in_title = True
        if tag == "a" and attributes.get("href"):
            self.links.append(attributes["href"])

    def handle_endtag(self, tag):
        if tag == "title":
            self.in_title = False

    def handle_data(self, data):
        if self.in_title:
            self.title_parts.append(data.strip())


def fetch_page(url):
    request = Request(url, headers={"User-Agent": "LearningWebScraper/1.0"})
    with urlopen(request, timeout=15) as response:
        content_type = response.headers.get_content_type()
        if content_type != "text/html":
            raise ValueError(f"Expected an HTML page, received {content_type}.")
        return response.read().decode(response.headers.get_content_charset() or "utf-8", errors="replace")


def main():
    print("Web Page Title and Link Collector")
    print("Only use this on public pages you are allowed to access. Respect each site's terms and robots.txt.\n")
    url = input("Public page URL (including https://): ").strip()
    if urlparse(url).scheme not in {"http", "https"}:
        print("Enter a complete http or https URL.")
        return

    try:
        html = fetch_page(url)
    except (HTTPError, URLError, ValueError) as error:
        print(f"Could not fetch the page: {error}")
        return

    summary = PageSummary()
    summary.feed(html)
    title = " ".join(part for part in summary.title_parts if part) or "No title found"
    unique_links = []
    for link in summary.links:
        absolute = urljoin(url, link)
        if absolute not in unique_links:
            unique_links.append(absolute)

    print(f"\nPage title: {title}")
    print(f"Links found: {len(unique_links)}")
    for link in unique_links[:10]:
        print(f"- {link}")
    if len(unique_links) > 10:
        print("- ...")


if __name__ == "__main__":
    main()

How it works

fetch_page() requests an HTML page with a clear learning user-agent. PageSummary collects the title and link attributes during parsing, then urljoin() turns relative links into complete URLs.

Run the project

  1. Run python web_scraper.py.
  2. Enter a public URL you are permitted to access.
  3. Read the title and first ten unique links.
  4. Stop if a site disallows the activity or requires authentication.

Notes for your notebook

  • Permission: follow terms of service and robots.txt.
  • Rate limits: never overload a website with repeated requests.
  • Public content: do not bypass logins, paywalls, or access controls.

Ways to extend it

Add a CSV export, optional CSS-selector parsing with Beautiful Soup, or a polite request delay for approved data-collection tasks.

What to learn next

Strengthen your foundations through Python functions and choose another task from the Softenant project library.