Lesson 63 · Data Collection 50–60 minutes Intermediate

Python Web Scraping

Learn how Python can collect structured information from web pages. You will work with HTTP requests, HTML parsing, Beautiful Soup, CSS selectors, links, tables, pagination and practical data-cleaning techniques.

0%
01

Learning Objectives

Web scraping is a useful technique for collecting information from websites when an appropriate API or other structured data source is not available.

You will learn how to:

  • Understand the concept of web scraping.
  • Understand basic HTML structure.
  • Download web pages using Requests.
  • Parse HTML with Beautiful Soup.
  • Find elements and extract text.
  • Extract HTML attributes and links.
  • Use CSS selectors.
  • Extract tables and structured information.
  • Handle pagination.
  • Clean scraped data.
  • Handle common scraping errors.
  • Understand dynamically generated websites.
  • Build a practical web-scraping workflow.
02

What Is Web Scraping?

Web scraping is the process of programmatically retrieving information from web pages and extracting useful data from their HTML structure.

Instead of manually opening hundreds of pages and copying information, a Python program can automate repetitive collection tasks.

Website
   ↓
HTTP Request
   ↓
HTML
   ↓
HTML Parser
   ↓
Extract Data
   ↓
Clean Data
   ↓
Save / Analyze Data

Example Use Cases

  • Collecting publicly available product information
  • Monitoring publicly displayed prices
  • Collecting article titles
  • Building research datasets
  • Extracting publicly available contact information
  • Collecting structured information for analysis
03

Ethical & Legal Considerations

Technical ability does not automatically mean that every website should be scraped.

Before collecting data, check the website's terms, applicable laws, access restrictions and any published scraping guidance.

Responsible Scraping

  • Respect the website's terms and policies.
  • Check robots.txt where appropriate.
  • Do not bypass authentication or access controls.
  • Do not overload a website with requests.
  • Respect rate limits.
  • Collect only information you have a legitimate reason to use.
  • Protect personal information appropriately.

When an official API is available and suitable for your purpose, it is often preferable to use the API.

04

Understanding HTML

Web pages are primarily structured using HTML (HyperText Markup Language).

<html>

    <body>

        <h1>Python Products</h1>

        <p class="price">$500</p>

        <a href="/products">
            View Products
        </a>

    </body>

</html>

Scraping involves locating the HTML elements that contain the information you need.

Important HTML Concepts

  • Elements
  • Tags
  • Attributes
  • Classes
  • IDs
  • Nested elements
05

Downloading a Web Page

The Requests library introduced in the previous lesson can retrieve the HTML of a web page.

import requests

url = "https://example.com"

response = requests.get(
    url,
    timeout=10
)

response.raise_for_status()

html = response.text

print(html)

At this stage, you have the raw HTML. The next step is parsing it.

06

Beautiful Soup

Beautiful Soup is a Python library designed to parse HTML and XML documents.

It provides convenient methods for navigating a document and finding the elements you need.

from bs4 import BeautifulSoup

html = """
<html>
    <body>
        <h1>Hello Python</h1>
    </body>
</html>
"""

soup = BeautifulSoup(
    html,
    "html.parser"
)

print(soup.h1.text)

Output

Hello Python
07

Installing Beautiful Soup

Install Beautiful Soup using pip.

python -m pip install beautifulsoup4

Requests and Beautiful Soup are often used together:

python -m pip install requests beautifulsoup4

Two Different Jobs

  • Requests retrieves the page.
  • Beautiful Soup parses the HTML.
08

Parsing HTML

Once you have HTML, create a Beautiful Soup object.

from bs4 import BeautifulSoup

html = """
<div>
    <h1>Laptop</h1>
    <p>$1200</p>
</div>
"""

soup = BeautifulSoup(
    html,
    "html.parser"
)

print(soup.get_text())

Output


Laptop
$1200

Beautiful Soup creates a structure that makes it easier to navigate the HTML document.

09

Finding HTML Tags

You can access common elements directly.

html = """
<h1>Python Course</h1>
<p>Learn Python step by step.</p>
"""

soup = BeautifulSoup(
    html,
    "html.parser"
)

print(soup.h1)
print(soup.p)

You can also search for specific elements.

heading = soup.find("h1")

print(heading.text)

Output

Python Course
10

find() and find_all()

find() returns the first matching element.

heading = soup.find("h1")

print(heading.text)

find_all() returns all matching elements.

paragraphs = soup.find_all("p")

for paragraph in paragraphs:
    print(paragraph.text)

This distinction is extremely important when scraping lists of products, articles or other repeated elements.

11

CSS Selectors

Beautiful Soup supports CSS selector-based searches through select() and select_one().

html = """
<div class="product">
    <h2>Laptop</h2>
    <p class="price">$1200</p>
</div>
"""

soup = BeautifulSoup(
    html,
    "html.parser"
)

product = soup.select_one(".product")

price = product.select_one(".price")

print(price.text)

Output

$1200

Common selectors include:

.product
.price
#main
div.product
div .price
a[href]
12

Extracting Text

Once an element is found, you can extract its text.

title = soup.find("h1")

print(title.text)

For more controlled extraction:

text = title.get_text(
    strip=True
)

print(text)

strip=True removes unnecessary whitespace around the extracted text.

13

Extracting Attributes

HTML elements often contain useful attributes.

html = """
<a href="/products/1">
    Laptop
</a>
"""

soup = BeautifulSoup(
    html,
    "html.parser"
)

link = soup.find("a")

print(link["href"])

Output

/products/1

You can also use:

print(link.get("href"))

get() is useful when an attribute might not exist.

15

Scraping Tables

HTML tables can contain useful structured information.

html = """
<table>

    <tr>
        <th>Product</th>
        <th>Price</th>
    </tr>

    <tr>
        <td>Laptop</td>
        <td>1200</td>
    </tr>

</table>
"""

soup = BeautifulSoup(
    html,
    "html.parser"
)

rows = soup.select("table tr")

for row in rows:

    cells = row.find_all(
        ["th", "td"]
    )

    values = [
        cell.get_text(
            strip=True
        )
        for cell in cells
    ]

    print(values)

Output

['Product', 'Price']
['Laptop', '1200']
16

Scraping Images

Image URLs are commonly stored in the src attribute.

images = soup.find_all("img")

for image in images:

    source = image.get("src")

    print(source)

You can also extract alternative text:

alt_text = image.get("alt")

print(alt_text)

Important

Extracting an image URL does not automatically mean you have permission to reuse the image. Copyright and website terms still apply.

17

Cleaning Scraped Data

Raw scraped information often contains whitespace, currency symbols, duplicated values or missing fields.

price_text = "$1,299.99"

clean_price = (
    price_text
    .replace("$", "")
    .replace(",", "")
)

price = float(clean_price)

print(price)

Output

1299.99

Data cleaning is an important part of scraping because collecting data is only the first step.

Web Page
   ↓
Raw Data
   ↓
Clean Data
   ↓
Structured Data
   ↓
Analysis
18

Handling Scraping Errors

Web scraping combines network operations with HTML parsing, so multiple types of failures are possible.

import requests
from bs4 import BeautifulSoup

url = "https://example.com"

try:

    response = requests.get(
        url,
        timeout=10
    )

    response.raise_for_status()

    soup = BeautifulSoup(
        response.text,
        "html.parser"
    )

    title = soup.find("h1")

    if title:
        print(title.get_text(strip=True))
    else:
        print("Title not found.")

except requests.exceptions.Timeout:

    print("Request timed out.")

except requests.exceptions.RequestException as error:

    print("Request failed:", error)

Web Pages Change

A scraper can stop working when a website changes its HTML structure. Good scrapers should therefore validate important fields instead of assuming every element always exists.

19

Dynamic Websites

Not every website sends all visible content directly in the initial HTML response.

Some pages use JavaScript to retrieve or generate content after the page loads.

Browser
   ↓
Initial HTML
   ↓
JavaScript executes
   ↓
API request
   ↓
Additional data
   ↓
Final page

If the information you need is not present in the HTML returned by Requests, Beautiful Soup alone may not be enough.

Possible Alternatives

  • Look for an official API.
  • Inspect network requests where appropriate.
  • Use a browser automation tool when justified.
  • Check whether the data exists in an embedded JSON structure.
20

Pagination

Product catalogs, article archives and search results are often divided into multiple pages.

/products?page=1
/products?page=2
/products?page=3

A simple scraper can follow multiple pages.

import requests
from bs4 import BeautifulSoup

for page in range(1, 4):

    url = (
        "https://example.com/products"
        f"?page={page}"
    )

    response = requests.get(
        url,
        timeout=10
    )

    response.raise_for_status()

    soup = BeautifulSoup(
        response.text,
        "html.parser"
    )

    products = soup.select(
        ".product"
    )

    for product in products:

        print(
            product.get_text(
                " ",
                strip=True
            )
        )

Real websites may use next-page links, cursor-based pagination or other mechanisms.

21

Practical Web Scraping Example

Let's combine Requests and Beautiful Soup into a small product scraper.

import requests
from bs4 import BeautifulSoup

url = "https://example.com/products"

try:

    response = requests.get(
        url,
        timeout=10
    )

    response.raise_for_status()

    soup = BeautifulSoup(
        response.text,
        "html.parser"
    )

    products = soup.select(
        ".product"
    )

    for product in products:

        name_element = product.select_one(
            ".product-name"
        )

        price_element = product.select_one(
            ".price"
        )

        if not name_element or not price_element:
            continue

        name = name_element.get_text(
            strip=True
        )

        price = price_element.get_text(
            strip=True
        )

        print(
            f"{name}: {price}"
        )

except requests.exceptions.RequestException as error:

    print("Scraping request failed:", error)

This example demonstrates a practical pattern:

Request
   ↓
Check Response
   ↓
Parse HTML
   ↓
Find Products
   ↓
Extract Fields
   ↓
Clean Data
   ↓
Process / Save
22

Web Scraping Best Practices

1. Prefer APIs When Available

An official API is usually more structured and stable than scraping HTML.

2. Use Timeouts

Network requests should not wait indefinitely.

3. Control Request Frequency

Avoid sending a large number of requests in a very short period.

4. Cache Results When Appropriate

If the same information does not need to be retrieved repeatedly, save previous results.

5. Validate Extracted Data

Websites can change their structure or return incomplete information.

6. Keep Scrapers Maintainable

Use functions and clear variable names rather than writing one giant script.

7. Respect Website Rules

Responsible scraping is part of professional data collection.

23

Common Mistakes

Mistake 1 — Assuming an Element Exists

soup.find() can return None. Check before accessing it.

Mistake 2 — Using the Wrong Selector

Inspect the HTML structure carefully before writing selectors.

Mistake 3 — Ignoring Network Errors

Requests can fail because of timeouts, connection problems or HTTP errors.

Mistake 4 — Scraping Only the First Page

Important data may be distributed across many pages.

Mistake 5 — Forgetting Data Cleaning

Raw text is rarely ready for analysis immediately.

Mistake 6 — Ignoring Dynamic Content

Requests only retrieves what the server provides in the response. JavaScript-generated content may require another approach.

24

Practice Exercises

Exercise 1 — Extract a Heading

Download an HTML page and extract its main <h1> element.

Exercise 2 — Extract All Links

Find all <a> elements and print their text and URLs.

Exercise 3 — Extract Product Cards

Find all elements with a product CSS class and extract the product names and prices.

Exercise 4 — Clean Prices

Convert values such as $1,299.99 into a Python float.

Exercise 5 — Multiple Pages

Build a scraper that processes three pages and combines the extracted records into one list.

25

Mini Challenge — Ecommerce Product Scraper

Build a small ecommerce data collector that retrieves publicly accessible product information from a permitted practice website.

Your Mission

  1. Send an HTTP GET request.
  2. Set a reasonable timeout.
  3. Check the HTTP response.
  4. Parse the HTML with Beautiful Soup.
  5. Find product cards.
  6. Extract product names.
  7. Extract product prices.
  8. Clean the prices.
  9. Store the records in a Python list.
  10. Calculate the average product price.

Starter Structure

import requests
from bs4 import BeautifulSoup


def scrape_products(url):

    response = requests.get(
        url,
        timeout=10
    )

    response.raise_for_status()

    soup = BeautifulSoup(
        response.text,
        "html.parser"
    )

    products = []

    for card in soup.select(
        ".product"
    ):

        name_element = card.select_one(
            ".product-name"
        )

        price_element = card.select_one(
            ".price"
        )

        if not name_element or not price_element:
            continue

        name = name_element.get_text(
            strip=True
        )

        price_text = price_element.get_text(
            strip=True
        )

        products.append({
            "name": name,
            "price": price_text
        })

    return products

Advanced Extension

After collecting the products:

  • convert prices to numbers,
  • find the cheapest product,
  • find the most expensive product,
  • calculate the average price,
  • save the records into SQLite.
Website
   ↓
Requests
   ↓
Beautiful Soup
   ↓
Product Data
   ↓
Data Cleaning
   ↓
Analysis
   ↓
SQLite

Professional Goal

Treat scraping as a complete data pipeline rather than simply extracting HTML. Reliable collection, validation, cleaning and storage are equally important.

26

Knowledge Check

1. What is web scraping?

2. Which library can retrieve web pages?

3. What is Beautiful Soup mainly used for?

4. What does find_all() return?

5. Which method can use CSS selectors?

6. Which attribute commonly contains an image URL?

7. Why should scraped data be cleaned?

8. Why might Beautiful Soup fail to find visible content?

9. Why is pagination important?

10. What should you do before scraping a website?

27

Lesson Summary

Web scraping allows Python programs to collect information from web pages and turn unstructured HTML into useful data.

What You Learned

  • Web scraping collects information programmatically from web pages.
  • Requests can retrieve HTML from a server.
  • Beautiful Soup parses HTML and XML.
  • find() finds the first matching element.
  • find_all() finds multiple matching elements.
  • CSS selectors can target complex HTML structures.
  • Text and HTML attributes can be extracted.
  • Tables, links and images can be processed.
  • Scraped data often requires cleaning.
  • Pagination may be necessary for large datasets.
  • JavaScript-generated content may require a different approach.
  • Timeouts and exception handling make scrapers more reliable.
  • Responsible scraping requires respecting applicable rules and website policies.

You now know how to collect and structure information from web pages. In the next lesson, we move into one of Python's most important data-science libraries: NumPy.

Ready to Continue?

You have completed the Python Web Scraping lesson.