Python robotparser: Read robots.txt

Published on: August 20, 2026
Reading time: 4 minutes
HTML code on a screen representing responsible crawling with Python robotparser

The urllib.robotparser module parses robots.txt files and answers whether a user agent may fetch a specific URL. It is an important component of responsible crawlers, indexers, monitors, and automation because it handles Allow, Disallow, crawl delays, suggested request rates, and sitemap declarations.

Respecting robots.txt is not the only responsibility of a crawler. The file does not grant permission, replace terms of service, secure private content, or guarantee that collection is ethical or lawful. This guide covers RobotFileParser, bounded fetching, updates, shared rate limits, failure policy, and origin-aware caching.

What is robots.txt?

A site can publish /robots.txt at the root of an origin, such as https://example.com/robots.txt. The file groups rules by user-agent token.

User-agent: MyCrawler
Disallow: /admin/
Allow: /admin/public-docs/
Crawl-delay: 5
Sitemap: https://example.com/sitemap.xml

Rules apply to a specific scheme, hostname, and port. One subdomain’s file does not automatically control another. Core behavior is standardized by RFC 9309.

Basic RobotFileParser usage

from urllib.robotparser import RobotFileParser

parser = RobotFileParser("https://example.com/robots.txt")
parser.read()

allowed = parser.can_fetch(
    "MyCrawler",
    "https://example.com/articles/python",
)
print(allowed)

read() downloads and parses the file. For explicit timeout, size, status, and TLS handling, fetch the content with an HTTP client and call parse(lines).

The urllib.request guide demonstrates bounded HTTP downloads.

Manual parsing with limits

from urllib.request import Request, urlopen
from urllib.robotparser import RobotFileParser

MAX_ROBOTS_BYTES = 512 * 1024
USER_AGENT = "MyCrawler/1.0 (+https://example.org/bot)"

request = Request(
    "https://example.com/robots.txt",
    headers={"User-Agent": USER_AGENT},
)

with urlopen(request, timeout=10) as response:
    raw = response.read(MAX_ROBOTS_BYTES + 1)

if len(raw) > MAX_ROBOTS_BYTES:
    raise ValueError("robots.txt exceeded the size limit")

text = raw.decode("utf-8", errors="replace")
parser = RobotFileParser()
parser.set_url(request.full_url)
parser.parse(text.splitlines())
parser.modified()

Full crawlers should implement the protocol’s exact limits and encoding rules. A conservative application cap prevents unexpected resource consumption.

Use one consistent user agent

Use the same token when downloading robots.txt, calling can_fetch(), and sending real requests. A descriptive HTTP user agent helps administrators understand traffic and contact the operator.

USER_AGENT_TOKEN = "MyCrawler"
HTTP_USER_AGENT = "MyCrawler/1.0 (+mailto:bot@example.org)"

if not parser.can_fetch(USER_AGENT_TOKEN, target_url):
    raise PermissionError("URL is blocked by robots.txt")

Do not impersonate a browser or a well-known crawler. Do not use * merely because it produces a more permissive decision when a specific group applies.

Validating target URLs

from urllib.parse import urljoin, urlsplit

base = "https://example.com/"
target = urljoin(base, "/products?page=2")
parts = urlsplit(target)

if parts.scheme != "https" or parts.hostname != "example.com":
    raise ValueError("Unexpected target")

if parser.can_fetch(USER_AGENT_TOKEN, target):
    print("Allowed")

The urllib.parse guide explains secure URL joining and validation.

Crawl delay

crawl_delay() returns the suggested number of seconds between requests or None.

delay = parser.crawl_delay(USER_AGENT_TOKEN)
if delay is None:
    delay = 2.0

delay = max(delay, 1.0)

Coordinate the delay across every worker accessing the same origin. Sleeping independently in ten threads can still generate ten times the intended rate.

Request rate

request_rate() returns a tuple containing request count and seconds.

rate = parser.request_rate(USER_AGENT_TOKEN)
if rate:
    interval = rate.seconds / rate.requests
    print(f"average minimum interval: {interval:.2f}s")

Use a shared token bucket or another limiter. Combine request-rate, crawl-delay, HTTP 429, Retry-After, and your own conservative ceiling.

Sitemaps

site_maps() returns sitemap URLs declared in the file.

for sitemap_url in parser.site_maps() or []:
    print(sitemap_url)

Sitemaps can reduce crawling by listing public resources directly. Validate each URL, limit response size and decompression, and apply the same origin and request policies.

Caching and refresh

mtime() reports when rules were fetched, and modified() updates that time.

import time

REFRESH_SECONDS = 6 * 60 * 60

if time.time() - parser.mtime() > REFRESH_SECONDS:
    refresh_robots(parser)

Maintain one cached parser per origin, refresh it periodically, and use synchronization so many workers do not refresh simultaneously.

Failure policy

Define behavior for 404, 401, 403, timeout, malformed content, and 5xx responses. A responsible crawler should usually pause or reduce collection on temporary failure rather than converting every exception into permission.

Use bounded retries with backoff and record the status. A network outage is not evidence that all paths are allowed.

robots.txt is not access control

The rules are public and voluntary. They do not stop a malicious client. Private data requires authentication and authorization on the server. Publishing sensitive paths in robots.txt may even reveal them.

Redirects and origin changes

Whenever a link or redirect changes scheme, hostname, or port, load a separate parser and rate limiter. Never apply rules from example.com to cdn.example.net.

Identification and logging

Use a user agent containing a name, version, and contact URL or email. Record the target URL, robots decision, applied delay, status, byte count, and duration. Avoid logging collected personal data unnecessarily.

Integrating with a crawler

def may_fetch(url: str, cache: dict[str, RobotFileParser]) -> bool:
    origin = origin_from_url(url)
    parser = cache.get(origin)

    if parser is None or is_stale(parser):
        parser = load_robots(origin)
        cache[origin] = parser

    return parser.can_fetch(USER_AGENT_TOKEN, url)

After the decision, the crawler still needs shared concurrency limits, 429 handling, cycle prevention, canonicalization, deduplication, and SSRF protection.

Responsible web scraping

The web scraping guide covers extraction, pagination, and responsible practices. Prefer an official API, feed, or export when one exists.

Common mistakes

Typical errors include downloading robots.txt for every page, using different user agents for parsing and requests, ignoring delays, treating errors as permission, applying one parser to another origin, assuming robots.txt provides authorization, allowing an unlimited file, and running many workers without a shared limiter.

Best practices

Cache one parser per origin, refresh it, identify the crawler truthfully, apply the most conservative rate, validate sitemaps and redirects, cap responses, and pause on uncertainty. Respect privacy, copyright, terms, and administrator requests even when a path is technically allowed.

Conclusion

urllib.robotparser provides a simple interface to robots.txt, but responsible crawling requires origin-aware caching, shared rate limits, conservative failure handling, honest identification, and URL validation. Treat the module as an operational-respect layer, not authorization or legal approval.

Read the official urllib.robotparser documentation and RFC 9309.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Developer configuring a Python HTTPS server and TLS certificate
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: Build a Local HTTPS Server in Python

    Learn Python HTTPSServer for local HTTPS services, TLS certificates, threaded handling, testing, and practical security limits.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Protected files representing secure TAR extraction with Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: Extract TAR Safely

    Learn Python tarfile extraction_filter for safer TAR extraction, path validation, links, permissions, and resource limits.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Python code showing warnings controlled with catch_warnings
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: Capture Python Warnings in Tests

    Learn Python catch_warnings to capture, test, and control warnings with focused filters and safe temporary scopes.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Python code representing persistent pickle references
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: Serialize External References

    Learn Python pickle persistent_id for stable external references, validation, security, performance, and long-term compatibility.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Binary code representing Python buffers and memory views
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: Count Values Without Buffer Copies

    Learn Python memoryview.count to count bytes and values in buffers without copies, with formats, limits, and practical safety.

    Ler mais

    Tempo de leitura: 5 minutos
    06/10/2026
    Python code and type annotations
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: Avoid Circular Imports in Annotations

    Learn Python annotationlib to inspect deferred annotations, avoid circular imports, and build safer runtime tooling.

    Ler mais

    Tempo de leitura: 5 minutos
    06/10/2026