Skip to main content

philiprehberger-web-scraper

Tests PyPI version Last updated

philiprehberger-web-scraper

Lightweight web scraper with rate limiting and CSS selectors.

Installation

pip install philiprehberger-web-scraper

Usage

from philiprehberger_web_scraper import Scraper

scraper = Scraper(rate_limit=2.0, retry_attempts=3)

# Fetch a single page
page = scraper.get("https://example.com")
titles = page.select_all("h2.title")
link = page.select_one("a.next")
all_links = page.links()

# Extract data
for el in page.select_all(".product"):
    print(el.select_one(".name").text)
    print(el.select_one("a").attr("href"))

# Crawl multiple pages
for page in scraper.crawl("https://example.com/blog", max_pages=20):
    for article in page.select_all("article"):
        print(article.select_one("h2").text)

# Export
Scraper.export_csv(data, "output.csv")
Scraper.export_json(data, "output.json")

Retry with Backoff

Transient HTTP errors (429 and 503) are retried automatically with exponential backoff. Configure the number of attempts and base delay:

from philiprehberger_web_scraper import Scraper

scraper = Scraper(retry_attempts=5, retry_delay=2.0)
page = scraper.get("https://example.com/api")

Response Caching

Cache fetched pages to disk so repeated requests for the same URL skip the network entirely:

from philiprehberger_web_scraper import Scraper, ResponseCache

cache = ResponseCache(cache_dir=".scraper_cache")
scraper = Scraper(cache=cache)

page = scraper.get("https://example.com")  # fetches from network
page = scraper.get("https://example.com")  # served from disk cache

cache.clear()  # remove all cached responses

Cache TTL

Expire cached entries after a number of seconds. Stale files are deleted on the next read so the cache directory does not grow unbounded:

from philiprehberger_web_scraper import ResponseCache

cache = ResponseCache(cache_dir=".scraper_cache", ttl=3600)  # 1 hour

Table Extraction

Pull an HTML table into a list of dicts using extract_table(). Use extract_tables() to pull every matching table on the page:

from philiprehberger_web_scraper import Scraper, extract_table, extract_tables

scraper = Scraper()
page = scraper.get("https://example.com/data")

# First matching table
rows = extract_table(page, "table#prices")
# [{"Product": "Widget", "Price": "$9.99"}, ...]

# All tables on the page
all_tables = extract_tables(page, "table")
# [[{...}, ...], [{...}, ...]]

Use follow_links() to crawl paginated content by following a CSS-selected link on each page:

from philiprehberger_web_scraper import Scraper

scraper = Scraper()
for page in scraper.follow_links("https://example.com/page/1", "a.next-page", max_pages=10):
    for item in page.select_all(".result"):
        print(item.text)

Proxy Rotation

Distribute requests across multiple proxies by passing a list of proxy URLs:

from philiprehberger_web_scraper import Scraper

scraper = Scraper(proxies=[
    "http://proxy1:8080",
    "http://proxy2:8080",
    "http://proxy3:8080",
])
page = scraper.get("https://example.com")  # uses proxy1
page = scraper.get("https://example.com/2")  # uses proxy2

Rotating User Agents

Rotate the User-Agent header round-robin across requests:

from philiprehberger_web_scraper import Scraper

scraper = Scraper(user_agents=[
    "Mozilla/5.0 (X11; Linux x86_64) Firefox/130.0",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) Safari/17.5",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/127.0.0.0",
])
page = scraper.get("https://example.com")  # uses agent #1
page = scraper.get("https://example.com/2")  # uses agent #2

Respecting robots.txt

Enable respect_robots to honor each origin's robots.txt. The file is fetched once per origin and cached; disallowed URLs raise RobotsDisallowedError from get() (and are skipped automatically during crawl()/follow_links()):

from philiprehberger_web_scraper import Scraper, RobotsDisallowedError

scraper = Scraper(respect_robots=True)
try:
    page = scraper.get("https://example.com/private")
except RobotsDisallowedError as e:
    print(f"blocked: {e.url}")

Meta Tags

Read <meta> tags (including Open Graph og:* properties) from a page:

from philiprehberger_web_scraper import Scraper

scraper = Scraper()
page = scraper.get("https://example.com")

page.meta("description")   # "Example description"
page.meta("og:title")      # "Example — Open Graph title"
page.meta_tags()           # {"description": "...", "og:title": "...", ...}

Structured Data (JSON-LD)

Parse every <script type="application/ld+json"> block on a page. A block holding a JSON array contributes its items; invalid blocks are skipped:

from philiprehberger_web_scraper import Scraper

page = Scraper().get("https://example.com/product")
for item in page.json_ld():
    if isinstance(item, dict) and item.get("@type") == "Product":
        print(item["name"])

Selecting Text

Get the stripped text of every element matching a selector:

page.select_text("h2.title")  # ["First post", "Second post", ...]

Sitemaps

Read every page URL from an XML sitemap. Sitemap indexes are followed up to max_depth levels, URLs are deduplicated in order, and every fetch goes through get() (rate limiting, retries, robots.txt, caching, proxies, User-Agent rotation). A root document that fails, is malformed, or is not a sitemap raises ValueError; broken nested sitemaps are skipped:

from philiprehberger_web_scraper import Scraper

scraper = Scraper(rate_limit=1.0)
urls = scraper.sitemap_urls("https://example.com/sitemap.xml", max_depth=2)
for page in (scraper.get(u) for u in urls[:10]):
    print(page.title)

API

Function / Class Description
Scraper(rate_limit, retry_attempts, retry_delay, timeout, headers, respect_robots, cache, proxies, user_agents) Web scraper with rate limiting, retry, caching, proxy rotation, User-Agent rotation, and optional robots.txt enforcement
Scraper.get(url) Fetch a single page with retry and optional caching
Scraper.get_json(url) Fetch JSON from a URL
Scraper.sitemap_urls(url, *, max_depth=3) Return <loc> URLs from an XML sitemap, following nested sitemap indexes; raises ValueError for a failed, malformed, or non-sitemap root
Scraper.follow_links(start_url, selector, max_pages) Follow paginated links matching a CSS selector
Scraper.crawl(start_url, max_pages, same_domain, next_selector) Crawl pages starting from a URL
Scraper.export_csv(data, path) Export list of dicts to CSV
Scraper.export_json(data, path, indent) Export data to JSON
Page A fetched web page with select_one(), select_all(), links(), images(), meta(), meta_tags(), json_ld(), select_text(), and title/text properties
Page.meta(name) Return a <meta> tag's content by name or property (e.g. og:title), or None
Page.meta_tags() Return all <meta> tags as a {name/property: content} dict
Page.json_ld() Parse all application/ld+json script blocks into a list; arrays are flattened and invalid blocks skipped
Page.select_text(selector) Return the stripped text of every element matching a CSS selector
Element Wrapper around a parsed element with text, html, attr(), select_one(), select_all()
ResponseCache(cache_dir, ttl=None) Disk-backed response cache with optional TTL; get(), put(), and clear() methods
RobotsDisallowedError Raised by get() when respect_robots blocks a URL disallowed by robots.txt
extract_table(page, selector) Extract the first matching HTML table into a list of dicts
extract_tables(page, selector) Extract every matching HTML table; returns a list of row-dict lists

Development

pip install -e .
python -m pytest tests/ -v

Support

If you find this project useful:

⭐ Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT

Metadata

Release files for philiprehberger-web-scraper 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for philiprehberger-web-scraper 0.5.0
File Size Uploaded Provenance Actions
philiprehberger_web_scraper-0.5.0.tar.gz 198.1 kB Without provenance Details for philiprehberger_web_scraper-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for philiprehberger-web-scraper 0.5.0
File Interpreter ABI Platform Actions
philiprehberger_web_scraper-0.5.0-py3-none-any.whl Python 3 none any Details for philiprehberger_web_scraper-0.5.0-py3-none-any.whl

Total release size: 210.6 kB

Release files / philiprehberger_web_scraper-0.5.0.tar.gz

Download URL philiprehberger_web_scraper-0.5.0.tar.gz
Size 198.1 kB
Tags Source
SHA-256 checksum
How to use checksums
efad676164d1c5b38165e8650ecace5c4d88dd2169ac8032b6e627bf5ab6667d
BLAKE2b-256 checksum
How to use checksums
ea4ede92e2d5b1ef37e0c5683d5dec66907c2c4e879eaed8761a080c04d35b89
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / philiprehberger_web_scraper-0.5.0-py3-none-any.whl

Download URL philiprehberger_web_scraper-0.5.0-py3-none-any.whl
Size 12.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6c951020360d3e31dbfe376b6698c0ae5bfff95bec814c07e13ac54c061e9e55
BLAKE2b-256 checksum
How to use checksums
e4ffd4c6ca62063f2a0d6106d97070fee5e56d9540b40c3013747a31e9ade413
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Security for philiprehberger-web-scraper 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page