robotparser-rs
A faithful Rust port of Python's
urllib.robotparser — parse a
robots.txt file and answer can_fetch / crawl_delay / request_rate / site_maps. Behaviour
is verified against the reference urllib.robotparser module itself across thousands of queries.
The point of this crate is CPython parity, not a fresh interpretation of the spec. It mirrors
what CPython actually does today: a prefix-match, first-match algorithm (no */$ wildcards,
no longest-match precedence), with CPython's exact path normalization, parse state machine, and
user-agent substring matching. If you are porting Python code and need identical decisions, this
matches; a WHATWG/Google-spec parser would not.
Usage
use RobotFileParser;
let mut rp = new;
rp.parse;
assert_eq!;
assert_eq!;
assert_eq!;
Fetching the file is left to you (CPython's read() is a thin urlopen wrapper): retrieve the
bytes however you like, then feed the lines to parse, exactly as
CPython's read() does internally.
Installation
Requires a Rust toolchain with 2024-edition support (Rust 1.85 or newer).
How it matches CPython
- Matching is prefix-based and first-match: a rule applies when the request path
starts_withthe rule's path, and the first applicableAllow/Disallowdecides. An emptyDisallow:means allow-all. - Path normalization round-trips percent-encoding (
quote(unquote(path))) and, within a?query, normalizes each run of non-=&characters — implemented byte-exactly via theurlparse-rsprimitives this crate is built on. - User-agent matching lowercases the agent token (up to the first
/) and tests each entry's agents as substrings;*is the catch-all, used only when no specific entry applies. crawl-delayis read only when the value is all digits;request-rateonly for anint/intvalue;sitemaplines are collected independently of user-agent.
The deliberate non-goals match CPython's own: no Google */$ wildcard expansion, and the
network read() path is left to the caller. (A few Unicode corner cases of str.isdigit() and
str.lower() are not modelled, matching only ASCII there.)
Verification
Faithfulness isn't a claim here — it's checked on every push. CI exercises the crate over a corpus
of robots.txt documents and queries and compares every result against Python's own
urllib.robotparser (CPython 3.13); the build fails on any divergence. The green badge above
means the port currently matches CPython exactly across the whole corpus.
Reproduce it locally (needs Python 3.13 on PATH):
src/bin/diff.rs exposes the crate over a small stdin protocol;
difftest.py drives both it and the reference module and diffs the output line by
line. The current corpus is 21 documents / 4,578 query comparisons.
License
Licensed under the MIT License.