w3lib

A dependency-free Python library of browser-accurate URL, HTML, and HTTP utilities built for Scrapy and other web-scraping tools.

Library
PyPI
v2.4.1
418stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
83/100Excellent
Development Activity96
Maintenance72
Community84
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
75/100Good
Architecture78
Code Quality90
Innovation55
Learning Curve75

w3lib is Scrapy’s toolbox of web-related functions that Python’s standard library doesn’t cover accurately: safe URL construction and canonicalization, base URL resolution from raw HTML, HTML entity and tag stripping, raw HTTP header parsing, and charset detection from HTTP headers, meta tags, and BOMs. It has zero runtime dependencies and targets the exact quirks browsers exhibit, not just what RFC 3986 or the HTML spec technically allow.

Under the hood it separates WHATWG Infra and URL primitives (_infra.py, _url.py) from a public API (url.py, html.py, http.py, encoding.py), giving downstream code a stable, well-tested surface for the string-mangling problems that come up constantly when fetching and parsing arbitrary web pages. Scrapy itself depends on it directly for request/response URL handling, encoding detection, and header parsing.

What You Get

  • Browser-accurate URL handling - safe_url_string, canonicalize_url, and url_query_parameter normalize and compare URLs the way browsers actually resolve them, not just per RFC 3986.
  • HTML entity and tag utilities - remove_tags, replace_entities, and get_base_url clean markup and resolve <base href> while correctly skipping comments and <script>/<noscript> blocks.
  • Encoding detection - html_body_declared_encoding and http_content_type_encoding recover a page’s real charset from HTTP headers, <meta> tags, or BOM bytes when declarations conflict or are missing.
  • Raw HTTP header parsing - headers_raw_to_dict and headers_dict_to_raw convert between multi-line raw header blocks and Python dicts for protocols that don’t hand you parsed headers.
  • Zero runtime dependencies - the entire library is pure Python 3.10+ with no third-party packages required at install time.

Common Use Cases

  • Normalizing crawled URLs - a scraper deduplicates and compares URLs found on different pages by running them through canonicalize_url before storing them.
  • Resolving relative links - a crawler extracts every link on a page and resolves it against the page’s actual base URL, including one set via a <base> tag.
  • Decoding fetched pages correctly - an HTTP client that only gets raw bytes back uses w3lib’s encoding detection to pick the right charset before decoding a response body to text.
  • Building an HTTP Basic Auth header - a client constructs the Authorization header value from a username and password without hand-rolling base64 encoding.

Under The Hood

Architecture w3lib is organized as a flat set of purpose-specific modules rather than a class hierarchy: _infra.py and _types.py hold small WHATWG Infra primitives and shared type aliases, _url.py is a private, spec-following reimplementation of URL parsing/quoting internals (RFC 3986 character classes, IDNA encoding, query-string splitting), and url.py re-exports a curated public API built on top of it. html.py and http.py build on url.py/util.py for markup cleanup and header conversion, while encoding.py handles charset detection independently. Every function is stateless and independently callable — there is no dependency injection or shared runtime state — so the practical dependency graph runs one direction: public modules depend on the private WHATWG primitives in _url.py/_infra.py, meaning a spec-compliance change there would ripple through nearly every public URL function.

Tech Stack The library is pure Python 3.10+ with zero runtime dependencies declared in pyproject.toml — only requires-python is set, no [project.dependencies] entries. It builds with hatchling, pulling its version dynamically from w3lib/__init__.py. Development tooling is comprehensive: ruff with an extensive rule set (bugbear, bandit, pyupgrade, pydocstyle, and more) plus pylint for linting, and mypy in strict = true mode for type checking. CI on GitHub Actions runs a dedicated test workflow, a lint/type-check workflow, a CodSpeed performance-regression workflow, and an automated PyPI publish workflow — no database or web framework involved, since this is a foundational dependency consumed by Scrapy and other crawling tools rather than an application itself.

Code Quality Tests mirror the source layout one-to-one (test_url.py, test_html.py, test_http.py, test_encoding.py, test_util.py) plus a dedicated benchmarks/ directory for CodSpeed. test_url.py alone pulls in Hypothesis (given, example, settings, HealthCheck) with a provisional-URL strategy, meaning URL handling is exercised with property-based fuzzing well beyond hand-written examples. Every module uses from __future__ import annotations with full type hints (TypeAlias, overload, NamedTuple), and mypy --strict runs across the whole codebase in CI. Error handling is precise rather than swallowed — for example, url.py registers a dedicated codec error handler (percentencode) for byte-to-Unicode decode failures instead of catching broadly. Naming enforces a clear public/private boundary (underscore-prefixed internals in _url.py/_infra.py versus the re-exported public surface in url.py), and coverage is tracked via Codecov on every push.

What Makes It Unique w3lib doesn’t attempt novel abstractions; its value is spec-fidelity and browser-accurate edge-case handling that naive regex or stdlib-only URL/HTML parsing gets wrong. The <base href> scanner in html.py, for instance, explicitly mimics how a browser ignores <base> tags inside HTML comments or <script>/<noscript> elements — a detail that matters when scraping arbitrary, imperfect real-world HTML rather than well-formed documents. Its internal WHATWG Infra and URL primitives track the same specifications browsers implement, so the library’s differentiator is precision under adversarial input, sourced from years of production use inside Scrapy, rather than a new programming model.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search