DEV Community

Cover image for Getting Started With Python Web Scraping: Tools, Tips & Code
Alina Shah for Dev Stack Community

Posted on

Getting Started With Python Web Scraping: Tools, Tips & Code

Have you ever wanted to collect product prices from multiple websites, track job listings, or gather article titles without copying everything manually?

That is where Python web scraping comes in. Instead of opening dozens of pages and transferring information into a spreadsheet, you can write a script to retrieve web pages, extract the information you need, and organize it into a usable format.

The tricky part is that websites don't all store information the same way. Some use straightforward HTML, while others load content with JavaScript, block automated requests, or change their page structure without warning.

In this guide, I will walk through essential Python web scraping tools, show working code examples, and explain how to build easier-to-maintain scrapers.

What Is Python Web Scraping?

Python web scraping is the process of using Python code to retrieve information from web pages and extract specific data from their content.

For example, suppose you want to collect:

  • Product names and prices from a permitted product listing page.
  • Job titles and company names from public job listings.
  • Article headlines and publication dates from a news archive.
  • Publicly available research data from a website.
  • Links and metadata from a directory of resources.

You could copy the information manually, but that becomes inefficient when you need hundreds of records or want to repeat the process regularly.

A scraper automates the workflow:

  1. Send a request to a web page.
  2. Receive the page content.
  3. Parse the HTML.
  4. Identify the elements containing the information.
  5. Extract and clean the values.
  6. Save the results to a file or database.

Python is a popular choice because its libraries make HTTP requests, HTML parsing, browser automation, and data processing relatively straightforward.

The Best Python Web Scraping Tools to Know

You do not need a complicated stack to start scraping. The tools you choose should depend on how the target website delivers its content.

1. Requests: Retrieve Web Pages

The Requests library sends HTTP requests and provides a convenient way to access responses.

It is a good starting point when the information you need is already present in the page's HTML.

Install it with:

pip install requests
Enter fullscreen mode Exit fullscreen mode

A basic example:

import requests

url = "https://example.com"

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

response.raise_for_status()

print(response.status_code)
print(response.text[:500])
Enter fullscreen mode Exit fullscreen mode

The get() method requests the page. The timeout prevents the request from waiting indefinitely, while raise_for_status() raises an exception for unsuccessful HTTP responses.

For real projects, handle network errors and timeouts instead of assuming every request will succeed.

2. Beautiful Soup: Extract Information From HTML

A successful HTTP request does not automatically give you clean data. The response usually contains HTML with headings, links, paragraphs, navigation, and other elements.

Beautiful Soup helps you parse that HTML and select the elements you need.

Install it:

pip install beautifulsoup4
Enter fullscreen mode Exit fullscreen mode

Let's extract the title and headings from a page:

import requests
from bs4 import BeautifulSoup

url = "https://example.com"

response = requests.get(
    url,
    timeout=15
)
response.raise_for_status()

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

page_title = (
    soup.title.get_text(strip=True)
    if soup.title
    else "No title found"
)

print("Page title:", page_title)

for heading in soup.find_all(["h1", "h2"]):
    print(heading.get_text(" ", strip=True))
Enter fullscreen mode Exit fullscreen mode

This example demonstrates two useful techniques: checking whether an element exists before accessing it and using get_text() to remove the HTML markup from the extracted content.

Beautiful Soup is especially useful for small and medium scraping tasks involving predictable HTML.

3. lxml: Parse HTML Efficiently

The lxml library is another option for parsing HTML and XML. It supports XPath expressions, which can be useful when you need to select elements using more detailed structural rules.

Install it with:

pip install lxml
Enter fullscreen mode Exit fullscreen mode

You can use it directly with BeautifulSoup:

from bs4 import BeautifulSoup

html = """
<html>
  <body>
    <h1>Python Web Scraping</h1>
    <p class="summary">A beginner's guide</p>
  </body>
</html>
"""

soup = BeautifulSoup(html, "lxml")

heading = soup.find("h1")
summary = soup.select_one(".summary")

print(heading.get_text(strip=True))
print(summary.get_text(strip=True))
Enter fullscreen mode Exit fullscreen mode

For many beginners, Beautiful Soup with Python's built-in HTML parser is enough. Consider lxml when you need XPath or want to evaluate parsing performance for a larger workload.

4. Scrapy: Build Larger Scraping Projects

Scrapy is a more complete framework for crawling websites and extracting data across multiple pages. It provides request scheduling, item processing, pipelines, and result exporting.

Install it:

pip install scrapy
Enter fullscreen mode Exit fullscreen mode

Create a project:

scrapy startproject my_scraper
Enter fullscreen mode Exit fullscreen mode

Scrapy becomes useful when a project needs to follow pagination, visit many links, manage retries, and process a substantial number of records.

For a single page, it may be unnecessary. For a recurring crawl with several stages, its built-in structure can save time compared with writing everything from scratch.

5. Playwright: Handle JavaScript-Rendered Pages

Some websites display their main content only after JavaScript runs. A simple Requests call may return an HTML shell without the data you expected.

Playwright automates a browser, allowing you to wait for page content and interact with elements when permitted.

Install it:

pip install playwright
playwright install chromium
Enter fullscreen mode Exit fullscreen mode

Example:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()

    page.goto(
        "https://example.com",
        wait_until="domcontentloaded"
    )

    page_title = page.title()
    print(page_title)

    browser.close()
Enter fullscreen mode Exit fullscreen mode

This example opens a page in a browser and retrieves its title. If you need content loaded later by JavaScript, wait for a specific, documented page element rather than adding an arbitrary long delay.

Browser automation consumes more resources than a basic HTTP request, so use it only when the target page actually requires it.

A Complete Python Web Scraping Example

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

For this example, we will use Books to Scrape, a demonstration website intended for scraping practice. The script retrieves book titles and prices from its first catalog page.

Install the dependencies:

pip install requests beautifulsoup4
Enter fullscreen mode Exit fullscreen mode

Then create a Python file named scrape_books.py:

import csv
import requests
from bs4 import BeautifulSoup

URL = "https://books.toscrape.com/catalogue/page-1.html"

HEADERS = {
    "User-Agent": "LearningScraper/1.0"
}


def scrape_books():
    response = requests.get(
        URL,
        headers=HEADERS,
        timeout=15
    )
    response.raise_for_status()

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

    books = []

    for item in soup.select("article.product_pod"):
        title_element = item.select_one("h3 a")
        price_element = item.select_one(".price_color")

        if title_element is None or price_element is None:
            continue

        books.append({
            "title": title_element.get("title", "").strip(),
            "price": price_element.get_text(strip=True)
        })

    return books


def save_to_csv(books):
    with open(
        "books.csv",
        "w",
        newline="",
        encoding="utf-8"
    ) as file:
        writer = csv.DictWriter(
            file,
            fieldnames=["title", "price"]
        )
        writer.writeheader()
        writer.writerows(books)


if __name__ == "__main__":
    books = scrape_books()
    save_to_csv(books)

    print(f"Saved {len(books)} books to books.csv")

    for book in books[:5]:
        print(book)
Enter fullscreen mode Exit fullscreen mode

Run the script:

python scrape_books.py
Enter fullscreen mode Exit fullscreen mode

If the request succeeds and the page structure matches the selectors, the script creates a books.csv file containing the extracted titles and prices.

How the Code Works

Step 1: Request the page. Requests downloads the HTML from the catalog URL.

Step 2: Parse the HTML. Beautiful Soup converts the response into a structure that Python can search.

Step 3: Find book listings. The CSS selector article.product_pod targets each book card.

Step 4: Extract fields. The script retrieves the book title from the link's title attribute and the displayed price from the price element.

Step 5: Save the results. Python's built-in csv module writes the records to a spreadsheet-friendly file.

The script also checks whether the expected elements exist. This matters because a missing element should not crash the entire scraping process unnecessarily.

The example is deliberately limited to one page. A production scraper would need additional handling for pagination, unexpected page layouts, network failures, and website changes.

How to Choose the Right Python Scraping Tool

Use this quick guide when deciding which library to start with.

Tool Best use case Learning curve
Requests Downloading HTML Low
Beautiful Soup Extracting text, links, and attributes Low
lxml HTML/XML parsing and XPath Low to moderate
Scrapy Multi-page crawling and structured scraping pipelines Moderate
Playwright Pages that require browser rendering or interaction Moderate

For a first project, I would start with Requests and Beautiful Soup. They cover most straightforward scraping tasks without adding unnecessary complexity.

Move to Scrapy when the project grows into a crawler. Use Playwright when you confirm that the information depends on browser execution or interaction.

Practical Tips for More Reliable Web Scraping

A script that works once is not necessarily a reliable scraper. Small changes to the website, network conditions, or response format can break it.

1. Check the Website's Rules First

Before collecting data, review the website's terms, applicable policies, and robots.txt guidance. Prefer an official API or permitted data export when available.

Do not assume that publicly visible information is automatically free to collect or reuse. Respect access restrictions, copyright, privacy requirements, and rate limits. Do not bypass authentication, CAPTCHAs, or other technical controls without authorization.

2. Add Timeouts and Error Handling

Network requests can fail because of timeouts, DNS problems, server errors, or temporary connectivity issues.

A simple pattern is:

import requests

try:
    response = requests.get(
        "https://example.com",
        timeout=15
    )
    response.raise_for_status()

except requests.exceptions.Timeout:
    print("The request timed out.")

except requests.exceptions.HTTPError as error:
    print(f"HTTP error: {error}")

except requests.exceptions.RequestException as error:
    print(f"Request failed: {error}")
Enter fullscreen mode Exit fullscreen mode

For recurring jobs, record errors in logs and decide which failures are safe to retry. Respect server-provided rate-limit information and avoid retrying indefinitely.

3. Use Specific CSS Selectors

Broad selectors can collect unrelated information. For example, selecting every paragraph may capture navigation labels, advertisements, and footer text.

Inspect the page's HTML and target the elements that correspond to the data you actually need.

When possible, use stable attributes or semantic elements. Avoid depending on automatically generated CSS class names that may change frequently.

4. Handle Missing and Inconsistent Data

Not every record will contain every field. Prices might be missing, publication dates might use different formats, or a page might have an unexpected layout.

Check for missing elements before extracting values. Normalize whitespace, dates, and numbers, and validate the resulting data before saving it.

If the output will feed a dashboard or database, add tests for the fields that must always be present.

5. Avoid Sending Too Many Requests

High request volumes can overload a website and cause your IP address to be blocked. Keep request rates reasonable, follow published limits, and cache responses when appropriate.

For a small sequential scraper, even a short delay between requests may help reduce unnecessary load:

import time

for url in permitted_urls:
    # Fetch and process the page here.
    time.sleep(1)
Enter fullscreen mode Exit fullscreen mode

This is a basic pacing example, not a guarantee that one request per second is permitted. Follow the target site's actual requirements.

6. Keep Scraped Data Separate From Your Code

Save collected records to CSV, JSON, or a database instead of mixing data extraction with every downstream task.

This makes it easier to inspect the results, rerun analysis without downloading the pages again, and identify whether a problem occurred during retrieval, parsing, or storage.

For recurring projects, record when the data was collected and where it came from. This provides useful context when values change.

Common Python Web Scraping Problems and Fixes

  • The script returns an empty list: Check the page source and confirm that your CSS selectors match the actual HTML. The data might be loaded through JavaScript, or the page may have returned an error message instead of the expected content.
  • The request returns a 403 or 429 response: The server may be denying access or enforcing rate limits. Review the site's rules and reduce request frequency where appropriate. Do not attempt to bypass access controls.
  • The extracted text contains extra whitespace: Use methods such as get_text(" ", strip=True) and normalize the values before saving them.
  • The scraper stops when one element is missing: Check for None before accessing an element's attributes or methods, and log records that do not match the expected structure.
  • The scraper becomes slow: Avoid launching a browser for pages that work with HTTP requests. For larger crawls, consider Scrapy's request scheduling and concurrency controls, configured to respect the target website's limits.
  • The website changes its layout: Add tests that verify important selectors and expected fields. When a scraper runs regularly, monitor record counts and unexpected missing values so structural changes do not go unnoticed.

When Should You Use an API Instead of Web Scraping?

Web scraping is not always the best solution. If a website provides an official API, that is usually the first option to investigate.

An API typically provides structured responses, documented fields, and clearer expectations about access and rate limits. Scraping may be useful when the information is available only in HTML, and you are permitted to collect it.

Before building a scraper, ask three questions:

  1. Is there an official API or downloadable dataset?
  2. Am I permitted to access, collect, and use this information?
  3. Can I maintain the scraper if the page structure changes?

If an API provides the data you need, it may be more reliable and easier to maintain than parsing HTML.

Conclusion

Python web scraping is a practical way to automate data collection, whether you are learning to extract a few headlines or building a recurring data pipeline. Requests and Beautiful Soup are excellent starting points for ordinary HTML pages, while lxml, Scrapy, and Playwright address more specialized needs.

The biggest lesson is to keep the first project simple. Choose one permitted page, inspect its HTML, extract a small set of fields, and save the results to CSV. Once that works, add error handling, validation, logging, and sensible request limits.

Reliable scraping is not just about getting data out of a page. It is about collecting the right information responsibly and building a script that keeps working when conditions change.

FAQs

Is Python good for web scraping?

Yes. Python has libraries for HTTP requests, HTML parsing, browser automation, crawling, and data processing. Beginners can start with Requests and Beautiful Soup, then use tools such as Scrapy or Playwright when their requirements become more complex.

Which Python library is best for web scraping?

Beautiful Soup is a good choice for extracting information from HTML, usually alongside Requests. Scrapy suits larger crawling projects, while Playwright is useful when content requires browser rendering or interaction.

Can Python scrape JavaScript websites?

Yes, but the right method depends on how the website delivers its content. If the data is available through a permitted underlying API or in the initial HTML, a normal HTTP request may be enough. If the page requires browser-side JavaScript, a tool such as Playwright can render it.

Is web scraping legal?

It depends on the website, the data, the access method, and the intended use. Review the site's terms, access rules, privacy obligations, and applicable laws. Public visibility alone does not establish permission to collect or reuse all content.

How do I save scraped data in Python?

You can save scraped records to CSV using Python's built-in csv module or to JSON using the json module. For larger or recurring projects, a database may be a better fit.

Top comments (0)