bigywater.tech
Scrapy · 26

OffsiteMiddleware:allowed_domains 的匹配规则

allowed_domains 到底怎么匹配?子域名算不算?写错格式会怎样?被过滤的请求会通知谁?这篇既直接对着中间件做匹配表,也跑了真实抓取看 stats 与日志。

0 / 11
allowed_domains 写成 example.org:8080 或 http://example.org 时能匹配上的 URL 数(11 个里 0 个)
静默
被站外过滤的请求不会调用回调,也不会调用 errback
dont_filter
默认 start_urls 的请求自带,所以不受站外过滤

匹配规则

实测 · 本机执行输出
  allowed_domains=['example.org']                     ✓ ✓ ✓ ✓ ✓ ✗ ✗ ✗ ✓ ✗ ✗
  allowed_domains=['www.example.org']                 ✗ ✓ ✓ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗
  allowed_domains=['example.org', 'other.com']        ✓ ✓ ✓ ✓ ✓ ✗ ✗ ✗ ✓ ✗ ✗
  allowed_domains=['example.org:8080']                ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗
  allowed_domains=['http://example.org']              ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗
  allowed_domains=['127.0.0.1']                       ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✓
  allowed_domains=[]                                  ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓
  (下面编号对应的 URL)                                      1 2 3 4 5 6 7 8 9 0 1
     1. http://example.org/a
     2. http://www.example.org/a
     3. http://bob.www.example.org/a
     4. http://EXAMPLE.ORG/a
     5. http://example.org:8080/a
     6. http://notexample.org/a
     7. http://example.org.evil.com/a
     8. http://example.orgx/a
     9. http://api.example.org/a
    10. http://[::1]/a
    11. http://127.0.0.1:9000/a

主机名必须等于 allowed_domains 的某一项,或者是它的子域名;大小写不敏感(主机名先小写),端口和协议被忽略,所以带端口或协议的写法永远匹配不上。

被过滤的请求

实测 · 本机执行输出
  回调被调用的页面:['/', 'on'];服务器收到 ['/', '/on'];offsite stats {'domains': 1, 'filtered': 4}
  日志:[scrapy.downloadermiddlewares.offsite] DEBUG: Filtered offsite request to 'localhost': <GET http://localhost:9960/off1>

OffsiteMiddleware 在请求进入调度器时就抛 IgnoreRequest,请求不会发出,也没有回调,stats 里记 offsite/filtered 和 offsite/domains。

绕过

实测 · 本机执行输出
  meta allow_offsite = True   服务器收到 ['/', '/off2', '/on', '/off1'];offsite stats {}
  dont_filter = True          服务器收到 ['/', '/off2', '/on', '/off1'];offsite stats {}
  不设 allowed_domains          服务器收到 ['/', '/off2', '/on', '/off1'];offsite stats {}

请求 meta 里 allow_offsite = True 或 dont_filter = True 就不受限制;不设 allowed_domains 则全部放行。

起始请求与重定向

实测 · 本机执行输出
  回调/errback 被调用的次数:0(被过滤的请求既没有回调也没有 errback,静默丢弃)
  服务器收到 ['/to-local'];offsite stats {'domains': 1, 'filtered': 2}

手写的起始请求和重定向产生的请求同样受限;默认的 start_urls 请求自带 dont_filter = True,不受限。

start_urls

实测 · 本机执行输出
  start_urls 指向站外 localhost:回调 ['via-start-urls'];服务器收到 ['/via-start-urls'];offsite stats {}

起始 URL 想受 allowed_domains 约束,就在 start() 里自己 yield 不带 dont_filter 的 Request。

分步演示

站外过滤
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Scrapy 2.19.0 / Twisted 26.4.0 / lxml 6.1.3 / parsel 1.12.1(临时虚拟环境);所有请求只发给本机自己起的测试服务器(127.0.0.1,端口 9960 以上,不访问任何真实网站);服务器可以按实验需要制造延迟、重定向、压缩、断连等行为,并记录收到的原始请求;每个实验在临时目录里新建 Scrapy 项目或单文件 spider,命令输出来自真实的 scrapy 进程;计时用 time.perf_counter,数字每次运行会有小幅抖动。匹配表直接构造 OffsiteMiddleware 和 Spider 判断 11 个 URL;真实抓取用 127.0.0.1(站内)与 localhost(站外)当两个域名
    • 没有验证:IDN(中文)域名的匹配;子域名过滤时 www 前缀的特殊处理(没有特殊处理,表里只有 www.example.org 是自己写的);自定义 should_follow 子类;spider 运行期间修改 allowed_domains 的生效时机(源码里 _update_host_regex 会在下次判断时更新,没有实测);与 CrawlSpider 的 LinkExtractor allow_domains 的关系(见后面的文章)
    京ICP备2026067502号-1京公网安备11010502063487号