Sunday, August 23, 2026
HomeData ScienceWeb Scraping with Python: BeautifulSoup and Requests Complete Guide (2026)

Web Scraping with Python: BeautifulSoup and Requests Complete Guide (2026)

Table of Content

Web scraping is the process of automatically extracting data from websites. For data scientists, it’s an essential technique when the data you need doesn’t come in a convenient CSV or API. Job listings, product prices, news articles, sports statistics, social media posts — all of this data can be collected programmatically with Python’s BeautifulSoup and Requests libraries. This guide walks you through the complete workflow from fetching a page to storing clean, structured data.

Setting Up: Requests and BeautifulSoup

You need two main libraries: requests to fetch web pages and beautifulsoup4 to parse the HTML. Install them with pip:

pip install requests beautifulsoup4 lxml

The basic pattern is always the same — fetch the page content, parse it as HTML, then navigate the tree to extract what you want:

import requests
from bs4 import BeautifulSoup
import time

def get_page(url, headers=None):
    # Fetch a page with error handling and polite delays
    default_headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                      'AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/120.0.0.0 Safari/537.36'
    }
    headers = headers or default_headers
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()  # raise exception for 4xx/5xx
        return response
    except requests.RequestException as e:
        print(f"Error fetching {url}: {e}")
        return None

url = "https://example.com/articles"
response = get_page(url)
if response:
    soup = BeautifulSoup(response.text, 'lxml')
    print(soup.title.text)

Always set a User-Agent header. Without it, many sites will block your requests immediately, assuming you’re a bot. The User-Agent tells the server what browser you’re pretending to be.

BeautifulSoup lets you navigate HTML using element names, CSS classes, IDs, and attributes. The two most important methods are find() (returns the first match) and find_all() (returns all matches as a list). Here’s a practical example scraping job listings:

import requests
from bs4 import BeautifulSoup
import pandas as pd

def scrape_jobs(url):
    response = get_page(url)
    if not response:
        return []

    soup = BeautifulSoup(response.text, 'lxml')
    jobs = []

    # Find all job listing cards
    job_cards = soup.find_all('div', class_='job-card')

    for card in job_cards:
        # Extract specific fields - use .get_text(strip=True) to clean whitespace
        title_elem = card.find('h2', class_='job-title')
        company_elem = card.find('span', class_='company-name')
        location_elem = card.find('span', class_='location')
        salary_elem = card.find('span', class_='salary')

        job = {
            'title':    title_elem.get_text(strip=True) if title_elem else 'N/A',
            'company':  company_elem.get_text(strip=True) if company_elem else 'N/A',
            'location': location_elem.get_text(strip=True) if location_elem else 'N/A',
            'salary':   salary_elem.get_text(strip=True) if salary_elem else 'Not listed',
            'url':      card.find('a', href=True)['href'] if card.find('a') else ''
        }
        jobs.append(job)

    return jobs

CSS selectors via soup.select() are often cleaner than chained find() calls. soup.select('div.job-card h2.title') finds all h2 elements with class “title” inside divs with class “job-card”.

Handling Pagination

a close up of a book with writing on it
Photo by Brett Jordan on Unsplash

Most real scraping jobs require navigating multiple pages. Two patterns cover 90% of cases — URL-based pagination (where the page number is in the URL) and “next page” link scraping:

def scrape_all_pages(base_url, max_pages=10):
    # Scrape multiple pages with polite delays
    all_data = []

    for page_num in range(1, max_pages + 1):
        url = f"{base_url}?page={page_num}"
        print(f"Scraping page {page_num}...")

        response = get_page(url)
        if not response:
            break

        soup = BeautifulSoup(response.text, 'lxml')
        page_data = extract_data(soup)  # your extraction function

        if not page_data:
            print("No data found — reached end of results")
            break

        all_data.extend(page_data)

        # Polite delay between requests (IMPORTANT — be respectful)
        time.sleep(2)  # 2 seconds between requests

    return all_data

def get_next_page_url(soup, current_url):
    # Returns the URL of the next page, or None if there is no next page
    next_btn = soup.find('a', {'rel': 'next'}) or soup.find('a', string='Next')
    if next_btn and next_btn.get('href'):
        from urllib.parse import urljoin
        return urljoin(current_url, next_btn['href'])
    return None

Storing Scraped Data and Avoiding Blocks

Save your scraped data progressively — don’t wait until you’ve scraped everything before saving. If the script crashes halfway through, you lose nothing:

import pandas as pd
import json
from datetime import datetime

def save_data(data, filename_prefix='scraped_data'):
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')

    # Save as CSV for easy analysis
    df = pd.DataFrame(data)
    csv_path = f"{filename_prefix}_{timestamp}.csv"
    df.to_csv(csv_path, index=False)
    print(f"Saved {len(df)} records to {csv_path}")

    return df

To avoid getting blocked: always add delays between requests (2-5 seconds minimum), rotate User-Agents if scraping at scale, respect the site’s robots.txt file, and never hammer a server with parallel requests unless you have explicit permission. For JavaScript-rendered sites where BeautifulSoup returns empty divs, use Playwright or Selenium instead — those tools actually execute JavaScript before returning the page content.

Frequently Asked Questions

Is web scraping legal?
It depends. Scraping publicly available data is generally legal in most countries, but scraping behind login walls, violating a site’s Terms of Service, or using data commercially can be legally problematic. Always check robots.txt and the site’s ToS, and when in doubt, look for an official API instead.

When should I use an API instead of scraping?
Always use an API when one exists. APIs are more stable (the data format doesn’t change when the website redesigns), faster, and explicitly permitted. Scraping is a fallback for when no API is available.

How do I scrape JavaScript-rendered sites?
BeautifulSoup only sees the raw HTML — it can’t execute JavaScript. For React, Vue, or Angular sites that load content dynamically, use Playwright (pip install playwright) or Selenium. Playwright is the modern choice — it’s faster, has an async API, and is easier to use than Selenium.

Leave feedback about this

  • Rating

Durgesh Kekare
Durgesh Kekarehttps://www.dataexpertise.in
Durgesh Kekare is a data science educator and founder of DataExpertise.in. With expertise in Python, machine learning, and analytics, he helps 10,000+ learners break into data careers.

Latest Posts

List of Categories