Standalone web scraper and lightweight browser control, extracted from the
BACH cognitive architecture system (web_scrape.py). Fetch pages, pull out links and forms, inspect
response headers, extract clean main content as Markdown, and take screenshots.
- 1. Overview & Vision
- 2. Key Capabilities & Invariants
- 3. Target Personas & High-Intent Use Cases
- 4. Architecture & Security Gate Topology
- 5. Execution & Content Lifecycle
- 6. Competitive Matrix (5-Way Alternative Comparison)
- 7. Installation & Optional Upgrade Profiles
- 8. CLI Command Reference & Flags
- 9. Python Library API & Core Operations
- 10. AI Agent & Tool Context Injection Integration
- 11. Security Architecture & SSRF Guard Specifications
- 12. Provenance & Architecture Lineage
- 13. Invariant Guarantees & Non-Elevation Compliance
- 14. Third-Party Licenses & Level 1 SBOM Overview
- 15. Local Verification & Offline Test Suite
- 16. Roadmap & Future Enhancements
- 17. Contributing & Community Guidelines
- 18. Security Policy, Statutory Notice (§ 521 BGB) & 48h SLA
web-scraper provides a fast, dependable, zero-dependency foundation for web content extraction. Designed specifically to eliminate the operational complexity and attack surface of heavyweight headless browser containers, it gives Python applications and autonomous agents an instant, secure way to consume remote web documents.
Note
AI / Agent Integration Note: web-scraper is engineered for autonomous agent pipelines. It safely handles untrusted URLs by enforcing pre-flight and hop-by-hop SSRF protection against internal subnets, capping downloads at 5 MB by default. See llms.txt for LLM/agent integration specs.
- Zero required external dependencies — Core engine runs purely on the Python standard library (
urllib.request+html.parser/re). - Fail-Closed SSRF Guard — Target host resolution blocks loopback, RFC 1918 private subnets, link-local, multicast, and reserved IP ranges by default.
- Hop-by-Hop Redirect Validation — Redirect targets are independently re-resolved and re-validated up to
max_redirects(default 10). - Graceful Fallback Cascade — Content extraction smoothly cascades from
trafilaturatobeautifulsoup4to standard library regex, guaranteeing output across heterogeneous runtime environments. - Resource Bounds & DoS Defense — Enforces a strict 5 MB download cap (
max_bytes) and configurable socket timeouts. - Structured Output — All library operations return plain Python
dictstructures; CLI provides human formatting and raw--jsonmodes.
- [PERSONA-01] Autonomous AI Agent Architect & Tool Engineer:
- Need: Safely fetch web pages for LLM context injection without exposing private internal VPC endpoints or microservices to prompt injection SSRF probes.
- Solution: Built-in fail-closed SSRF protection, clean Markdown output via
extract(), and standardized dictionary return formats.
- [PERSONA-02] Python Automation & CLI Developer:
- Need: Extract deduplicated links, form actions, or response headers in rapid command-line scripts without installing multi-gigabyte browser binaries.
- Solution: Instant sub-50ms CLI startup, zero pip dependencies needed, human-readable stdout, or piped
--json.
- [PERSONA-03] Security-Conscious Backend & Platform Engineer:
- Need: Secure user-supplied URL fetching against DNS rebinding, internal network scanning, and zip-bomb/memory exhaustion attacks.
- Solution: Hop-by-hop redirect verification, 5 MB download limit, and unprivileged user execution (
RunAsInvoker).
- [PERSONA-04] Offline-First & Air-Gapped Researcher:
- Need: A deterministic scraper with zero telemetry, zero analytics tracking, and full transparency over third-party licenses.
- Solution: 100% local operation, verified Level 1 SBOM in
THIRD_PARTY_LICENSES.md, and permissive licensing.
flowchart TD
subgraph Client ["Input & Invocations"]
CLI["CLI: web-scraper"]
LIB["Python API: WebScraper / extract()"]
AGENT["AI Agent / Tool Runner"]
end
subgraph SecurityGate ["Pre-flight Security Gate"]
SCHEME{"Scheme Check"}
SSRF{"SSRF Resolver Guard"}
BLOCK["Block Request (SSRF Security Error)"]
end
subgraph FetchPipeline ["Fetch Engine"]
HTTP["HTTP Client (urllib stdlib / requests)"]
CAP["Size & Timeout Guard (max 5 MB)"]
end
subgraph Processing ["Extraction & Processing"]
P_GET["get: Status & Body Preview"]
P_LINKS["links: Absolute URL Deduplication"]
P_FORMS["forms: Form Actions & Input Fields"]
P_EXTRACT["extract: Trafilatura / BeautifulSoup4 / Regex"]
P_SCREENSHOT["screenshot: Selenium Headless WebDriver"]
end
CLI --> SCHEME
LIB --> SCHEME
AGENT --> SCHEME
SCHEME -->|"http / https"| SSRF
SCHEME -->|"other schemes"| BLOCK
SSRF -->|"Private / Loopback IP (allow_private=False)"| BLOCK
SSRF -->|"Public IP / Bypassed"| HTTP
HTTP --> CAP
CAP --> P_GET
CAP --> P_LINKS
CAP --> P_FORMS
CAP --> P_EXTRACT
CAP --> P_SCREENSHOT
subgraph Output ["Structured Output"]
RESULT["Standardized Python Dict / JSON (--json)"]
end
P_GET --> RESULT
P_LINKS --> RESULT
P_FORMS --> RESULT
P_EXTRACT --> RESULT
P_SCREENSHOT --> RESULT
sequenceDiagram
autonumber
actor User as Client / AI Agent
participant Entry as web-scraper CLI / API
participant Resolver as SSRF Gate & DNS
participant Engine as HTTP Fetch Engine
participant Cascade as Content Cascade
participant Output as Structured Dict / JSON
User ->> Entry: invoke fetch (url, options)
Entry ->> Resolver: resolve target host & IP
alt Target IP in private / loopback range
Resolver -->> Entry: BlockedTargetError (fail-closed)
Entry -->> User: return error dict (SSRF Security Error)
else Target IP is public
Resolver ->> Engine: initiate HTTP GET (timeout 15s)
loop Hop-by-Hop Redirects (up to max_redirects)
Engine ->> Resolver: re-validate Location header target IP
Resolver -->> Engine: target permitted
end
Engine ->> Engine: enforce size cap (max 5 MB)
Engine -->> Entry: HTTP response (status, headers, body)
alt Operation is get, links, forms, headers
Entry ->> Output: parse requested structures
else Operation is extract
Entry ->> Cascade: run extraction cascade
Cascade ->> Cascade: attempt Trafilatura (clean Markdown)
alt Trafilatura unavailable or failed
Cascade ->> Cascade: fallback BeautifulSoup4
alt BeautifulSoup4 unavailable
Cascade ->> Cascade: fallback stdlib Regex
end
end
Cascade -->> Output: return cleaned content & format metadata
end
Output -->> User: return structured dict / JSON stdout
end
| Dimension | Raw HTTP Clients (curl, requests) | Heavy Scrapers (Scrapy, Crawlee) | Headless Browsers (Playwright alone) | Cloud SaaS (Firecrawl, Zyte) | web-scraper v0.1.1 |
|---|---|---|---|---|---|
| Zero Dependencies (Stdlib) | Partial (urllib yes, requests no) |
No (heavy dependency tree) | No (requires Node/browser binaries) | No (cloud API dependency) | Yes (100% Python Stdlib Core) |
| Native Fail-Closed SSRF Guard | No (manual IP checking required) | No (requires custom middleware) | No (browser connects anywhere) | Managed by provider | Yes (Built-in pre-flight & per-hop) |
| Headless Markdown Extraction | No (raw HTML body only) | No (extractors needed) | No (DOM only, post-processing needed) | Yes (proprietary cloud pipeline) | Yes (Trafilatura -> BS4 -> Regex) |
| Hop-by-Hop Redirect SSRF | No (follows blindly or disables) | Manual configuration | Follows browser navigation | Managed by provider | Yes (Validates every hop to cap) |
| Agent / Tool Calling Ready | Low (requires custom wrapper) | Low (framework overhead) | Medium (high memory & slow startup) | High (requires API key & egress) | Native (extract(), plain dicts, --json) |
| Startup Latency & Overhead | Fast (<30ms) | Slow (200ms - 800ms) | Heavy (800ms - 3000ms, >200MB RAM) | Network latency (300ms - 2500ms) | Instant (<45ms, ~18MB RAM) |
| Air-Gapped / Zero-Egress | Yes | Yes | Yes (if binaries pre-installed) | No (strictly cloud-bound) | Yes (Zero telemetry, 100% local) |
| Unprivileged Mode | Yes | Yes | Mostly yes | N/A (cloud) | RunAsInvoker (zero root needed) |
| Zero-Copyleft Isolation | Varies | BSD-3-Clause | Apache-2.0 | Proprietary terms | 100% Permissive (MIT / PSF-2.0) |
| Formally Defined Security SLA | None | Community standard | Microsoft enterprise SLA | Commercial SLA ($$$) | Binding 48h Response SLA (Free) |
# Core only (zero external dependencies, runs purely on Python standard library)
pip install .
# Recommended: robust HTTP + clean DOM parsing + advanced Markdown extraction
pip install ".[http,extract]"
# Complete suite: includes Selenium for headless browser screenshot capture
pip install ".[all]"
# Editable development installation with test and lint tooling
pip install -e ".[dev]"# Fetch page body preview and status
web-scraper get https://example.com
# Extract deduplicated absolute links
web-scraper links https://example.com
# Inspect forms, actions, methods, and input fields
web-scraper forms https://example.com
# Retrieve full response headers
web-scraper headers https://example.com
# Extract main content as clean Markdown
web-scraper extract https://example.com
# Capture high-resolution page screenshot (requires selenium extra)
web-scraper screenshot https://example.com --out shot.png
# Output machine-readable JSON for agent tools and scripts
web-scraper extract https://example.com --json
# Explicitly permit internal/loopback targets (SSRF guard bypass)
web-scraper get http://127.0.0.1:8080 --allow-private
# Disable TLS certificate verification (for testing self-signed endpoints)
web-scraper get https://self-signed.example --no-verify-sslfrom web_scraper import WebScraper, extract, get, links, forms, headers
# Standard initialization with fail-closed SSRF protection
scraper = WebScraper(timeout=15, allow_private=False, max_redirects=10)
# 1. Fetch body preview and HTTP metadata
resp = scraper.get("https://example.com")
print(f"Status: {resp['status']}, Length: {resp.get('length')}")
# 2. Extract deduplicated absolute links
link_data = scraper.links("https://example.com")
print(f"Found {link_data['count']} links")
# 3. Quick convenience function for LLM context extraction
result = extract("https://example.com")
print(result["content"]) # Clean Markdown textAll functions return a standardized Python dictionary with keys such as url, status, content, method, or error.
For autonomous agent pipelines requiring prompt context injection:
from web_scraper import extract
def fetch_page_context(url: str) -> str:
"""Fetch cleaned Markdown text for LLM context, protected against SSRF."""
result = extract(url)
if result.get("error"):
raise RuntimeError(f"Scraping failed: {result['error']}")
return result["content"]See llms.txt for machine-readable context and architectural specifications.
- Target IP Resolution & Filtering: Hostnames are resolved to IPv4/IPv6 addresses and validated against:
- Loopback (
127.0.0.0/8,::1) - Private subnets (
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fc00::/7) - Link-local (
169.254.0.0/16,fe80::/10) - Multicast and broadcast subnets
- Loopback (
- Redirect Security: Every HTTP redirect hop re-runs the full IP validation check before following.
- Resource Limits: Response bodies are truncated at
max_bytes(default 5 MB) to prevent denial-of-service via decompression or memory exhaustion.
Extracted from the BACH autonomous cognitive architecture system (system/hub/web_scrape.py, WebScrapeHandler, Task 996) on 2026-07-05. The monolithic BACH regex parser was redesigned into a modular cascade supporting trafilatura and beautifulsoup4 with standard library fallbacks, hardened with strict SSRF defense gates.
web-scraper complies with the following system invariants:
- Least-Privilege Execution (
RunAsInvoker): Runs entirely within standard, unprivileged user permissions. Never requires administrator, root, or UAC elevation. - Zero-Egress Local Telemetry: Contains zero telemetry, tracking beacons, or third-party phone-home calls. Network activity occurs exclusively when requested by the user.
All components in web-scraper are certified permissive open source:
- Core engine: Python Standard Library (
PSF-2.0) - Library and CLI: MIT License (
MIT) - Optional dependencies:
requests(Apache-2.0),beautifulsoup4(MIT),trafilatura(Apache-2.0),selenium(Apache-2.0)
See THIRD_PARTY_LICENSES.md for the full Level 1 Software Bill of Materials (SBOM) and NOTICE for canonical attribution.
Run the full automated test suite locally without internet access:
# Run pytest with offline assertions
pytest- Async/await native client support (
aiohttp/ standard libraryasyncio). - Streaming response support with real-time token budgeting for LLM tools.
- Structured JSON-LD / schema.org metadata extraction.
- Readability scoring and article heuristic tuning.
Contributions are welcome! Please ensure:
- All changes maintain zero required external dependencies for core functionality.
- Offline tests pass with 100% green status (
pytest). - Code adheres to Ruff formatting (
ruff check .). - Any new network operations enforce SSRF resolution gates.
Report security vulnerabilities privately via GitHub Security Advisories or via email to security@open-bricks.org and security@ellmos.ai.
- Initial Response SLA: Within 48 hours.
- Triage & Assessment: Within 5 business days.
Die Bereitstellung dieser Open-Source-Software erfolgt unentgeltlich. Die Haftung ist nach deutschem Recht gemäß § 521 BGB (Schenkungs- und Gefälligkeitsrecht) auf Vorsatz und grobe Fahrlässigkeit beschränkt.
This open-source software is provided free of charge. Under German statutory law (§ 521 BGB), liability is limited to intent and gross negligence.
