0%

57- Introduction to Web Scraping

Extract data from websites automatically. Parse HTML, navigate the DOM, and turn web pages into structured data. Your gateway to the data on the internet.

The internet is full of data. News articles, product prices, weather forecasts, sports scores, job listings, and much more. Much of this data is locked inside HTML pages designed for humans, not machines. Web scraping is the automated extraction of data from websites. You write a program that downloads web pages, parses the HTML, extracts the information you need, and saves it in a structured format like JSON or CSV. Python is one of the best languages for web scraping. With libraries like requests for downloading pages and BeautifulSoup for parsing HTML, you can build powerful scrapers quickly. This lesson introduces the fundamentals of web scraping: making HTTP requests, parsing HTML, navigating the document tree, and extracting data. You will also learn about ethical scraping practices and how to avoid being blocked.

🕯️ Magic Note

Web scraping walks a fine line between gathering public data and violating terms of service. Always check a website’s robots.txt file (e.g., https://example.com/robots.txt) and terms of service before scraping. Respect rate limits and identify your bot. Be a good citizen of the web.

Installing Required Libraries
You will need to install third-party libraries for web scraping.

Bash

# Install requests and beautifulsoup4

pip install requests beautifulsoup4

# Optional: lxml parser (faster than built-in)

pip install lxml

# Optional: for JavaScript-heavy sites

pip install selenium

💡 Always use a virtual environment for web scraping projects to isolate dependencies. Different projects may need different versions of scraping libraries.
Making HTTP Requests with requests
The requests library makes HTTP requests simple and human-friendly.

Python

import requests

# GET request (most common)

response = requests.get(“https://example.com”)

# Check status code

print(f”Status code: {response.status_code}”) # 200 means OK

# Get the HTML content

html_content = response.text

print(f”Content length: {len(html_content)} characters”)

# Raise exception for bad status codes

response.raise_for_status() # Raises HTTPError for 4xx or 5xx

# Add headers to mimic a browser

headers = {

“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36”

}

response = requests.get(“https://example.com”, headers=headers)

⚠️ Some websites block requests without a proper User-Agent header. Always set a realistic User-Agent to identify your scraper and reduce the chance of being blocked. Better yet, check robots.txt and respect it.
Parsing HTML with BeautifulSoup
BeautifulSoup parses HTML and makes it easy to navigate and search.

Python

from bs4 import BeautifulSoup

html = “””

<html>

<body>

<h1 id=”title”>Welcome to My Site</h1>

<p class=”description”>This is a paragraph.</p>

<div class=”content”>

<a href=”https://example.com”>Click here</a>

<a href=”https://google.com”>Visit Google</a>

</div>

</body>

</html>

“””

# Parse the HTML

soup = BeautifulSoup(html, “html.parser”)

# Pretty print (makes HTML readable)

print(soup.prettify())

🕯️ Magic Note

BeautifulSoup can use different parsers: “html.parser” (built-in), “lxml” (faster, needs installation), and “html5lib” (most lenient). For most projects, “html.parser” is sufficient.

Finding Elements by Tag Name
Access elements directly by their tag name.

Python

from bs4 import BeautifulSoup

soup = BeautifulSoup(html, “html.parser”)

# Get first h1 tag

h1 = soup.h1

print(f”Tag: {h1}”)

print(f”Text: {h1.text}”)

# Get first p tag

p = soup.p

print(f”Paragraph: {p.text}”)

# Get all a tags (returns a list)

all_links = soup.find_all(“a”)

for link in all_links:

print(f”Link text: {link.text}, href: {link.get(‘href’)}”)

Finding Elements by Class and ID
Use CSS selectors or find()/find_all() with attributes.

Python

from bs4 import BeautifulSoup

soup = BeautifulSoup(html, “html.parser”)

# Find by ID

title = soup.find(id=”title”)

print(f”By ID: {title.text}”)

# Find by class

description = soup.find(class_=”description”) # Note: class_ (underscore)

print(f”By class: {description.text}”)

# Find all elements with a specific class

content_divs = soup.find_all(class_=”content”)

# Using CSS selectors (more powerful)

links = soup.select(“div.content a”)

for link in links:

print(f”CSS selector: {link.get(‘href’)}”)

# CSS selector for ID

title = soup.select_one(“#title”)

print(f”CSS ID selector: {title.text}”)

💡 Use soup.select() with CSS selectors for complex queries. For simple queries, find() and find_all() are more readable. Learn basic CSS selectors: #id for ID, .class for class, tag for tag, parent child for nested elements.
Navigating the Parse Tree
BeautifulSoup allows you to move between parent, children, and siblings.

Python

from bs4 import BeautifulSoup

html = “””

<div id=”container”>

<ul>

<li>Item 1</li>

<li>Item 2</li>

<li>Item 3</li>

</ul>

</div>

“””

soup = BeautifulSoup(html, “html.parser”)

# Get children

container = soup.find(id=”container”)

children = list(container.children) # Includes text nodes

# Get direct children (tags only)

for child in container.find_all(recursive=False):

print(f”Child: {child.name}”)

# Get parent

first_li = soup.find(“li”)

parent_ul = first_li.parent

print(f”Parent of li: {parent_ul.name}”)

# Get siblings

for sibling in first_li.next_siblings:

if sibling.name == “li”:

print(f”Sibling: {sibling.text}”)

Extracting Data: Text, Attributes, and Nested Elements
Use .text or .get_text() for text, .get() for attributes.

Python

from bs4 import BeautifulSoup

html = “””

<div class=”product”>

<h2>Python Book</h2>

<p class=”price”>$29.99</p>

<a href=”/products/123″ class=”details” data-id=”123″>View Details</a>

</div>

“””

soup = BeautifulSoup(html, “html.parser”)

div = soup.find(“div”, class_=”product”)

# Extract text

title = div.h2.text

price = div.find(“p”, class_=”price”).text

print(f”Title: {title}”)

print(f”Price: {price}”)

# Extract attributes

link = div.find(“a”)

href = link.get(“href”)

data_id = link.get(“data-id”)

print(f”Link: {href}”)

print(f”Data ID: {data_id}”)

# get() with default if attribute missing

missing = link.get(“data-missing”, “default”)

print(f”Missing attribute: {missing}”)

Practical Example: Scraping a Book List
A complete example scraping a simple HTML page.

Python

import requests

from bs4 import BeautifulSoup

import csv

# Example: Scraping a demo book catalog

# Note: This uses a fake URL for demonstration

url = “https://books.toscrape.com/” # Real demo site for scraping practice

def scrape_books():

headers = {

“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36”

}

try:

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

response.raise_for_status()

except requests.exceptions.RequestException as e:

print(f”Error fetching page: {e}”)

return []

soup = BeautifulSoup(response.text, “html.parser”)

books = []

# Find all book containers

for article in soup.find_all(“article”, class_=”product_pod”):

title_elem = article.find(“h3”).find(“a”)

title = title_elem.get(“title”, “No title”) if title_elem else “No title”

price_elem = article.find(“p”, class_=”price_color”)

price = price_elem.text if price_elem else “No price”

rating_elem = article.find(“p”, class_=”star-rating”)

rating = rating_elem.get(“class”)[1] if rating_elem else “No rating”

books.append({

“title”: title,

“price”: price,

“rating”: rating

})

return books

def save_to_csv(books, filename=”books.csv”):

if not books:

print(“No data to save”)

return

with open(filename, “w”, newline=””, encoding=”utf-8″) as f:

writer = csv.DictWriter(f, fieldnames=[“title”, “price”, “rating”])

writer.writeheader()

writer.writerows(books)

print(f”Saved {len(books)} books to {filename}”)

if __name__ == “__main__”:

books = scrape_books()

for book in books[:5]:

print(f”{book[‘title’]} – {book[‘price’]} – Rating: {book[‘rating’]}”)

save_to_csv(books)

🕯️ Magic Note

The site books.toscrape.com is specifically designed for practicing web scraping. It has no robots.txt restrictions and is safe to scrape. Always test your scrapers on such practice sites before targeting real websites.

Handling Common Issues
Web scraping can encounter various problems. Here is how to handle them.

Python

import requests

import time

from requests.adapters import HTTPAdapter

from urllib3.util.retry import Retry

# 1. Retry on failure

session = requests.Session()

retry = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])

adapter = HTTPAdapter(max_retries=retry)

session.mount(“http://”, adapter)

session.mount(“https://”, adapter)

# 2. Rate limiting (be polite)

time.sleep(1) # Wait 1 second between requests

# 3. Handle different status codes

response = session.get(“https://example.com”)

if response.status_code == 200:

html = response.text

elif response.status_code == 404:

print(“Page not found”)

elif response.status_code == 429:

print(“Rate limited. Waiting…”)

time.sleep(60)

else:

print(f”HTTP {response.status_code}”)

# 4. Handle missing elements gracefully

element = soup.find(“div”, class_=”maybe-missing”)

if element:

text = element.text

else:

text = “Not found”

Ethical Web Scraping Guidelines
Always follow these principles when scraping.
  • Check robots.txt (e.g., https://example.com/robots.txt) and respect it
  • Read the website’s terms of service
  • Identify yourself with a descriptive User-Agent header
  • Implement rate limiting (delay between requests)
  • Scrape during off-peak hours if possible
  • Cache responses to avoid repeated requests
  • Do not bypass login systems or access non-public data
  • Do not overload the server (use reasonable concurrency)
When to Use Alternatives to Scraping
Before scraping, consider if there is a better way.
  • Check for an official API (most ethical and reliable)
  • Check for RSS feeds or structured data exports
  • Look for JSON data embedded in the page (many sites include JSON in <script> tags)
  • Contact the website owner about data access
Common Mistakes with Web Scraping
  • Not handling missing elements (leading to AttributeError)
  • Not using rate limits (getting IP banned)
  • Parsing JavaScript-rendered content with BeautifulSoup (use Selenium for dynamic sites)
  • Hard-coding selectors that may change (build resilient scrapers)
  • Not checking robots.txt
  • Scraping too aggressively (overwhelming the server)
Check Your Understanding
  • What is the purpose of the requests library?
  • How do you find all elements with a specific class using BeautifulSoup?
  • What is the difference between find() and find_all()?
  • Why should you add a delay between requests?
  • What is robots.txt and why should you check it?
  • How do you extract the href attribute from an <a> tag?

⚡ Whisper

The web is an ocean of data. Web scraping is your fishing rod. With requests you cast your line. With BeautifulSoup you examine what you catch. You find the .title and .price hidden among the HTML. You extract them carefully, handling the ones that are missing, ignoring the ones you do not need. But fishing has rules. Respect the robots.txt signs. Do not fish in protected waters. Do not fish too fast. Leave some for others. Identify yourself. Be polite. And remember: sometimes the website offers an API, a cleaner way to get data. Check for it before you scrape. But when there is no API, and the data is public, and the rules allow it, scraping is your tool. Use it wisely. Respect the web. It gives you data. Give it space in return.

Related posts