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.
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.
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
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.
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
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.
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
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.
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.
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
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.
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]
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.
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.
Extracting Links
Links are represented by the HTML
<a> element.
links = soup.find_all("a")
for link in links:
url = link.get("href")
text = link.get_text(
strip=True
)
print(text, url)
A real website may contain links without an
href, so using get() can make
the code safer.
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']
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.
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
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.
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.
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.
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
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.
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.
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.
Mini Challenge — Ecommerce Product Scraper
Build a small ecommerce data collector that retrieves publicly accessible product information from a permitted practice website.
Your Mission
- Send an HTTP GET request.
- Set a reasonable timeout.
- Check the HTTP response.
- Parse the HTML with Beautiful Soup.
- Find product cards.
- Extract product names.
- Extract product prices.
- Clean the prices.
- Store the records in a Python list.
- 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.
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?
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.