bigywater.tech
Scrapy · 30

LinkExtractor:链接是怎么被找到并过滤的

LinkExtractor 是怎么把页面里的链接找出来并过滤的? 怎么生效、重复怎么去、哪些扩展名默认被丢掉、allow 匹配的是什么、rel=nofollow 管不管?这篇用一个精心准备的页面把每个参数跑一遍。

13 个
默认 LinkExtractor 在这个页面上提取到的链接数(tags="*"、attrs="*" 时是 21 个)
91 个
IGNORED_EXTENSIONS 默认过滤的扩展名个数
2.17.0
deny_tags、deny_attrs 加入的版本(源码文档注明)

提取与解析

实测 · 本机执行输出
  默认:13 个
      http://site.test/docs/intro.html        text='Intro'               nofollow=False
      http://site.test/abs/path               text='Abs'                 nofollow=False
      http://other.test/x                     text='Other site'          nofollow=False
      http://site.test/docs/page?b=2&a=1      text='query'               nofollow=False
      http://site.test/docs/page?a=1&b=2      text='query2'              nofollow=False
      http://site.test/docs/#frag             text='fragment only'       nofollow=False
      http://site.test/nofollow               text='nf'                  nofollow=True
      http://site.test/spaces                 text='with spaces'         nofollow=False
      http://site.test/tel                    text='onclick'             nofollow=False
      http://site.test/area                   text=''                    nofollow=False
      http://site.test/in-nav                 text='in nav'              nofollow=False
      http://site.test/ad                     text='ad link'             nofollow=False
      http://site.test/x                      text='nested text'         nofollow=False
  没有出现在结果里的 href/src:img src=/pic.jpg、iframe src=/frame、link href=/style.css(tags 默认只有 a、area)

默认只看 <a> 和 <area> 的 href,用 <base> 或响应 URL 做基准把相对链接变成绝对链接,默认去重并过滤 mailto、javascript 和黑名单扩展名。

过滤

实测 · 本机执行输出
  allow=r"/abs|/nofollow":2 个
      http://site.test/abs/path               text='Abs'                 nofollow=False
      http://site.test/nofollow               text='nf'                  nofollow=True
  deny=r"/(abs|ad|x)$":10 个
      http://site.test/docs/intro.html        text='Intro'               nofollow=False
      http://site.test/abs/path               text='Abs'                 nofollow=False
      http://site.test/docs/page?b=2&a=1      text='query'               nofollow=False
      http://site.test/docs/page?a=1&b=2      text='query2'              nofollow=False
      http://site.test/docs/#frag             text='fragment only'       nofollow=False
      http://site.test/nofollow               text='nf'                  nofollow=True
      http://site.test/spaces                 text='with spaces'         nofollow=False
      http://site.test/tel                    text='onclick'             nofollow=False
      http://site.test/area                   text=''                    nofollow=False
      http://site.test/in-nav                 text='in nav'              nofollow=False
  allow_domains="other.test":1 个
      http://other.test/x                     text='Other site'          nofollow=False
  deny_domains="other.test"(其余照常):3 个
      http://site.test/docs/intro.html        text='Intro'               nofollow=False
      http://site.test/abs/path               text='Abs'                 nofollow=False
      http://site.test/docs/page?b=2&a=1      text='query'               nofollow=False
  deny_domains="other.test" 时总数: 12 (默认是 13 )

allow / deny 是对完整绝对 URL 的正则;allow_domains / deny_domains 按域名。

区域与标签

实测 · 本机执行输出
  restrict_css="#nav":1 个
      http://site.test/in-nav                 text='in nav'              nofollow=False
  restrict_xpaths="//div[@class='ad']":1 个
      http://site.test/ad                     text='ad link'             nofollow=False
  restrict_text=r"^nested|^Intro$":2 个
      http://site.test/docs/intro.html        text='Intro'               nofollow=False
      http://site.test/x                      text='nested text'         nofollow=False
  tags=("a","area","iframe","link"), attrs=("href","src")(只看新增的几个):2 个
      http://site.test/area                   text=''                    nofollow=False
      http://site.test/frame                  text=''                    nofollow=False
  tags="*", attrs="*"(所有标签所有属性):1 个
      http://site.test/frame                  text=''                    nofollow=False
  tags="*", attrs="*" 总数: 21
  tags="*", attrs="src", deny_extensions=[]:2 个
      http://site.test/frame                  text=''                    nofollow=False
      http://site.test/pic.jpg                text=''                    nofollow=False
  tags="*", deny_tags="iframe", attrs="src", deny_extensions=[]:1 个
      http://site.test/pic.jpg                text=''                    nofollow=False

restrict_css、restrict_xpaths 限定在页面的某个区域里找;tags、attrs 扩展要看的标签和属性,支持 "*" 通配以及 deny_tags / deny_attrs(后两个在源码文档里标注为 2.17.0 加入)。

规范化

实测 · 本机执行输出
  unique=True(默认): 13 个;unique=False: 14 个
  query 两个链接(page?b=2&a=1 / page?a=1&b=2)默认 unique:2 个
      http://site.test/docs/page?b=2&a=1      text='query'               nofollow=False
      http://site.test/docs/page?a=1&b=2      text='query2'              nofollow=False
  canonicalize=True:1 个
      http://site.test/docs/page?a=1&b=2      text='query'               nofollow=False
  process_value 用于 attrs=("onclick",):1 个
      http://site.test/from-onclick           text='onclick'             nofollow=False

canonicalize=True 把查询参数排序等规范化后再去重;process_value 可以改写属性值。

扩展名

实测 · 本机执行输出
  IGNORED_EXTENSIONS 一共 91 个,前 12 个: ['7z', '7zip', 'bz2', 'rar', 'tar', 'tar.gz', 'xz', 'zip', 'mng', 'pct', 'bmp', 'gif']
  deny_extensions=[](pdf、png 通过):2 个
      http://site.test/docs/file.pdf          text='PDF file'            nofollow=False
      http://site.test/docs/img.png           text='PNG'                 nofollow=False
  deny_extensions=["pdf"](只过滤 pdf):1 个
      http://site.test/docs/img.png           text='PNG'                 nofollow=False

默认丢掉 91 种扩展名(压缩包、图片、音视频、文档等);deny_extensions 可以自定义。

细节

实测 · 本机执行输出
  /nofollow 的 nofollow = [True] (链接仍被提取)
  href="#frag" 的链接是否被提取: True | href 以 mailto: 开头: False | javascript:: False
  <area href="/area"> 是否被提取: True
  嵌套文本 <a><b>nested</b> text</a> 的 text = ['nested text']
  Link 对象的字段: ['fragment', 'nofollow', 'text', 'url']

nofollow 只记录不过滤,片段链接会保留,文字取嵌套标签的全部文本。

分步演示

链接提取
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。页面是手写的 HTML(含 、重复链接、pdf/png、mailto/javascript、rel=nofollow、area、iframe、img、两个 div 区域);直接构造 HtmlResponse 后调用 extract_links,不经过网络
    • 没有验证:LinkExtractor 在 CrawlSpider 规则里与 process_links 的配合(见下一篇);很大的页面上的耗时;frame/iframe 里内容的抓取; 之外的 data-href 等自定义属性的各种写法;XML/Atom 响应上的行为;allow 与 restrict_text 同时设置时的求值顺序(逻辑上是并集还是交集,只单独测了每个参数)
    京ICP备2026067502号-1京公网安备11010502063487号