Back to Blog

Web Scraping · Last updated: July 28, 2026

How to Scrape Data From a Website: A Practical Guide

To scrape data from a website, first identify the fields you need, check for an official API or export, and determine whether the page contains the data in its original HTML. You can then choose a spreadsheet import, no-code tool, Python scraper, browser automation, or managed service.

The important question is not merely whether one page can be scraped. It is whether the method can collect all required records accurately, repeat the task, and remain manageable when the website changes.

Extraction pipeline

  1. Website
  2. Identify required fields
  3. Choose extraction method
  4. Collect records
  5. Validate
  6. Clean & structure
  7. CSV / Excel / JSON / Database / API
Website data extraction from web pages into structured output formats
Choose the extraction method after defining the required output.

What is website data scraping?

Web scraping retrieves content from webpages and converts selected information into structured records.

For example, an ecommerce scraper might collect:

  • Product name, price, availability, seller, rating, and product URL

Webpage

  • Product name
  • Price
  • Availability
  • Seller
  • Rating
  • URL

Scraping

Selects required information

Structured record

CSV · Excel · JSON · Database · API

The output can be stored in CSV, Excel, JSON, a database, or delivered through an API. For recurring price collection, see our guide to ecommerce price scraping.

Crawling

Discovers pages and links

Website → category → page → page

Scraping

Extracts selected fields

Product page → name / price / URL

Scraping differs from crawling. Crawling discovers pages and links, while scraping extracts selected fields from those pages. Learn the difference between crawling and scraping.

Define the required output first

At minimum, define:

  • Source website and example URLs
  • Required fields, page types, and estimated volume
  • Refresh frequency, output format, duplicate rules, missing-value rules, and completeness checks
  • Source

    Example URLs

  • Schema

    SKU / Name / Price / Availability / URL

  • Volume

    Expected record count

  • Schedule

    Refresh frequency

  • Quality

    Duplicate + missing-value rules

  • Delivery

    CSV / API

RequirementExample
Page typeProduct-detail pages
Required fieldsSKU, name, price, availability, URL
Estimated volume8,000 products
Refresh frequencyDaily
OutputCSV and API
Duplicate ruleOne record per source product ID
Missing priceKeep the record and mark the price unavailable

This definition gives you a measurable result. A scraper that returns 7,200 records is not necessarily successful when the source contains 8,000 relevant products.

Use the checklist at the end of this guide to confirm requirements before you choose a tool or write code.

View the Checklist

Check for an API or export

Before scraping, check whether the source already offers:

  • A public or licensed API, CSV or Excel export, RSS or XML feed
  • Reporting download, partner integration, data feed, or webhook

Before scraping, check for

  • API
  • CSV / Excel
  • RSS / XML
  • Partner integration
  • Data feed
  • Reporting export
  • Webhook

Official data source available? Yes → Evaluate its field coverage, restrictions, freshness and cost

No / insufficient coverage → Evaluate appropriate web extraction

An official data source may offer documented fields and more stable access than parsing a webpage. However, an API may not expose every field visible on the site. Compare its coverage, restrictions, update frequency, and cost with your actual requirement.

Determine whether the page is static or dynamic

This decision determines whether a basic HTTP request is enough.

Static pages

  1. Server
  2. HTML already contains data
  3. Requests
  4. Beautiful Soup
  5. Structured record

JavaScript-rendered pages

  1. Server
  2. Basic HTML shell
  3. Browser executes JavaScript
  4. Visible data appears
  5. Playwright / browser automation

Static pages

On a static page, the required data is included in the HTML returned by the server. A Python script can usually request the page, parse the HTML, select elements, and save the fields.

Requests and Beautiful Soup are commonly used for this type of task. Requests supports HTTP response handling and explicit timeouts, while Beautiful Soup can select parsed elements using tags, attributes, and CSS selectors.

JavaScript-rendered pages

Some pages return a basic HTML shell and load visible data through JavaScript after the browser opens. A normal HTTP request may return a successful status code without containing the products, prices, or records visible on screen.

To diagnose this:

  1. Open the page in a browser and view the original page source.
  2. Compare it with the rendered DOM in developer tools.
  3. Review relevant requests in the Network panel.
  4. Check whether an official API provides the required data.
  1. 01

    View original page source

  2. 02

    Compare with rendered DOM

  3. 03

    Inspect relevant Network requests

  4. 04

    Check for an official API

When the data appears only after rendering, browser automation such as Playwright may be needed. Do not assume that a network endpoint found in developer tools is an unrestricted public API.

Comparison between static website content and dynamically loaded web content
Original HTML may not contain data that appears only after JavaScript runs.

Seven ways to scrape website data

Decision tree for selecting the appropriate website scraping method
Match the method to page behavior, volume, and maintenance capacity.
  • Manual collection

    Best use: A few one-time records

    Main limitation: Slow and inconsistent

  • Spreadsheet import

    Best use: Simple public tables

    Main limitation: Limited dynamic-page support

  • Browser extension

    Best use: Small lists with repeated layouts

    Main limitation: Fragile on complex pagination

  • No-code tool

    Best use: Visual setup without programming

    Main limitation: Platform limits and recurring cost

  • Python HTTP scraper

    Best use: Static HTML and custom processing

    Main limitation: Does not render browser JavaScript

  • Browser automation

    Best use: Dynamic and interactive pages

    Main limitation: Higher maintenance and resource use

  • Managed extraction

    Best use: Recurring or business-critical collection

    Main limitation: Requires scope and budget

Method path — may fit

  1. How much data? Very small / one-time → Manual
  2. Is the source a simple public table? Yes → Spreadsheet
  3. Need simple point-and-click extraction? → Browser extension / no-code
  4. Is the data present in raw HTML? Yes → Python + Requests / Beautiful Soup
  5. Does content require JavaScript or interaction? Yes → Browser automation
  6. Is extraction recurring, multi-source or business-critical? → Evaluate managed extraction

01

1. Manual collection

Manual copy and paste can be acceptable when only a few records are needed, the task will not be repeated, and the source uses a simple table. It becomes unsuitable when the data spans many pages or needs regular updates.

02

2. Spreadsheet imports

Excel and Google Sheets can import some public HTML tables or feeds. This approach often fails with JavaScript-rendered content, infinite scroll, nested product cards, location-based results, complex pagination, and required user interactions.

03

3. Browser extensions

Browser extensions let users select visible elements and export repeated records without coding. Confirm whether the extension collected every page, records loaded after scrolling, optional fields, correct URLs, and unique rows. A correct-looking preview does not prove completeness.

04

4. No-code and AI-assisted tools

No-code tools can identify repeated page structures and schedule extraction. Test total record count, pagination, missing values, duplicate rows, export structure, second-run consistency, and error reporting. AI-assisted field detection may still choose the wrong element or miss alternate layouts.

05

5. Python with Requests and Beautiful Soup

Suitable when data is present in returned HTML, you need custom cleaning rules, the structure is reasonably consistent, and someone can maintain the code. Requests does not apply a timeout unless one is provided, so production code should set an explicit timeout and handle request errors.

06

6. Browser automation with Playwright

Playwright controls a real browser environment, allowing JavaScript to execute before extraction. Use it when content appears after rendering, clicking or scrolling loads records, or client-side pagination is used. Playwright locators support automatic waiting—prefer meaningful page attributes over fragile nested selectors.

07

7. Managed data extraction

Appropriate when thousands of pages must be collected, multiple websites must be combined, data must be refreshed on a schedule, records need cleaning or deduplication, or internal engineering capacity is limited.

Nenodata presents web scraping, custom data extraction, JavaScript rendering, data transformation, and file or API delivery as service areas. See professional website data extraction.

Python example for a static page

The following pattern demonstrates how a basic static-page scraper is structured. Before publishing or using it, replace the sample URL and selectors with a tested, approved demonstration source.

Install the libraries

Bash

pip install requests beautifulsoup4

Request and parse the page

Python

from __future__ import annotations

import requests
from bs4 import BeautifulSoup

URL = "https://approved-demo-site.example/catalog"

try:
    response = requests.get(URL, timeout=20)
    response.raise_for_status()
except requests.RequestException as exc:
    raise SystemExit(f"Unable to retrieve the page: {exc}") from exc

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

Extract fields

Assume each item has this simplified structure:

HTML

<article class="product-card">
  <h2 class="product-name">Example Product</h2>
  <span class="price">$49.00</span>
  <a class="details" href="/products/example-product/">View product</a>
</article>

The extraction logic could be:

Python

from urllib.parse import urljoin

records: list[dict[str, str]] = []

for card in soup.select("article.product-card"):
    name_node = card.select_one(".product-name")
    price_node = card.select_one(".price")
    link_node = card.select_one("a.details")

    if name_node is None or link_node is None:
        continue

    relative_url = link_node.get("href", "")

    records.append(
        {
            "name": name_node.get_text(" ", strip=True),
            "price": (
                price_node.get_text(" ", strip=True)
                if price_node
                else ""
            ),
            "url": urljoin(URL, relative_url),
        }
    )

Export the result to CSV

Python

import csv
from pathlib import Path

output_path = Path("products.csv")

with output_path.open("w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(
        file,
        fieldnames=["name", "price", "url"],
    )
    writer.writeheader()
    writer.writerows(records)

print(f"Saved {len(records)} records to {output_path}")

Validate the extracted records

Creating a file is not the end of the task. Check:

  • Expected versus extracted record count
  • Blank required fields, duplicate identifiers, invalid URLs, inconsistent prices
  • Missing pages and second-run differences

Raw extracted records

  1. Record count
  2. Required fields
  3. Duplicate check
  4. URL validation
  5. Value consistency
  6. Repeat run

Validated dataset

Basic validation example

A basic validation function could be:

Python

def validate_records(records: list[dict[str, str]]) -> None:
    seen_urls: set[str] = set()

    for index, record in enumerate(records, start=1):
        if not record["name"]:
            raise ValueError(f"Record {index} has no name")

        if not record["url"]:
            raise ValueError(f"Record {index} has no URL")

        if record["url"] in seen_urls:
            raise ValueError(
                f"Duplicate URL found: {record['url']}"
            )

        seen_urls.add(record["url"])

Handle pagination

Pagination may use numbered pages, next-page links, offset parameters, cursors, load-more buttons, infinite scrolling, or JavaScript requests. Do not assume that adding ?page=2 will work.

  • Numbered pages
  • Next page
  • Offset
  • Cursor
  • Load more
  • Infinite scroll
  • JavaScript requests

Page 1 → Page 2 → Page 3 → … → stop when no records remain

For numbered pages, a simplified loop might look like:

Python

import time

BASE_URL = "https://approved-demo-site.example/catalog?page={page}"

all_records: list[dict[str, str]] = []

for page_number in range(1, 11):
    page_url = BASE_URL.format(page=page_number)

    response = requests.get(page_url, timeout=20)
    response.raise_for_status()

    soup = BeautifulSoup(response.text, "html.parser")
    cards = soup.select("article.product-card")

    if not cards:
        break

    for card in cards:
        name_node = card.select_one(".product-name")

        if name_node:
            all_records.append(
                {
                    "name": name_node.get_text(" ", strip=True),
                    "source_page": str(page_number),
                }
            )

    time.sleep(1)

Check whether featured records repeat across pages. Where possible, deduplicate using a stable source identifier rather than the product name.

Scrape dynamic content with Playwright

A simplified browser-automation pattern looks like this:

  1. Open browser
  2. Load JavaScript
  3. Wait for meaningful element
  4. Locate cards
  5. Extract fields
  6. Close browser

Python

from playwright.sync_api import sync_playwright

URL = "https://approved-demo-site.example/dynamic-catalog"

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

    page.goto(
        URL,
        wait_until="domcontentloaded",
        timeout=30_000,
    )

    cards = page.locator("article.product-card")
    cards.first.wait_for(state="visible")

    records: list[dict[str, str]] = []

    for index in range(cards.count()):
        card = cards.nth(index)

        records.append(
            {
                "name": card.locator(
                    ".product-name"
                ).inner_text(),
                "price": card.locator(
                    ".price"
                ).inner_text(),
            }
        )

    browser.close()

Clean and structure the data

Raw webpage text often requires additional processing.

Normalize text

Python

def normalize_text(value: str) -> str:
    return " ".join(value.split())

Separate prices into useful fields

Instead of storing only $1,299.00, consider:

JSON

{
  "price_amount": 1299.00,
  "price_currency": "USD",
  "price_display": "$1,299.00"
}

Do not infer a currency solely from its symbol when the source serves several countries.

Preserve missing-value meaning

Price unavailable, price equal to zero, field not displayed, extraction failed, and product out of stock are not equivalent. Store enough context to distinguish them.

These are not equivalent

  • Price = 0
  • Price unavailable
  • Field absent
  • Extraction failure
  • Out of stock

Use reliable duplicate keys

Product ID, SKU, listing ID, canonical URL, or source record ID are stronger keys than product names alone.

Add collection metadata

For recurring extraction, include source URL, collection timestamp, region, run ID, scraper version, and validation status.

  • Source URL
  • Timestamp
  • Region
  • Run ID
  • Scraper version
  • Validation status

Choose the right output format

Website data delivered through spreadsheets databases JSON APIs and dashboards
Choose the format your downstream workflow can consume and validate.
  • CSV

    Best use: Flat data and simple transfer

    Main limitation: Does not represent nested data well

  • Excel

    Best use: Manual review and business analysis

    Main limitation: Less suitable for large automation workflows

  • JSON

    Best use: Applications and nested records

    Main limitation: Less convenient for spreadsheet users

  • Database

    Best use: Large recurring datasets

    Main limitation: Requires database management

  • API

    Best use: System-to-system delivery

    Main limitation: Requires endpoint and access management

Nenodata's workflow page lists files, APIs, and webhooks among delivery options. See how Nenodata processes website data. Confirm exact delivery timing and availability for your project during scoping.

Common scraping problems

  • Visible data is absent from the response

    Likely cause: JavaScript rendering

    Next step: Compare raw HTML with the rendered DOM

  • Only the first page is collected

    Likely cause: Pagination was not handled

    Next step: Identify the real page or cursor mechanism

  • Duplicate rows appear

    Likely cause: Page overlap or repeated featured records

    Next step: Deduplicate using a stable identifier

  • Some fields are blank

    Likely cause: Multiple layouts or optional values

    Next step: Inspect varied records and log missing fields

  • The script breaks after a redesign

    Likely cause: Fragile selectors

    Next step: Use meaningful selectors and validation

  • The request hangs

    Likely cause: Missing timeout or unresponsive source

    Next step: Add explicit timeouts and error handling

  • Access errors appear

    Likely cause: Rate limits or access restrictions

    Next step: Stop and review permitted access options

Requests documentation confirms that requests do not time out unless a timeout is explicitly set.

Operational guidance

Collect website data responsibly

A publicly viewable page does not automatically mean that every collection or reuse scenario is permitted.

Before collecting data:

  • Review applicable terms and policies; prefer an official API or feed where appropriate
  • Respect authentication and access controls; avoid private or restricted information
  • Use reasonable request rates; collect only fields needed for the stated purpose
  • Apply suitable data-protection controls; obtain legal advice for high-risk use cases

A robots.txt file mainly instructs search-engine crawlers about URL access. Google states that it is not a mechanism for keeping a page out of search results. It should not be treated as a complete legal determination for an unrelated data-collection project.

This section is operational guidance, not legal advice.

When a basic scraper is no longer enough

A small script may be suitable when one static source is involved, the dataset is limited, collection is occasional, the structure is stable, and missing records have low business impact.

Prototype

  • One source
  • Occasional extraction
  • Stable template
  • Small dataset

Production workflow

  • Multiple templates
  • Retries
  • Deduplication
  • Validation
  • Monitoring
  • Scheduled delivery
  • Failure alerts
End-to-end website data extraction cleaning validation and delivery pipeline
Production workflows add validation, delivery, and monitoring beyond the first script.
  1. Discovery
  2. Retrieval
  3. Extraction
  4. Normalization
  5. Deduplication
  6. Validation
  7. Delivery
  8. Monitoring

A production workflow may also require:

  • Page discovery, multiple template handling, retries, and duplicate checks
  • Completeness validation, run history, change monitoring, scheduled delivery, and failure alerts

This distinction matters when scraped data feeds pricing, research, sales, inventory, or operational decisions. Read enterprise web scraping considerations.

Should you build or outsource the workflow?

Build internally

Build internally when the source is simple, the scale is limited, and your team can maintain the code.

Consider managed extraction

Consider managed extraction when:

  • Several changing websites are involved and updates must run on a fixed schedule
  • Data must be normalized across sources with API delivery into internal systems
  • Failed or incomplete records create business risk and engineering maintenance is becoming a distraction

Before choosing a provider, ask:

  • How is completeness measured and how are failed pages reported?
  • How are layout changes detected and which fields are validated?
  • Which output formats are supported and who owns monitoring?

Nenodata's live site describes web scraping, JavaScript rendering, data transformation, and structured delivery as current offerings. Specific project feasibility and performance must be confirmed during scoping.

Have a recurring extraction requirement?

Share one public target URL, the fields you need, and your preferred output format. Nenodata can review source feasibility and discuss a representative sample before a recurring workflow is configured.

  1. Public target URL
  2. Required fields
  3. Preferred output format
Request a Data Sample

Website scraping checklist

Before launching a scraper, confirm:

Pre-launch website scraping checklist

  • Required fields are documented.
  • Example URLs have been reviewed.
  • An API or export has been considered.
  • Static versus dynamic behavior has been checked.
  • Pagination has been identified.
  • Expected record counts are known.
  • Missing-value rules are defined.
  • Duplicate rules are defined.
  • Output format is confirmed.
  • Timeouts and error handling are configured.
  • A second run has been compared with the first.
  • Access requirements have been reviewed.
  • Someone owns ongoing maintenance.

Final takeaway

Choose by page behavior

Static → HTTP scraper

JavaScript-dependent → Browser automation

Always validate

Counts, missing fields, duplicates, repeat-run consistency

Evaluate operations

Multiple sources, scheduled updates, structured delivery, monitoring

The best website-scraping method depends on the source, required volume, update frequency, and consequences of missing data.

Use a basic HTTP scraper for suitable static pages. Use browser automation when public content genuinely depends on JavaScript rendering. In every case, validate record counts, missing fields, duplicates, and repeat-run consistency.

When the requirement involves multiple sources, scheduled updates, structured delivery, or ongoing monitoring, evaluate the full maintenance workload rather than only the initial extraction code.

Need help scoping a recurring extraction workflow?

Talk to a Nenodata specialist about your source, schema, and delivery requirements.

  1. Source
  2. Schema
  3. Delivery
Talk to a Web Scraping Expert