Quick Summary

  • Python is widely regarded as an ideal tool for web scraping due to its readable syntax, powerful library ecosystem (including Requests, BeautifulSoup, Selenium, and Scrapy), data-cleaning capabilities with pandas, and strong community support.
  • For eCommerce scraping, adhere to a website’s Terms of Service (ToS), respect robots.txt directives, avoid collecting Personally Identifiable Information (PII), avoid overloading servers by rate-limiting, and comply with computer access laws such as the CFAA.
  • To scrape the website, start by installing Python, then install dependencies, make HTTP requests, parse static or JavaScript-rendered HTML, handle pagination, and securely store structured data in CSV files, databases, or cloud solutions.
  • Follow ethical practices when extracting or scraping website data to avoid legal issues and violations of the website’s rules.

Web scraping eCommerce websites with Python is the process of automatically extracting publicly accessible product data, such as product names, prices, descriptions, ratings, availability, and URLs, from online stores. Using Python libraries such as Requests, BeautifulSoup, Scrapy, and Selenium, you can build scrapers to collect and organize eCommerce data for market research, price monitoring, product analysis, and other legitimate use cases.

In this complete guide, you’ll learn how to scrape eCommerce websites using Python, from choosing the right scraping approach and setting up your environment to extracting product data, handling dynamic websites, storing scraped information, and managing common scraping challenges.

What is eCommerce Web Scraping?

eCommerce web scraping is the automated process of extracting product and market data from online retail stores, including Amazon, eBay, Walmart, Flipkart, and other platforms, using software scripts or tools. The eCommerce data scraping tool is known as an eCommerce scraper.

What eCommerce Scraping Is Used For

Retailers and brands scrape competitor storefronts for a handful of concrete reasons:

  • Price monitoring: Track a rival’s price on 200 SKUs daily instead of checking by hand.
  • Stock and supply signals: A competitor going out of stock on a product is a demand signal and an opportunity to bid harder on those keywords.
  • MAP enforcement: Brands watch for resellers pricing below their Minimum Advertised Price agreement.
  • Assortment gaps: Compare your catalog against a competitor’s to find categories you do not carry.
  • Trend spotting: New products appearing across several stores in the same week are worth knowing about.

None of those need customer data, which matters for the legal section later.

Why is Python Good for Web Scraping?

Python is one of the best programming languages for web scraping because of its mature ecosystem of specialized scraping libraries, simple syntax that reduces development time, and seamless integration with data science tools. The following are the major benefits of using Python for web scraping:

  • Simple and Readable Syntax: Python’s simple syntax makes scraping scripts easier to build, understand, and maintain.
  • Powerful Scraping Libraries: Tools like BeautifulSoup, Scrapy, and Selenium support a range of scraping needs, from static pages to dynamic websites.
  • Handles Data Processing Easily: Python can clean, transform, filter, and structure scraped data using libraries such as Pandas.
  • Supports Dynamic Websites: Selenium and other browser automation tools can interact with JavaScript-heavy websites and dynamic content.
  • Easy Integration: Python can connect scraping workflows with databases, APIs, cloud services, and data analysis tools.
  • Strong Community Support: A large developer community provides extensive documentation, libraries, and solutions for common scraping challenges.

Ethical and Legal Considerations When Scraping with Python

Scraping eCommerce websites with Python can help collect product prices, descriptions, reviews, availability, and other publicly accessible information. However, the fact that data is publicly visible does not automatically mean it can be collected, stored, or reused without restrictions.

Before scraping an eCommerce website, consider both legal requirements and ethical practices. The rules can vary by country, website, type of data, and intended use, so treat the following as practical guidance rather than legal advice.

Legal Consideration

(1) Terms of Service (ToS)

The enforceability of a website’s Terms of Service (ToS) depends on how a user accepts them. The following are the main types of ToS agreements:

Click-Wrap Agreements

The Click-wrap agreement is an online agreement that requires the user’s consent: “I agree” (to all terms and conditions) before logging in, creating an account, or browsing any website. Courts across the globe consider this a binding contract. If you are logging in to a website to scrape, you are bound by the contract, and violating these terms is considered a breach of contract.

Browser-Wrap Agreements

You will see the hyperlink at the bottom of the page saying, ‘By continuing to use this site, you agree to our Terms.’ To establish legal standing against scraping, the website must prove that the scraper has actual or constructive notice of the term. If a website sends you a formal Cease-and-Desist letter, you now have “actual notice,” and continuing to scrape elevates your legal risk.

(2) Copyright and IP

Reusing copyrighted material or IP (intellectual property) from scraped data can lead to copyright infringement or breach-of-contract lawsuits.

Copyright and IP Rules

  • Factual vs. Creative Data: Facts, raw numbers, and unoriginal listings (such as product prices or stock counts) generally lack copyright protection and are safer to scrape. At the same time, original creative works, including videos, text, images, articles, and paintings, are fully protected under copyright law.
  • Republishing Risk: You can download the data for private analysis or research purposes, which may be fair to use in some cases. However, copying the publicly published and copyrighted content as your own violates intellectual property rights.
  • Database Rights: Many countries enforce database protection rights that prohibit scraping large, structured collections of information, even if individual items inside the database are not strictly copyrightable.

(3) Data Privacy Regulations

Data privacy regulations have shifted the focus from how you obtain data to what type of data you hold.

Data Protection and Privacy Laws

  • Personally Identifiable Information (PII): Scraping names, email addresses, phone numbers, or IP addresses is strictly restricted.
  • Global Regulations: Laws such as the EU’s GDPR and California’s CCPA require a lawful basis or explicit consent for collecting and processing personal data. If you scrape EU citizens’ data, you must comply with GDPR requirements even if your server is located elsewhere.
  • Anonymization: Always strip or anonymize personal identifiers from your scraped datasets unless you have explicit permission to store them.

(4) Computer Fraud and Abuse Act (CFAA)

The Computer Fraud and Abuse Act (CFAA) prohibits unauthorized access of a computer, but scraping of publicly available data is not illegal. However, bypassing security measures and ignoring cease-and-desist warnings is illegal.

What Is Unauthorized Access?

  • The Van Buren Ruling: Under US Supreme Court precedent, you violate the CFAA if you access data you are completely restricted from accessing, such as a password-protected area.
  • Public Access: Accessing public webpages, even if the website owner told you to stop, does not violate the CFAA.

Technical Gate-Bypassing

  • Prohibited Actions: If your Python script uses stolen credentials, spoofs authentication cookies, or bypasses a CAPTCHA specifically to defeat a site’s access controls, this strengthens a case for a CFAA violation.

Ethical Consideration

(1) Robots.txt Adherence 

Adhering to robots.txt is a foundational baseline of scraping ethically. Robots.txt is governed by the Robots Exclusion Protocol, and it’s a voluntary protocol with no technically enforced mechanism. It serves as a clear communication of intent from a website owner regarding how automated bots should interact with their infrastructure. Ignoring it translates directly to bypassing a site owner’s explicit requests, raising critical ethical and civil liability issues.

(2) Transparency

Operate your automated scripts as an accountable digital citizen rather than a hidden, malicious entity that hides its identity through spoofed browser fingerprints and residential proxies. Ethical software design recommends customizing the standard HTTP User-Agent header to include the name, objective, and contact link of your development project, so you can reach out before resorting to a hard IP block.

Once you declare your digital identity and provide a valid admin email in every network request, you prevent minor scripting from gradually evolving into an aggressive malicious attack against the network infrastructure.

(3) Data Minimization

Data minimization in web scraping means collecting only the data you need for your research or project and avoiding unnecessary personal or sensitive data. The following are key ethical principles of data minimization:

  • Limit Data Scope: Extract only necessary data, such as product prices and catalogs, and omit sensitive data, including user names, IDs, and other sensitive information.
  • Respect Privacy Law: Comply with data privacy laws like GDPR, CCPA, DPDPA, and more by omitting PII.
  • Anonymize and Purge: Strip personal details during the parsing phase of Python and delete the sensitive raw file containing personal data that is no longer required.
  • Honor Site Rules: Carefully read robots.txt files and terms of service to know the owner’s boundaries.

(4) Alternative APIs

Using APIs instead of scraping is the most ethical way to collect the data. The following are the reasons for the same:

  • Structured Access: APIs offer you structured data (like JSON), without parsing raw HTML or over-burdening front-end servers.
  • Explicit Rules: Terms of service and authentication give you a clear idea of which types of data you can pull and which are prohibited.
  • Server Respect: Official endpoints prevent accidental denial-of-service attacks or heavy bandwidth drain caused by automated scraping APIs.

Step-by-Step Guide to eCommerce Web Scraping in Python

Read This Before You Write Any Code

Three checks, in this order:

  1. Open /robots.txt on the target domain and read it.
  2. Check whether the site’s Terms of Service prohibit automated access, and whether you had to click “I agree” to reach the pages you want.
  3. Decide what you are collecting. If any field is a person’s name, email, or IP address, stop and reconsider the scope.

The legal detail sits further down, after the code. If you are scraping a site you do not own for anything commercial, read that section before you run anything.

What We Are Building

A scraper that walks a WooCommerce product listing page by page and writes one row per product: name, price, stock status, and product URL. It ends up in a CSV and a SQLite table.

WooCommerce is a useful target for a walkthrough because the markup is standardized across stores. li.product for the card, h2.woocommerce-loop-product__title for the name, span.price for the price. Learn those four selectors, and the same script works against a large share of the stores you will meet.

For a practice target, use a store that exists to be scraped rather than a live retailer. scrapeme.live/shop is a WooCommerce demo built for this purpose. Verify it is still online before you follow along, and swap in any WooCommerce store you own if it is not.

Step 1: Set Up the Project

Install Python from the official downloads page, then create a virtual environment so this project’s packages stay out of your system Python:

python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
python -m pip install requests beautifulsoup4 lxml pandas

lxml is optional but worth installing. It parses several times faster than Python’s built-in parser, which you notice once you are on page 30.

Step 2: Check robots.txt in Code, Not Just in the Browser

Reading robots.txt once by eye is fine for a one-off. If the scraper is going to run on a schedule, have it check for itself, because the file can change:

from urllib.parse import urlparse
from urllib.robotparser import RobotFileParser

USER_AGENT = "PriceMonitor/1.0 (+https://yourdomain.com/bot; [email protected])"


def robots_check(url, user_agent=USER_AGENT):
    """Return (allowed, crawl_delay) for a URL according to the site's robots.txt."""
    parts = urlparse(url)
    rp = RobotFileParser()
    rp.set_url(f"{parts.scheme}://{parts.netloc}/robots.txt")
    rp.read()
    return rp.can_fetch(user_agent, url), rp.crawl_delay(user_agent)


allowed, delay = robots_check("https://scrapeme.live/shop/")
print(allowed, delay)

crawl_delay returns the number of seconds the site asks bots to wait between requests, or None if it does not specify a delay. Use it as your sleep interval instead of guessing. RobotFileParser is in the standard library, so this costs you nothing.

Note the user agent string. It names the project and provides an email address. If your scraper causes a problem, that string is how a site owner contacts you instead of just blocking the IP range.

Step 3: Find Your Selectors in Developer Tools

Before writing the parser, look at the actual markup. Open the listing page, right-click a product name, and choose Inspect.

You are looking for four things:

  • The element that wraps one product card. On WooCommerce this is <li class="product ...">.
  • The element holding the name, usually <h2 class="woocommerce-loop-product__title">.
  • The element holding the price, usually <span class="price">.
  • The link to the product page, usually <a class="woocommerce-LoopProduct-link">.

Check a product that is on sale and one that is out of stock while you are in there. Those two cases have different markup from a plain in-stock product, and they are what break scrapers in week two.

Step 4: Fetch a Page Politely

import requests

session = requests.Session()
session.headers.update({
    "User-Agent": USER_AGENT,
    "Accept-Language": "en-GB,en;q=0.9",
})

response = session.get("https://scrapeme.live/shop/", timeout=20)
response.raise_for_status()
html = response.text

Three things here are deliberate. A Session reuses the TCP connection, which is faster across 30 pages and lighter on the server. timeout=20 stops the script hanging forever on a slow response. raise_for_status() makes a 403 or 500 fail loudly instead of handing you an error page to parse.

Step 5: Parse the Product Cards

import re
from bs4 import BeautifulSoup


def clean_price(price_el):
    """Return the price a shopper actually pays, as a float."""
    if price_el is None:
        return None
    # On a sale item, WooCommerce puts the old price in <del> and the new one
    # in <ins>. Reading the whole element gives you both numbers stuck together,
    # so "£87.00£72.50" becomes 8700.72 if you strip characters blindly.
    sale = price_el.find("ins")
    text = sale.get_text() if sale else price_el.get_text()
    digits = re.sub(r"[^\d.]", "", text.replace(",", ""))
    return float(digits) if digits else None


def parse_products(html):
    soup = BeautifulSoup(html, "lxml")
    rows = []
    for card in soup.select("li.product"):
        name_el = card.select_one("h2.woocommerce-loop-product__title")
        link_el = card.select_one("a.woocommerce-LoopProduct-link")
        rows.append({
            "name": name_el.get_text(strip=True) if name_el else None,
            "price": clean_price(card.select_one("span.price")),
            "in_stock": "outofstock" not in card.get("class", []),
            "url": link_el["href"] if link_el else None,
        })
    return rows

Run that against a listing page, and you get:

{'name': 'Bulbasaur', 'price': 63.0, 'in_stock': True, 'url': '.../product/bulbasaur/'}
{'name': 'Ivysaur', 'price': 72.5, 'in_stock': False, 'url': '.../product/ivysaur/'}
{'name': 'Charmander', 'price': 1842.0, 'in_stock': True, 'url': '.../product/charmander/'}
{'name': 'Price On Request', 'price': None, 'in_stock': True, 'url': '.../product/no-price/'}

Four details worth pointing out, because they are the difference between a demo and something you can leave running:

  • Ivysaur is on sale. The scraper returns 72.50, the price a shopper pays, not 87.00 and not both numbers glued together.
  • Charmander has a thousands separator. Stripping the comma before the regex keeps 1,842.00 from becoming 184200.
  • “Price On Request” has no price element. It returns None rather than crashing the whole run on page 12.
  • Stock comes from the card class, not from scraping the words “out of stock”, so it survives a change of language or theme.

Every select_one is guarded with a None check. That verbosity is the point. A scraper that dies on one odd product at 3 am is worse than one that records a blank field and carries on.

Step 6: Walk the Pagination

WooCommerce paginates as /shop/page/2/, /shop/page/3/, and so on. Rather than reading the page count off the page, keep requesting until a page comes back with no products:

import time

BASE = "https://scrapeme.live/shop/"


def scrape_all(max_pages=50, delay=2.0):
    all_rows = []
    for page in range(1, max_pages + 1):
        url = BASE if page == 1 else f"{BASE}page/{page}/"

        allowed, crawl_delay = robots_check(url)
        if not allowed:
            print(f"robots.txt disallows {url}, stopping")
            break

        response = session.get(url, timeout=20)
        if response.status_code == 404:
            break
        response.raise_for_status()

        rows = parse_products(response.text)
        if not rows:
            break

        all_rows.extend(rows)
        print(f"page {page}: {len(rows)} products")
        time.sleep(crawl_delay or delay)

    return all_rows

max_pages is a safety net. Without it, a site that returns the same page for any page number gives you an infinite loop and a very annoyed site owner. The time.sleep honors the site’s own crawl delay: it publishes one and falls back to two seconds when it does not.

Two seconds per page is slow on purpose. Thirty pages take a minute. That is nothing, and it is the difference between a scraper the site tolerates and one that gets your IP blocked.

Step 7: Store the Results

CSV for a one-off, SQLite when you want to compare today against last week:

import sqlite3
import pandas as pd

rows = scrape_all()
df = pd.DataFrame(rows)
df["scraped_at"] = pd.Timestamp.now("UTC").isoformat()
df.to_csv("products.csv", index=False)

conn = sqlite3.connect("products.db")
conn.execute("""
    CREATE TABLE IF NOT EXISTS products (
        url        TEXT PRIMARY KEY,
        name       TEXT,
        price      REAL,
        in_stock   INTEGER,
        scraped_at TEXT
    )
""")
conn.executemany("""
    INSERT INTO products (url, name, price, in_stock, scraped_at)
    VALUES (:url, :name, :price, :in_stock, :scraped_at)
    ON CONFLICT(url) DO UPDATE SET
        price      = excluded.price,
        in_stock   = excluded.in_stock,
        scraped_at = excluded.scraped_at
""", df.to_dict("records"))
conn.commit()
conn.close()

The product URL is the primary key because it is the only field that stays stable. Names get edited, and prices change by design. ON CONFLICT DO UPDATE means re-running the scraper updates existing rows instead of duplicating them, so you can put this on a daily cron without any cleanup step.

SQLite requires no server, and the entire database is a single file. Move to MySQL or Postgres when several people need to query it at once, not before.

Step 8: When the Products Are Rendered by JavaScript

If response.text contains your product markup, you are done. If it contains an empty <div id=”root”> and a bundle of JavaScript, the products are being rendered in the browser, and Requests will never see them.

Test which one you have before reaching for a browser:

print("li.product" in response.text)

If that prints False while the products are clearly visible in your browser, you need a real browser engine:

python -m pip install playwright
python -m playwright install chromium

from playwright.sync_api import sync_playwright

def fetch_rendered(url):
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page(user_agent=USER_AGENT)
        page.goto(url, wait_until="domcontentloaded")
        page.wait_for_selector("li.product", timeout=15000)
        html = page.content()
        browser.close()
    return html

wait_for_selector is the important line. Waiting for a fixed number of seconds is the most common mistake here, because it either wastes time or fails on a slow day. Waiting for the element you actually need is reliable.

The same parse_products function works on the HTML that comes back, so only the fetching step changes.

One more option before you launch a browser: open the Network tab, filter to Fetch/XHR, and reload. Plenty of storefronts load products from a JSON endpoint you can call directly, which is faster than a browser and gives you clean data with no parsing at all.

Elite logo

Looking to Hire the Best Python Development Companies?

Explore Top Python Development Companies.

Common Failures and What They Mean

What you seeWhat is happeningFix
403 Forbidden
Default Python-requests user agent is blocked

Set a real, identifying user agent

429 Too Many Requests

You are going too fast
Increase the sleep, honor Retry-After

Empty product list, page loads fine in browser

Content is JavaScript-rendered

Test for the selector, then use Playwright

Prices are 100x too large

Sale markup, both prices concatenated

Read the <ins> element only

Works for 20 pages then breaks

An unusual product missing a field

Guard every select_one with a None check

Same products on every page

Site ignores your pagination parameter

Check the real URL pattern in the browser

AttributeError: ‘NoneType’ has no attribute ‘text’

A selector matched nothing

The markup changed, re-inspect in dev tools

Web Scraping Using Python: Best Practices

The following are best practices for web scraping in Python.

(1) Always Respect Robots.txt

Before scraping, check the robots.txt file. It will tell you which data is allowed and which is restricted for automated crawlers.

(2) Use the Right Python Libraries

Different Python libraries fulfill different scraping requirements, so pick the best suited for your project.

  • Requests: For fetching raw HTML pages
  • BeautifulSoup: For parsing and extracting particular elements
  • Selenium: For extracting dynamic sites that rely heavily on JavaScript 
  • Scrapy: For large-scale scraping projects

(3) Handle Pagination and Dynamic Content

Many websites don’t show all the data on a single page, so you have to visit or scroll through the various pages to extract it. Selenium will help you ethically extract data from paginated and dynamic content.

(4) Avoid Getting Blocked

If your scraper sends too many requests quickly, the website might block your IP. To avoid being blocked, you can add delays between requests, use a rotating user agent to mimic different browsers, or consider using a proxy server for large-scale scraping projects.

(5) Structure & Clean Your Data

Raw HTML data is not always clean and structured, so you can use Python’s pandas library to convert it into structured data that you can analyze or extract into CSV or Excel.

(6) Store Data Securely

After scraping your data, make sure you store it securely. You can use MySQL or MongoDB to store it for the long term, or you can use cloud solutions like GoogleSheet to ensure quick analysis. 

Conclusion

Python is one of the best programming languages for scraping structured data. It gives Python developers greater control and flexibility when collecting and analyzing data. You can use this data for market research, sales forecasting, competitive analysis, and more, including product innovation. However, scraping an e-commerce website presents its own legal and ethical challenges. Checking the website’s terms of service, robots.txt file, and the legal and ethical practices discussed earlier will help Python developers scrape structured data without violating any laws.

FAQs

What is the best framework for web scraping with Python?

Each Python framework is used for different purposes, such as:
Scrapy: Best for large-scale, multi-page scrolling
BeautifulSoup: Best for static HTML
Playwright: Best for dynamic, JavaScript-heavy sites
There are more frameworks used for other purposes; hence, we can’t consider a single framework the best.

Which is better for web scraping, Python or JavaScript?

Python is generally better for standard web scraping, data extraction, or large-scale crawling. However, JavaScript is better for scraping dynamic, heavy single-page applications and handling complex browsing interactions.

Which is better, Scrapy or BeautifulSoup?

Neither tool is perfect, because they serve different purposes: Scrapy is for large-scale projects, while BeautifulSoup is for simple ones.

How long does it take to learn web scraping in Python?

Learning web scraping in Python takes a few days to a couple of months, depending on the existing programming language experience you have and the website you want to scrape.

Which Python library is used for web scraping?

There is no specific library we recommend, because the choice of libraries, including BeautifulSoup, Requests, Scrapy, and more, depends on the various project goals.