首页
/ Scrapling 元素选取完全指南:CSS/XPath 选择器、文本匹配、find_similar 与 Filters 搜索

Scrapling 元素选取完全指南:CSS/XPath 选择器、文本匹配、find_similar 与 Filters 搜索

2026-09-06 09:17:29作者:廉彬冶Miranda

本文系统讲解 Scrapling 框架中查找与提取 HTML 元素的全部核心手段:CSS3/XPath 选择器、按文本内容与正则匹配元素(find_by_text/find_by_regex)、Scrapling 标志性的相似元素查找 find_similar()、以及灵感来自 BeautifulSoup 的 find/find_all 过滤器搜索。读完本文,你将掌握从静态 HTML 到动态抓取结果(Response 对象)中提取任意数据的完整能力,并理解每种手段在 scrapling/parser.py 中的底层实现机制,能够根据实际页面结构选择最高效的选取策略。

五种元素查找方式与适用边界

Scrapling 目前仅支持解析 HTML 页面,不支持 XML feed。这是官方文档 docs/parsing/selection.md 明确说明的设计决策:自适应(adaptive)功能无法在 XML 上工作。文档同时表示该限制"可能很快改变",因此以当前仓库版本为准理解这一前提。

在 Scrapling 中,查找元素共有五种主要方式:

  1. CSS3 选择器css 方法)
  2. XPath 选择器xpath 方法)
  3. 基于过滤器/条件的查找find/find_all 方法)
  4. 按文本内容包含关系查找find_by_text 方法)
  5. 按正则表达式匹配查找find_by_regex 方法)

此外还有一个间接但极有特色的能力:查找与已知元素相似的元素find_similar),这是 Scrapling 相比传统解析库的差异化特性之一。

官方文档给初学者的一条实用建议:如果你刚接触 Web Scraping、几乎没有编写选择器的经验,建议直接跳到 Filters 搜索 一节的 find/find_all 方法上手,它比手写选择器更直观。

CSS3 选择器:css 方法

支持范围与两个非标准伪元素

Scrapling 实现了 W3C CSS3 选择器规范(REC-css3-selectors)。CSS 选择器的支持来自 cssselect 库,哪些选择器与伪函数/伪元素可用,以 cssselect 的文档为准。Scrapling 在此之上额外实现了两个非标准伪元素,这也是从 Scrapy/Parsel 迁移用户的熟悉用法:

  • ::text —— 选取文本节点
  • ::attr(name) —— 选取属性的值name 为属性名)

官方文档特别指出:如果你来自 Scrapy/Parsel,这里的逻辑与那里完全一致,无需学习一套陌生的选择逻辑。

选取元素时使用 css 方法,它返回 Selectors 对象;用 [0] 取第一个元素,对文本/属性伪选择器则用 .get() / .getall() 提取文本值。

源码视角:css 的本质是 CSS 转 XPath

从源码实现看,Selector.css() 定义在 scrapling/parser.py。它的执行路径比表面更值得注意:

  1. 选择器先经过 _css_to_xpath(selector) 转换为 XPath 表达式,再调用 self.xpath(...) 完成实际匹配——即 CSS 与 XPath 两条入口在底层汇合于 lxml;
  2. 若选择器中包含逗号分隔的组合选择器(如 .a, .b)且启用了自适应功能,会先拆分(split_selectors)、逐段规范化(.canonical())后分别匹配再合并,保证 auto_save 能正确保存组合选择器的数据;
  3. 语法非法的选择器不会静默失败,而是抛出 SelectorSyntaxError,错误信息中带原始选择器文本,便于排查。

css/xpath 方法还带有若干与自适应功能相关的参数(identifieradaptiveauto_savepercentage,默认 percentage=40),官方文档将其留到 adaptive 专页 详述,此处只需知道:不启用 adaptive 时,adaptive/auto_save 参数会被忽略并打印警告(见 scrapling/parser.py 中的告警逻辑)。

XPath 选择器:xpath 方法

XPath 是选取 XML 文档节点的语言,同样可用于 HTML。Scrapling 通过 lxml 直接提供 XPath 支持,xpath 方法的用法逻辑与 css 相同:返回 Selectors,支持 [0] 取首元素、.get() 提取文本。

与 Scrapy/Parsel 的一个重要差异:Scrapling 没有实现 XPath 扩展函数 has-class,取而代之的是元素对象上的 has_class 方法(实现见 scrapling/parser.py,内部直接判断 class_name in self._root.classes)。

另一个在 scrapling/parser.py 中可见的能力:xpath 方法的 **kwargs 会作为 XPath 变量传入表达式,即可以用 XPath 变量参数化查询,这在 Scrapy 中是没有的。

选择器实战示例

以下是官方文档给出的 CSS 与 XPath 对照示例(page 为任意 Selector/Response 对象):

选取所有 class 为 product 的元素:

products = page.css('.product')
products = page.xpath('//*[@class="product"]')

注意:XPath 版本在元素还有其它 class 时不够精确;按 class 选取时始终建议优先使用 CSS

选取第一个 class 为 product 的元素:

product = page.css('.product')[0]
product = page.xpath('//*[@class="product"]')[0]

获取第一个 h1 标签的文本(两种等价写法):

title = page.css('h1::text').get()
title = page.xpath('//h1//text()').get()
title = page.css('h1')[0].text
title = page.xpath('//h1')[0].text

获取第一个 a 标签的 href 属性:

link = page.css('a::attr(href)').get()
link = page.xpath('//a/@href').get()

选取 class 为 product 的元素下、包含 Phoneh1 的文本:

title = page.css('.product h1:contains("Phone")::text').get()
title = page.xpath('//*[@class="product"]//h1[contains(text(),"Phone")]/text()').get()

选择器可以任意嵌套和链式调用,只要中间有返回结果:

page.css('.product')[0].css('h1:contains("Phone")::text').get()
page.xpath('//*[@class="product"]')[0].xpath('//h1[contains(text(),"Phone")]/text()').get()
page.xpath('//*[@class="product"]')[0].css('h1:contains("Phone")::text').get()

再例如,选取所有 href 属性中含 image 的链接,并逐条输出:

links = page.css('a[href*="image"]')
links = page.xpath('//a[contains(@href, "image")]')
for index, link in enumerate(links):
    link_value = link.attrib['href']  # Cleaner than link.css('::attr(href)').get()
    link_text = link.text
    print(f'Link number {index} points to this url {link_value} with text content as "{link_text}"')

按文本内容选取:find_by_textfind_by_regex

Scrapling 提供两种基于元素直接文本内容的选取方式:

  1. 直接文本包含指定文本的元素 —— find_by_text,带多种选项
  2. 直接文本匹配指定正则模式的元素 —— find_by_regex,带多种选项

会用正则的话,find_by_regex 能做的 find_by_text 也都能做;官方提供双份接口是为了让两类用户都有易用入口。find_by_text 的第一个参数是文本,find_by_regex 的第一个参数是正则模式。两个方法共享以下参数:

参数 说明
first_match 默认 True,返回找到的第一个结果(Selector);设为 False 则返回 Selectors(列表)
case_sensitive 默认 False;设为 True 时字母大小写参与比较
clean_match 默认 True;匹配前将所有空白与连续空格压缩为单个空格

find_by_text 还有一个独有参数 partial:默认情况下 Scrapling 要求元素的文本完全等于你传入的文本,启用 partial=True 后则改为"包含即命中"。

一个在 scrapling/parser.py 源码中可见的细节:find_by_textfind_by_regex 都使用了 @overload 做返回类型收窄——first_match=True 时类型注解为 Selectorfirst_match=False 时为 Selectors,静态检查工具可以据此给出精确提示。另外 find_by_regex 的第一个参数既可以是普通字符串,也可以是 re.compile(...) 编译后的 pattern,Scrapling 会自动识别输入类型(实现中直接复用 TextHandler.re(..., check_match=True),见 scrapling/parser.py)。

查找相似元素:find_similar

这是 Scrapling 最具代表性的新特性之一。其灵感来自 AutoScraper 库,但关键区别是:在 Scrapling 中,它对任何方式找到的元素都可用。典型用法是先通过文本找到某个产品,再用它找出同容器里的其它产品。

工作机制分三步:

  1. 找出页面中所有与该元素 DOM 树深度相同的元素;
  2. 逐一检查,淘汰标签名、父标签名、祖父标签名不一致的元素;
  3. 前两步已保证约 99% 的准确率,最后一步用模糊匹配淘汰属性差异过大的元素。这一步由一个百分比阈值控制,官方建议除非默认设置达不到预期,否则不要调它。

方法参数如下:

  • similarity_threshold:第 3 步的属性比较百分比,默认 0.2(即两元素标签属性至少 20% 相似)。设为 0 可关闭这一步,但官方建议先弄清其它参数的作用再这么做。
  • ignore_attributes:最后一步匹配时忽略的属性名列表,默认 ('href', 'src')——因为 URL 在不同元素间变化剧烈,不可靠。
  • match_text:默认 False;设为 True 时元素文本内容也参与匹配计算。官方不推荐在常规场景使用,视具体页面而定。

从源码实现(scrapling/parser.py)可以看到三步算法的精确落点:先计算当前元素深度 current_depth = len(list(root.iterancestors())),再用 XPath //{祖父标签}/{父标签}/{标签}[count(ancestor::*) = {current_depth}] 一次性取出所有同深度、同三级标签路径的候选元素,随后逐个调用 __are_alike 做属性相似度比较。相似度打分在 scrapling/parser.py__calculate_similarity_score 中完成:标签同名计 1 分、文本用 SequenceMatcher.ratio() 计分、属性字典做 diff 计分,并额外对 classidhrefsrc 四个关键属性单独做相似度检测,以应对页面结构性大改的情况。一个边界情况也写在源码注释里:当前元素与候选元素都没有属性时,视为 100% 匹配。

官方文档还指出:若结果是 19 而非 20 个,是因为当前元素本身不包含在结果里

选取示例:文本、正则与相似元素

以下示例使用 Fetcher 类获取真实页面(该类的完整用法将在 fetchers 文档中详述):

from scrapling.fetchers import Fetcher
page = Fetcher.get('https://books.toscrape.com/index.html')

查找文本完全匹配的第一个元素:

>>> page.find_by_text('Tipping the Velvet')
<data='<a href="catalogue/tipping-the-velvet_99...' parent='<h3><a href="catalogue/tipping-the-velve...'>

结合 page.urljoin 把相对 href 拼成完整 URL(urljoin 实现见 scrapling/parser.py):

>>> page.find_by_text('Tipping the Velvet').attrib['href']
'catalogue/tipping-the-velvet_999/index.html'
>>> page.urljoin(page.find_by_text('Tipping the Velvet').attrib['href'])
'https://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html'

取全部匹配(注意返回的是列表):

>>> page.find_by_text('Tipping the Velvet', first_match=False)
[<data='<a href="catalogue/tipping-the-velvet_99...' parent='<h3><a href="catalogue/tipping-the-velve...'>]

取所有包含单词 the 的元素(部分匹配):

>>> results = page.find_by_text('the', partial=True, first_match=False)
>>> [i.text for i in results]
['A Light in the ...',
 'Tipping the Velvet',
 'The Requiem Red',
 'The Dirty Little Secrets ...',
 'The Coming Woman: A ...',
 'The Boys in the ...',
 'The Black Maria',
 'Mesaerion: The Best Science ...',
 "It's Only the Himalayas"]

默认搜索是不区分大小写的,所以结果里既有小写 the 也有 The;加 case_sensitive=True 只保留小写:

>>> results = page.find_by_text('the', partial=True, first_match=False, case_sensitive=True)
>>> [i.text for i in results]
['A Light in the ...',
 'Tipping the Velvet',
 'The Boys in the ...',
 "It's Only the Himalayas"]

取第一个文本匹配价格正则的元素:

>>> page.find_by_regex(r'£[\d\.]+')
<data='<p class="price_color">£51.77</p>' parent='<div class="product_price"> <p class="pr...'>
>>> page.find_by_regex(r'£[\d\.]+').text
'£51.77'

传入编译后的正则效果完全相同,Scrapling 会自动识别输入类型:

>>> import re
>>> regex = re.compile(r'£[\d\.]+')
>>> page.find_by_regex(regex)
<data='<p class="price_color">£51.77</p>' parent='<div class="product_price"> <p class="pr...'>
>>> page.find_by_regex(regex).text
'£51.77'

取所有匹配正则的元素:

>>> page.find_by_regex(r'£[\d\.]+', first_match=False)
[<data='<p class="price_color">£51.77</p>' parent='<div class="product_price"> <p class="pr...'>,
 <data='<p class="price_color">£53.74</p>' parent='<div class="product_price"> <p class="pr...'>,
 <data='<p class="price_color">£50.10</p>' parent='<div class="product_price"> <p class="pr...'>,
 <data='<p class="price_color">£47.82</p>' parent='<div class="product_price"> <p class="pr...'>,
 ...]

查找与当前元素在位置和属性上都相似的元素,匹配时忽略 title 属性:

>>> element = page.find_by_text('Tipping the Velvet')
>>> element.find_similar(ignore_attributes=['title'])
[<data='<a href="catalogue/a-light-in-the-attic_...' parent='<h3><a href="catalogue/a-light-in-the-at...'>,
 <data='<a href="catalogue/soumission_998/index....' parent='<h3><a href="catalogue/soumission_998/in...'>,
 <data='<a href="catalogue/sharp-objects_997/ind...' parent='<h3><a href="catalogue/sharp-objects_997...'>,
...]

注意元素数量是 19 而不是 20,因为当前元素本身不计入结果:

>>> len(element.find_similar(ignore_attributes=['title']))
19

从所有相似元素中提取 href 属性:

>>> [
    element.attrib['href']
    for element in element.find_similar(ignore_attributes=['title'])
]
['catalogue/a-light-in-the-attic_1000/index.html',
 'catalogue/soumission_998/index.html',
 'catalogue/sharp-objects_997/index.html',
 ...]

增加一点复杂度:以该元素为起点,提取页面上所有书籍的数据:

>>> for product in element.parent.parent.find_similar():
        print({
            "name": product.css('h3 a::text').get(),
            "price": product.css('.price_color')[0].re_first(r'[\d\.]+'),
            "stock": product.css('.availability::text').getall()[-1].clean()
        })
{'name': 'A Light in the ...', 'price': '51.77', 'stock': 'In stock'}
{'name': 'Soumission', 'price': '50.10', 'stock': 'In stock'}
{'name': 'Sharp Objects', 'price': '47.82', 'stock': 'In stock'}
...

这里同时演示了 find_similar 与 CSS 选择器、re_firstclean() 的组合拳:先用文本定位一个元素,find_ancestor/parent 上溯到产品卡片容器,再用 find_similar 找出所有同级卡片,最后在每张卡片内做精确字段提取。

进阶实战示例

电商产品网格提取:

def extract_product_grid(page):
    # Find the first product card
    first_product = page.find_by_text('Add to Cart').find_ancestor(
        lambda e: e.has_class('product-card')
    )

    # Find similar product cards
    products = first_product.find_similar()

    return [
        {
            'name': p.css('h3::text').get(),
            'price': p.css('.price::text').re_first(r'\d+\.\d{2}'),
            'stock': 'In stock' in p.text,
            'rating': p.css('.rating')[0].attrib.get('data-rating')
        }
        for p in products
    ]

表格行提取:

def extract_table_data(page):
    # Find the first data row
    first_row = page.css('table tbody tr')[0]

    # Find similar rows
    rows = first_row.find_similar()

    return [
        {
            'column1': row.css('td:nth-child(1)::text').get(),
            'column2': row.css('td:nth-child(2)::text').get(),
            'column3': row.css('td:nth-child(3)::text').get()
        }
        for row in rows
    ]

表单字段提取:

def extract_form_fields(page):
    # Find first form field container
    first_field = page.css('input')[0].find_ancestor(
        lambda e: e.has_class('form-field')
    )

    # Find similar field containers
    fields = first_field.find_similar()

    return [
        {
            'label': f.css('label::text').get(),
            'type': f.css('input')[0].attrib.get('type'),
            'required': 'required' in f.css('input')[0].attrib
        }
        for f in fields
    ]

评论列表提取:

def extract_reviews(page):
    # Find first review
    first_review = page.find_by_text('Great product!')
    review_container = first_review.find_ancestor(
        lambda e: e.has_class('review')
    )

    # Find similar reviews
    all_reviews = review_container.find_similar()

    return [
        {
            'text': r.css('.review-text::text').get(),
            'rating': r.attrib.get('data-rating'),
            'author': r.css('.reviewer::text').get()
        }
        for r in all_reviews
    ]

这些模式(先锚定一个样本元素 → 上溯/定位容器 → find_similar 批量展开 → 卡内精确提取)覆盖了大部分"结构规整但无稳定选择器"的列表型页面。仓库中对应的测试覆盖可参见 tests/parser/test_find_similar_advanced.py,其中包含大量针对边界情况的断言。

Filters 搜索:find / find_all

官方文档认为这是 Scrapling 中"可以说最佳"的元素查找方式:能力强大,且比手写选择器更容易被 Web Scraping 新手掌握。

灵感来自 BeautifulSoup 的 find_allfind_allfind 两个方法都接受多个过滤器,返回同时满足所有过滤器的元素,规则如下:

  • 传入的字符串被视为标签名
  • 传入的可迭代对象(List/Tuple/Set)被视为标签名集合
  • 传入的字典被视为"属性名 → 属性值"映射
  • 传入的正则模式用于按内容过滤元素,类似 find_by_regex
  • 传入的函数用作元素过滤器
  • 传入的关键字参数被视为"HTML 元素属性 → 属性值"

方法会收集所有位置参数与关键字参数,按**瀑布式(waterfall)**顺序依次过滤:

  1. 收集所有匹配所传标签名的元素;
  2. 在所有匹配所传属性的元素中收集(若已用过前一过滤器,则在前一结果上过滤);
  3. 在所有匹配所传正则模式的元素中收集(有前置结果则在前面结果上过滤);
  4. 在所有满足所传函数的元素中收集(有前置结果则在前面结果上过滤)。

两条重要说明:

  1. 过滤过程总是从过滤顺序中第一个实际存在的过滤器开始。比如没传标签名但传了属性,就从第 2 步开始;
  2. 参数传入的先后顺序不重要,唯一生效的顺序就是上面列出的 1→2→3→4。

源码视角:find_all 如何工作

scrapling/parser.py 的实现可以看出几个关键设计:

  1. 参数分桶:逐个检查 args,字符串进 tags 集合、列表/元组/集合校验后并入 tags、字典进 attributesre.Patternpatterns、可调用对象进 functions;不合法的类型(如嵌套可迭代、非字符串键值、无参函数)会直接抛出带说明的 TypeErrorkwargs 也要求值必须是字符串。
  2. Python 关键字白名单classfor 是 Python 保留字,无法作为关键字参数直接传入,源码中定义了 _whitelisted = {"class_": "class", "for_": "for"}(见 scrapling/parser.py),所以写 find_all('div', class_='quote') 时,尾下划线的 class_ 会被自动还原为 class
  3. 标签+属性直接编译为 CSS 选择器:源码注释明确写着"It's easier and faster to build a selector than traversing the tree"——标签和属性会拼成形如 div[class="quote"][href="/p/"] 的 CSS 选择器一次性匹配(属性值中的双引号会被转义,而属性不做转义,因此你可以传 {'href*': '/p/'} 这类带通配符的键来实现"属性值包含/结尾匹配");只有正则和函数过滤才是事后对结果集做 filter

这意味着 Filters 搜索的性能路径与手写 CSS 选择器相同,瀑布式的正则/函数过滤只作用于已缩小后的结果集。

示例

from scrapling.fetchers import Fetcher
page = Fetcher.get('https://quotes.toscrape.com/')

查找所有标签名为 div 的元素:

>>> page.find_all('div')
[<data='<div class="container"> <div class="row...' parent='<body> <div class="container"> <div clas...'>,
 <data='<div class="row header-box"> <div class=...' parent='<div class="container"> <div class="row...'>,
...]

查找所有 class 等于 quote 的 div 元素(三种等价写法):

>>> page.find_all('div', class_='quote')
>>> page.find_all('div', {'class': 'quote'})
>>> page.find_all({'class': 'quote'})

(三种写法均返回相同的 div.quote 元素列表)

查找所有 class 为 quote 的 div,且其内容包含 .text 子元素、该子元素文本含 world

>>> page.find_all('div', {'class': 'quote'}, lambda e: "world" in e.css('.text::text').get())
[<data='<div class="quote" itemscope itemtype="h...' parent='<div class="col-md-8"> <div class="quote...'>]

查找所有有子元素的元素:

>>> page.find_all(lambda element: len(element.children) > 0)

查找所有文本内容包含 world 的元素:

>>> page.find_all(lambda element: "world" in element.text)

查找所有匹配给定正则的 span 元素:

>>> page.find_all('span', re.compile(r'world'))

查找 class 为 quotedivspan 元素(页面上没有这样的 span,故只返回 div):

>>> page.find_all(['div', 'span'], {'class': 'quote'})

组合使用并立刻提取文本:

>>> page.find_all({'itemtype':"http://schema.org/CreativeWork"}, 'div').css('.author::text').getall()
['Albert Einstein',
 'J.K. Rowling',
 ...]

一个进阶技巧:查找 href 属性值 Einstein 结尾的元素(属性名后缀 $ 表示"结尾匹配",这正是上文提到的"键不做转义、可传通配符"的用法):

>>> page.find_all({'href$': 'Einstein'})

另一个技巧:查找 href 属性值包含 /author/ 的元素:

>>> page.find_all({'href*': '/author/'})

Selectors 结果集还支持链式 filter 二次筛选,其语义与测试用例 tests/parser/test_selectors_filter.py 覆盖的行为一致:按谓词过滤、无匹配时返回空的 Selectors(而非抛异常)、可连续链式调用,例如:

items.filter(lambda el: int(el.attrib.get("data-value", 0)) > 0)
      .filter(lambda el: not el.has_class("disabled"))

为任意元素生成选择器

无论你用哪种方式找到了某个元素,都可以随时为它生成可复用的 CSS/XPath 选择器,供本框架或任何其他工具使用:

>>> url_element = page.find({'href*': '/author/'})
>>> url_element.generate_css_selector
'body > div > div:nth-of-type(2) > div > div > span:nth-of-type(2) > a'

生成从页面起点开始的完整 CSS 选择器:

>>> url_element.generate_full_css_selector
'body > div > div:nth-of-type(2) > div > div > span:nth-of-type(2) > a'

生成短 XPath 选择器(尽量短,做不到短则输出完整路径):

>>> url_element.generate_xpath_selector
'//body/div/div[2]/div/div/span[2]/a'

生成完整 XPath 选择器:

>>> url_element.generate_full_xpath_selector
'//body/div/div[2]/div/div/span[2]/a'

官方提示:要求"短选择器"时,Scrapling 会尝试寻找一个唯一元素作为截断点(典型如带 id 属性的元素);示例页面没有这样的元素,所以短选择器与完整选择器输出相同。

从源码(scrapling/core/mixins.pySelectorsGeneration._general_selection)可以看到生成逻辑的完整规则:

  1. 从目标元素向根方向回溯,途中遇到带 id 的元素即以此为终点截断(full_path 模式则不截断,继续回到 html);
  2. 每一层输出 {标签名},并统计目标元素在同父节点下相同标签的序号,若大于 1 则追加 :nth-of-type(n)(CSS)或 [n](XPath)保证唯一性;
  3. 作者特意注释掉了基于 class 的生成——因为有些网站在不同元素间共用完全相同的 class,会导致选择器不唯一;
  4. 注释标明该实现参考了 Firefox 开发者工具的选择器生成逻辑(Mozilla Central 的 css-logic.js)。

选择器配合正则:rere_first

与 parsel/scrapy 类似,Scrapling 提供 rere_first 方法用正则提取数据。但有一个显著不同:在 parsel/scrapy 中这两个方法只在特定选择器类上可用,而在 Scrapling 中它们几乎存在于所有类上——Selector/Selectors/TextHandler/TextHandlers 都有,意味着即使你没有选取文本节点,也可以直接对元素调用。

>>> page.css('.price_color')[0].re_first(r'[\d\.]+')
'51.77'

>>> page.css('.price_color').re_first(r'[\d\.]+')
'51.77'

>>> page.css('.price_color').re(r'[\d\.]+')
['51.77',
 '53.74',
 '50.10',
 '47.82',
 '54.23',
 ...]

>>> page.css('.product_pod h3 a::attr(href)').re(r'catalogue/(.*)/index.html')
['a-light-in-the-attic_1000',
 'tipping-the-velvet_999',
 'soumission_998',
 'sharp-objects_997',
 ...]

对单个 Selector 调用同样可行(findfind_by_text 的返回值都是元素对象):

>>> filtering_function = lambda e: e.parent.tag == 'h3' and e.parent.parent.has_class('product_pod')
>>> page.find('a', filtering_function).attrib['href'].re(r'catalogue/(.*)/index.html')
['a-light-in-the-attic_1000']

>>> page.find_by_text('Tipping the Velvet').attrib['href'].re(r'catalogue/(.*)/index.html')
['tipping-the-velvet_999']

re 的默认行为参数在 scrapling/parser.py 的签名中可见:replace_entities=True(字符实体引用会被替换为对应字符)、clean_match=False(匹配时忽略空白)、case_sensitive=TrueTextHandler 侧的完整方法签名与更多字符串能力(cleansplitgetall 等)详见 docs/parsing/main_classes.md 中对 TextHandler 类的说明。

小结:如何为场景选择查找方式

结合本文内容,可以形成一个实用的选型参考:

场景 推荐方式
页面结构稳定、有清晰 class/属性 css 选择器(首选),或 xpath
只有文本特征(价格、标题文案) find_by_text / find_by_regex
列表页、网格、表格等"结构规整但无稳定选择器" 锚定一个元素 + find_similar
快速原型、混合条件、新手上手 find / find_all 过滤器搜索
需要把找到的元素固化为可复用规则 generate_css_selector / generate_xpath_selector 系列属性
从元素文本中抽取数字、ID 等 在任意 Selector/TextHandler 上直接 re / re_first

本文对应的实现与测试入口:核心实现在 scrapling/parser.pycss/xpath/find/find_all/find_similar/find_by_text/find_by_regex/re),选择器生成在 scrapling/core/mixins.py,文本处理类型在 scrapling/core/custom_types.py;测试参考 tests/parser/test_find_similar_advanced.pytests/parser/test_selectors_filter.pytests/parser/test_general.py。若后续启用 adaptive 自适应选取能力,css/xpathidentifieradaptiveauto_savepercentage 参数将发挥作用,相关内容见 docs/parsing/adaptive.md

登录后查看全文
热门项目推荐
相关项目推荐