0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python の urllib.robotparser は robots.txt の「*」と「$」を読まない ― 9 個のパスで比べた

0
Last updated at Posted at 2026-10-19

先に結果

Disallow: /api/* と書かれた robots.txt を、Python 標準の urllib.robotparser に読ませると、/api/v2/users を「取ってよい」と答えます。

9 個のパスで、標準の urllib.robotparser と、RFC 9309 のとおりに読む小さな関数を比べたところ、4 個で答えが違いました。違ったのは全部、「標準は取ってよいと言うが、RFC 9309 では取ってはいけない」の向きです。

気付いたきっかけ

自分で作っている取得の道具は、取る前に必ず urllib.robotparser で robots.txt を確かめていました。ある日、あるサイトの robots.txt を目で読むと Disallow: /api/* があり、道具はそのサイトの /api/ 以下を「取ってよい」と判断していたことが分かりました。道具の「許可」を信じきって、自分の目で確かめていなかったのが原因です。

試したこと

robots.txt(試しのために作ったもの):

User-agent: *
Disallow: /api/*
Disallow: /*?sort=
Disallow: /private$
Allow: /api/public/

比べたコード(Python 3.14.4):

import urllib.robotparser

rp = urllib.robotparser.RobotFileParser()
rp.parse(ROBOTS.splitlines())
rp.can_fetch("mybot", path)

数えた結果

パス urllib.robotparser RFC 9309 の読み方
/api/v2/users 取る 取らない
/api/ 取る 取らない
/api 取る 取る
/api/public/info 取る 取る
/items?sort=new 取る 取らない
/items 取る 取る
/private 取る 取らない
/private/page 取る 取る
/apix 取る 取る

9 個中 4 個で違いました。標準のほうは、* と $ を「ただの文字」として前方一致で比べるので、/api/* も /private$ も、どのパスにも当たりません。この試しでは、標準は 9 個すべてに「取る」と答えました。

RFC 9309 には何と書いてあるか

  • 2.2.3 Special Characters:* は「0 文字以上の任意の文字」、$ は「パターンの終わり」。クローラーはこの 2 つを扱わなければならない(MUST)。
  • 2.2.2:当たったルールが複数あるときは、いちばん長い(オクテット数が多い)ものを使う。allow と disallow が同じ長さなら allow を使うのがよい(SHOULD)。

直し方(RFC 9309 のとおりに読む小さな関数)

import re

def rule_regex(pattern):
    anchored = pattern.endswith("$")
    body = re.escape(pattern[:-1] if anchored else pattern).replace(r"\*", ".*")
    return re.compile(body + ("$" if anchored else ""))

def allowed(rules, path):
    """rules: [(allow: bool, pattern), ...] 自分の User-agent に当たるグループのルール"""
    best = None  # (長さ, allow):いちばん長いものが勝つ。同じ長さなら allow が勝つ
    for allow, pat in rules:
        if rule_regex(pat).match(path):
            cand = (len(pat), allow)
            if best is None or cand > best:
                best = cand
    return True if best is None else best[1]

上の表の「RFC 9309 の読み方」の列は、この関数(と、User-agent ごとにルールを分ける部分)で出したものです。

分かったこと

  • 標準ライブラリの urllib.robotparser だけで robots.txt を守っているつもりだと、* や $ を使ったルールを素通りします。大きなサイトの robots.txt には、/api/* や /*? のような書き方がよくあります。
  • 道具が「取ってよい」と言っても、取る前に一度は robots.txt を自分の目で読む。今回はそれで気付きました。
  • 直したあとは、同じ 9 個で上の表の右の列どおりに答えることを確かめてから、取得の道具に入れました。

試していないこと

  • urllib.robotparser 以外の Python のライブラリ(Protego など)との比べ。
  • 文字のエンコード(%2F など)が混ざるパスの扱い。この関数は、パーセントエンコードの違いをそろえていません。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?