Web Scraping
Arslan
ArslanMar 15, 2026

Learn how to build a web scraper in Python from scratch. Follow a step-by-step tutorial, skip fragile HTML parsing with the hidden JSON API trick, and scale to a pipeline.

How to Build a Web Scraper: Beginner Python Guide

Every data-driven project starts with one core problem: the information you need is trapped on someone else's website. If you want to know how to build a web scraper, you need to understand the mechanics of extraction. A web scraper programmatically mimics a browser to retrieve and structure this information.

But before you write a single line of Python, you need a strategy. I once copied a parsing tutorial perfectly, pointed it at a modern webpage, and received a completely empty HTML response because the data was rendered by JavaScript. If you start your extraction process in the browser rather than the script, you avoid this trap entirely. In this guide you will learn the classic Python extraction method, a hidden API shortcut that skips CSS selectors, and how to scale your simple script into an automated data pipeline.

Automated bots made up 51% of all global web traffic in 2024. This is why websites are increasingly aggressive about blocking naive web scraping scripts.

What is a Web Scraper?

A web scraper is an automated script that sends an HTTP request to a webpage, extracts specific structured data fields from the HTML or JSON response, and saves that data into a usable format like CSV or a database.

Web Scraper vs. Web Crawler vs. API

These three terms describe different web data acquisition methods, and choosing the wrong one is the most common beginner mistake. Use the table below to pick the right tool before you write any scraping scripts.

MethodWhat it doesWhen to use itFragility
Web crawlerDiscovers and maps URLs by following links, without extracting specific page contentYou need to find every page on a site before deciding what to extractLow — link discovery rarely breaks
Web scraperExtracts specific structured data fields from a known URLYou have target pages and need named fields (title, price, link)High — breaks when CSS classes or layout change
APIAn official channel provided by a platform to return structured data directlyThe platform publishes documented endpoints for the data you needLowest — versioned and contract-backed

Automated web scraping makes sense when you need public page data for research or monitoring, but no official API exists. It lets you automate structured extraction directly from the frontend. If a site provides a public API, use it first.

How Web Scraping Works

The core extraction workflow is: Send an HTTP request -> Receive the HTML/JSON response -> Parse the DOM -> Select elements -> Store the structured data.

Web scraping programmatically replicates what your browser does manually. You request a URL, receive text back, locate the targeted text, and save it as structured data.

Send an HTTP Request

Your script asks a server for a page using a specific URL. In Python, the Requests library handles sending this underlying HTTP request.

Download the HTML or JSON Response

The server returns a payload. For traditional pages, this payload is raw HTML markup. If the page requests data in the background, the payload is often a cleanly formatted JSON object. The server also returns a status code. You want a 200 (Success) and must avoid a 403 (Forbidden) or a 429 (Too Many Requests).

Parse the DOM

HTML is just a long string of text. The Document Object Model (DOM) is the tree-like structure a browser builds from that HTML. To write targeted rules, you must convert the raw HTML string into a searchable DOM tree. BeautifulSoup is the standard Python parser for this job.

Extract Data with CSS Selectors

CSS selectors are rules targeting specific DOM elements. The exact selectors frontend developers use to style a webpage (like .product-title or #price-tag) allow scrapers to locate the exact text nodes you want to extract.

Store the Output as Structured Data

Extracted data disappears when the script finishes running unless you save it. JSON is the default format because it seamlessly handles nested relationships and drops straight into downstream applications. CSV works for flat spreadsheet exports. SQLite is ideal for persistent database storage.

Before You Write Code: Choose the Right Scraping Method

Always use the lightest extraction method that returns structured data reliably.

Beginners often rush straight into writing HTML parsers. Professionals audit the website first to find the path of least resistance. The order is strict: API first, hidden JSON second, HTML parsing third, and browser automation last.

Check for an Official API or Dataset

Look for developer documentation, a public sitemap , or downloadable datasets. Supported data sources do not break when a frontend designer changes a CSS class name.

Inspect the Network Tab for Hidden JSON

Open your browser Developer Tools, navigate to the Network tab, reload the page, and filter traffic by XHR or Fetch. You are looking for background requests returning JSON responses. Modern web applications load an empty HTML shell and populate it by fetching a JSON file. Finding this JSON lets you scrape a website directly to JSON and bypass HTML parsing entirely.

Scrape the HTML Only if Necessary

If the page is static and server-rendered, the data lives directly in the visible HTML markup. In this scenario, combining the Requests library with BeautifulSoup is the correct lightweight approach.

Use Browser Automation for JavaScript Pages

Escalate to heavy tools only when required. If a page requires JavaScript execution to render content, you must load an actual browser engine. Playwright is the default modern option. Selenium is an older alternative that remains viable if it already exists in your QA stack.

How to Build a Web Scraper with Python

A basic Python scraper loops over HTML elements that match your chosen CSS selectors and appends the extracted text to a structured list. If you have ever wondered how to build a web scraper from scratch in Python, this is the smallest complete version.

We will build a simple beginner web scraping tutorial targeting a safe static page. This script intentionally strips away modern web complexity so you can master the core mechanics before layering on rendering, proxies, and scale.

Install Python and the Required Packages

Ensure you are running Python 3.12 or newer. Open your terminal and install the HTTP client and HTML parser.

code
pip install requests beautifulsoup4

Inspect the HTML and Identify Selectors

Right-click a product card in your browser and select "Inspect". Identify the CSS classes wrapping your data.

  • Card container: <article class="product_pod">
  • Title element: <h3><a title="Book Name">
  • Price element: <p class="price_color">
  • Link element: <a href="...">

Send the Request and Parse the Page

Create a new file named scraper.py. We will ask the server for the page and convert the raw HTML into a searchable DOM object.

code
import requests
from bs4 import BeautifulSoup
import json
url = "https://books.toscrape.com/catalogue/category/books/science_22/index.html"
response = requests.get(url)
if response.status_code == 200:
    soup = BeautifulSoup(response.text, "html.parser")
else:
    print(f"Failed to fetch page. Status: {response.status_code}")

Extract Fields and Save Straight to JSON

Find all product cards, loop through them, extract the text nodes, and store the output as structured JSON. Saving straight to JSON is deliberate: it hands you clean, nested, machine-readable data you can feed to any application without a second cleanup pass.

code
scraped_data = []
cards = soup.select("article.product_pod")
for card in cards:
    title = card.select_one("h3 a")["title"]
    price = card.select_one("p.price_color").text.strip()
    link = card.select_one("h3 a")["href"]
    scraped_data.append({
        "title": title,
        "price": price,
        "url": f"https://books.toscrape.com/catalogue/category/books/science_22/{link}"
    })
with open("science_books.json", "w", encoding="utf-8") as file:
    json.dump(scraped_data, file, indent=4)
print(f"{len(scraped_data)} items scraped and saved to JSON.")

A sample record in science_books.json looks like this:

code
{
    "title": "The Grand Design",
    "price": "£13.76",
    "url": "https://books.toscrape.com/catalogue/category/books/science_22/the-grand-design_405/index.html"
}

This code works because it strictly follows the fundamental extraction pipeline. It sends the request, builds the DOM, targets the CSS selectors, and maps the unstructured text into a structured JSON object.

When to Stop Maintaining a Hand-Built Scraper

Here is the part most tutorials skip: a hand-built script is worth maintaining only while three conditions hold. The moment any one of them breaks, the maintenance cost outruns the value, and you should move to managed infrastructure. The break-points are concrete.

  • Rendering: The page is static and server-rendered. Once the target ships client-side rendering, requests returns an empty shell and you are forced into a headless browser you now have to run and update.
  • Proxy rotation: You can scrape from a single IP without triggering 403s or 429s. Once the site starts blocking you, you need premium residential IPs and rotation — infrastructure that is expensive to build and babysit yourself.
  • Scale: You are scraping tens of pages. Once you need tens of thousands of dynamic pages daily, headless-browser memory leaks, concurrency, and broken custom parsers quietly consume your engineering time.

If you are still inside all three conditions, keep the script. When you cross any of them, that is the signal to hand rendering, proxy rotation, and scale to a platform like Olostep instead of expanding the script.

The Hidden API Shortcut Most Tutorials Skip

If the user's browser fetches data via a background JSON request, your Python script should fetch that exact same JSON request.

Parsing HTML is fragile. Bypassing the DOM to request the background JSON directly is faster, more reliable, and requires zero CSS selectors — which is also the cleanest way to scrape a website directly to JSON.

Find the JSON Request in DevTools

Navigate to your target website. Right-click anywhere, open "Inspect", and click the Network tab. Reload the page and filter by Fetch/XHR. Click through the listed requests and check the "Response" pane. You are searching for a clean list of objects matching the data visible on the screen.

Recreate the Request in Python

Copy the endpoint URL. Your scraping script becomes incredibly simple.

code
import requests
api_url = "https://api.example.com/v1/products?category=shoes"
headers = {"User-Agent": "Mozilla/5.0"}
response = requests.get(api_url, headers=headers)
data = response.json()
print(data["products"][0]["title"])

Parsing JSON removes fragility. You extract clean fields without regex cleanup and navigate pagination simply by changing a URL parameter like ?page=2.

What to Do When a Website Uses JavaScript

A JS-rendered page requires you to either intercept the background API or use a headless browser like Playwright to execute the code.

The most common failure for a beginner occurs when the page loads perfectly in the browser, but the script returns empty HTML.

If your selectors return None, right-click the page and select "View Page Source". If the source code lacks the visible data and instead shows an empty shell like <div id="app"></div>, the page uses Client-Side Rendering. The content appears only after the browser executes the JavaScript.

The requests library is an HTTP client, not a browser. It downloads the initial HTML file and stops. If there is no clean background API to intercept, you must use a headless browser. Playwright launches a real instance of Chromium, executes the JS, waits for the network to idle, and lets you extract the fully rendered DOM.

Common Web Scraping Problems and Fixes

Scrapers are inherently brittle. Because you do not control the target website, your code will eventually break. Use this troubleshooting table to map the symptom to the fix.

SymptomLikely causeFix
Selectors return nothing (None)The site changed its CSS class names, or the element is rendered by JavaScriptPrint the raw HTML in your script to confirm the element exists in the response; if it doesn't, switch to a headless browser
403 Forbidden or 429 Too Many RequestsThe server rejected or rate-limited your requestSlow down your extraction rate, add time.sleep() between requests, and pass a standard browser User-Agent header
Pagination hides dataYour scraper only captured the first pageFind the "Next Page" button's href and loop, or inspect the Network tab for the "load more" API parameters
Messy or duplicated dataInconsistent whitespace and repeated recordsNormalize with .strip() and deduplicate the final list on a unique product ID
Getting blocked repeatedly across IPsThe site fingerprints and bans single-IP trafficRotate premium residential IPs and throttle request volume; this is the point most teams move to managed infrastructure

From One Script to an Automated Scraping Pipeline

A script becomes a scalable scraping pipeline when you add persistent storage, retry logic, scheduling, and infrastructure management.

A script runs once on your laptop. A pipeline runs daily in the cloud, survives network errors, and feeds clean structured data to downstream applications.

Add Resilience and Scheduling

Production scrapers require robust logic. Add timestamps to every row to track data freshness. Wrap your HTTP requests in retry logic to handle temporary network blips. To schedule recurring runs, use cron on a Linux server for simple jobs, or orchestration tools like Airflow for complex workflows.

Leverage AI for Comprehension

The data extraction landscape is shifting from brittle, hand-written selectors toward models that comprehend a page. Open-source tools like Crawl4AI use AI models to extract nested fields based on natural language prompts, so when a layout changes, the prompt still resolves where a hard-coded selector would have snapped. The same shift is showing up in research: recent AI benchmarking shows end-to-end LLM agents can autonomously navigate and extract complex web data using just a single natural language prompt with minimal refinement (Source: Beyond BeautifulSoup, arXiv 2026 ).

This is also where managed platforms are heading. Olostep exposes prompt-based Parsers that extract JSON with just a prompt — including self-healing parsers that adapt when a source page changes — plus /agents that run prompt-driven research and automation on a schedule. The practical upshot for a scraper builder: you describe the fields you want in natural language instead of maintaining CSS selectors that break every time the frontend ships a redesign.

Scale Seamlessly with Olostep

Managing custom Python scripts works beautifully for tens of pages. It becomes a nightmare when you need to scrape tens of thousands of dynamic pages daily . Managing proxy rotation, headless browser memory leaks, and broken custom parsers drains engineering time.

Olostep is the infrastructure layer for exactly that point. It turns any URL into clean Markdown, HTML, or schema-defined JSON through one unified Web Data API, with the operational pieces handled for you:

  • Structured outputs on demand: Retrieve content as Markdown, HTML, raw PDF, or structured JSON, and specify a schema so you get only the clean fields you asked for.
  • Full JS rendering by default: Every request renders in a real browser, so client-side-rendered content is captured without you running a headless browser yourself.
  • Residential proxy rotation: Requests run through premium residential IPs with rotation to reduce bot detection and 403/429 blocks.
  • Batch throughput: Batch Executions accept 100 to 100,000 URLs and return content in 5–7 minutes, scaling toward 1 million requests in around 15 minutes with multiple threads.

Instead of fighting broken selectors, you interface with a single API that discovers, extracts, and structures public web data reliably — which is exactly the JSON-first, structured-output workflow this guide has been building toward.

Disclaimer: This is practical guidance, not legal advice.

Legal risk depends heavily on what data you extract, how you access it, and your jurisdiction. Web scraping public, non-personal factual data is generally legal. Scraping private data behind a login or extracting Personally Identifiable Information (PII) carries significant risk.

Before launching a scraper, confirm the data is public, avoid PII, and respect the server load by limiting your request rate. While a beginner scraping a practice site faces zero risk, commercial operations must stay vigilant. Always throttle your request speed to minimize server impact.

FAQ

What is a web scraper?

A web scraper is an automated tool that sends an HTTP request to a webpage, extracts specific structured data fields from the HTML or JSON response, and saves that data into a usable format like JSON, CSV, or a database.

How do I build my own web scraper?

Install Python, the Requests library, and BeautifulSoup. Send a request to a static page, parse the HTML into a DOM, target your fields with CSS selectors, and write the results to a JSON file. That five-step loop is the complete core of any scraper.

What is the easiest way to build a web scraper for beginners?

Start with a static, server-rendered practice site so there is no JavaScript to fight. Use Requests plus BeautifulSoup, target a handful of CSS selectors, and save straight to JSON. Master that before touching headless browsers or proxies.

What are the best tools and libraries for building a web scraper in Python?

Requests sends the HTTP request, BeautifulSoup parses static HTML, and Playwright drives a headless browser for JavaScript-rendered pages. For scale, a managed Web Data API like Olostep replaces the infrastructure you would otherwise build yourself.

What is Beautiful Soup?

BeautifulSoup is the standard Python library for parsing HTML. It converts a raw HTML string into a searchable DOM tree so you can locate and extract elements with CSS selectors.

What is the Requests library used for?

Requests is Python's HTTP client. In a scraper it sends the request to the target URL and returns the response body and status code. It downloads HTML but does not execute JavaScript.

How do I avoid getting blocked when scraping?

Pass a standard browser User-Agent header, throttle your request rate with time.sleep(), and respect the site's load. If a site still blocks you across IPs, rotate premium residential proxies — the point at which most teams move to managed infrastructure.

Can ChatGPT do web scraping?

Not reliably on its own — a chat model cannot fetch JS-rendered pages, rotate proxies, or handle scale. But LLMs are now used inside scraping tools to extract fields from a prompt instead of CSS selectors, as with Crawl4AI and Olostep's prompt-based Parsers.

What is the easiest web scraping tool?

For code-free extraction, no-code browser extensions and visual scrapers are easiest. For developers who want structured JSON at scale without maintaining infrastructure, a unified Web Data API such as Olostep is the simplest reliable option.

Scraping public, non-personal factual data is generally legal, but it depends on jurisdiction and access method. Extracting private data behind a login or collecting PII carries significant legal risk.

Do you need coding to scrape websites?

No. While Python provides the most flexibility, non-technical users can use no-code browser extensions or visual scraping software to extract structured data.

What programming language is best for scraping?

Python is the best language for web data extraction. It has the most robust ecosystem of libraries, including BeautifulSoup and Playwright, along with native integrations for data engineering pipelines.

Next Steps

You now possess the foundational workflow to build a web scraper. The key to mastering this skill is iteration.

  • Inspect the source first: Always open the Network tab to check for hidden JSON APIs before writing HTML parsers.
  • Start small: Use Python to target basic CSS selectors and output clean JSON data.
  • Scale with intent: Escalate to browser automation, scheduling tools, or managed infrastructure like Olostep only when JavaScript rendering, proxy rotation, or scale demands it.

About the Author

Arslan Ali

Co-Founder, Olostep · San Francisco, CA

Arslan is the co-founder of Olostep, a web data infrastructure platform that helps developers and teams access, extract, and structure web data at scale. He works closely on the product and technology behind Olostep, with a focus on building reliable infrastructure for web scraping, search APIs, and structured web data.

Read more