Essential Python Libraries for Web Scraping: A Starter Toolkit
Python is the default language for web scraping, and for good reason. It has a small set of mature libraries that cover every step of the job: fetching pages, parsing HTML, driving a real browser, and cleaning the data you pull out. The hard part when you are starting out is not writing code. It is knowing which library to reach for and why.
This guide walks through the libraries that matter, what each is good at, and how to combine them into a stack you can build on.
How to Think About a Scraping Stack
Every scraping job breaks down into three steps:
- Fetch the page (get the raw HTML or JSON from a server).
- Parse the content (find the values you care about inside that markup).
- Store the result (write it to CSV, JSON, or a database).
Some sites need a fourth step: render. If the data only appears after JavaScript runs, a plain HTTP request returns an empty shell, and you need a browser to execute the page first.
You do not need every library below on day one. Pick one tool per step, get a working scraper, and add more only when a site forces you to.
Fetching Pages: HTTP Clients
requests
requests is the library most people start with. It sends HTTP requests with a clean, readable API and handles sessions, headers, cookies, and redirects for you.
import requests
resp = requests.get("https://example.com", timeout=10)
print(resp.status_code)
print(resp.text[:500])
It is synchronous, which means it fetches one page at a time. That is fine for small jobs and for learning. When you need to scrape thousands of pages, the one-at-a-time model becomes the bottleneck.
httpx
httpx is a modern alternative with almost the same API as requests, plus two things requests lacks: async support and HTTP/2. Async lets you fire off many requests concurrently without threads, which is a big speedup for I/O-bound work like scraping.
import httpx
import asyncio
async def fetch(url, client):
resp = await client.get(url, timeout=10)
return resp.status_code
async def main():
urls = ["https://example.com"] * 20
async with httpx.AsyncClient(http2=True) as client:
results = await asyncio.gather(*(fetch(u, client) for u in urls))
print(results)
asyncio.run(main())
If you are new, start with requests. When speed matters, move to httpx.
curl_cffi
Some sites block requests based on their TLS fingerprint, the signature a client leaves during the HTTPS handshake. Standard Python clients have a fingerprint that anti-bot systems recognize instantly. curl_cffi can impersonate the fingerprint of a real Chrome or Safari browser, which gets you past a surprising number of soft blocks.
from curl_cffi import requests
resp = requests.get("https://example.com", impersonate="chrome")
print(resp.status_code)
Keep this one in your back pocket. You will not need it for friendly sites, but it is the first thing to try when a normal request gets a 403.
Parsing HTML
BeautifulSoup
Once you have HTML, you need to pull values out of it. BeautifulSoup is the friendliest parser in Python. You search the document by tag, class, or CSS selector, and it returns the matching elements.
from bs4 import BeautifulSoup
html = "<div class='price'>$29.99</div>"
soup = BeautifulSoup(html, "html.parser")
print(soup.select_one(".price").text) # $29.99
It is forgiving of broken markup and easy to read, which is why it is the standard first parser to learn.
lxml and parsel
lxml is faster than BeautifulSoup and supports XPath, a query language that is more precise for deeply nested documents. parsel (the parser Scrapy uses under the hood) wraps lxml with a clean API and works well as a standalone tool.
from parsel import Selector
sel = Selector(text="<div class='price'>$29.99</div>")
print(sel.css(".price::text").get()) # CSS selector
print(sel.xpath("//div[@class='price']/text()").get()) # XPath
Learn CSS selectors first. Reach for XPath when you need to match on text content or navigate to a parent element, which CSS cannot do.
Full Frameworks: Scrapy
Scrapy is not a single library, it is a complete framework. It handles requests, parsing, concurrency, retries, rate limiting, and data export out of the box. For a one-off script it is overkill. For a real crawler that visits thousands of pages and follows links, it saves you from reinventing a lot of plumbing.
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = ["https://quotes.toscrape.com"]
def parse(self, response):
for quote in response.css("div.quote"):
yield {
"text": quote.css("span.text::text").get(),
"author": quote.css("small.author::text").get(),
}
next_page = response.css("li.next a::attr(href)").get()
if next_page:
yield response.follow(next_page, self.parse)
Scrapy has a learning curve, so it is not where beginners should start. But once you outgrow single scripts, it is the natural next step.
Browser Automation for JavaScript Sites
When content loads through JavaScript, HTTP clients return an empty page. You need a real browser.
Selenium
Selenium is the veteran of browser automation. It drives Chrome, Firefox, and others through their official drivers. It is widely documented and works everywhere, though it is slower and heavier than newer tools.
Playwright
Playwright is the modern choice. It is faster, has a cleaner API, waits for elements automatically, and ships with browser binaries so setup is painless. It also supports async out of the box.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close()
Rule of thumb: try an HTTP client first. Browsers use far more CPU and memory, so only reach for one when the data genuinely requires JavaScript to appear.
Cleaning and Storing Data
Once you have extracted values, pandas is the standard tool for shaping them. Load your scraped rows into a DataFrame, drop duplicates, fix types, and export to CSV, Excel, or a database with one line.
import pandas as pd
df = pd.DataFrame(rows)
df.drop_duplicates().to_csv("output.csv", index=False)
For simple jobs, Python’s built-in csv and json modules are enough and add no dependencies.
A Recommended Starter Stack
If you want a single stack to learn first, use this:
requeststo fetch pagesBeautifulSoupto parse HTMLpandasto clean and export
That combination handles the majority of static sites. Add httpx when you need speed, playwright when you need JavaScript, and curl_cffi when you hit blocks.
When Sites Fight Back
The libraries above are enough for simple, cooperative sites. The moment you scrape at scale or target a site with anti-bot protection, you run into blocks, CAPTCHAs, rate limits, and pages that return a 200 status but no real content. Rotating proxies, managing browser fingerprints, and solving challenges yourself is a large, ongoing project on its own.
This is the gap ScrapeUnblocker fills, and it ships an official Python library so you do not have to change how you work. Install it and one call returns the fully unblocked HTML, with proxy rotation, browser rendering, and anti-bot handling done for you:
pip install scrapeunblocker
from scrapeunblocker import Client
su = Client(api_key="YOUR_API_KEY")
# Fully rendered, unblocked HTML - hand it straight to BeautifulSoup
html = su.get_page_source("https://example.com")
# Or get parsed structured data directly
result = su.get_parsed("https://www.amazon.com/dp/B08N5WRWNW")
print(result.data["title"], result.data["price"])
You keep your favorite parsing tools. The fetching step just stops failing. See the developer docs for the full API.
FAQ
Which Python library should a beginner learn first?
Start with requests for fetching and BeautifulSoup for parsing. They have the gentlest learning curve and cover most static websites. Add other tools only when a specific site forces you to.
Do I need Scrapy or Playwright to get started? No. Scrapy is worth it once you are crawling thousands of pages and following links. Playwright is only needed when content loads through JavaScript. Both are next steps, not starting points.
Why does my scraper get an empty page even though the site works in my browser? The site almost certainly renders content with JavaScript. A plain HTTP request only gets the initial HTML, before scripts run. Use Playwright or Selenium to execute the page, or a service that renders it for you.
How do I avoid getting blocked while scraping?
Respect rate limits, set a realistic user agent, and slow down. If a site actively blocks you, you will need rotating proxies and fingerprint handling. Tools like curl_cffi help with soft blocks, and a service like ScrapeUnblocker handles the hard cases end to end.
Wrapping Up
You do not need to learn every scraping library at once. Get comfortable with a fetch tool, a parser, and a way to store data, then expand your toolkit as real sites push back. Start small, ship a working scraper, and add the heavier tools only when the job demands them.
Try ScrapeUnblocker free
95%+ success rate · from 0.55€ per 1,000 calls · 500 free requests on signup.