Scrapling 元素选取完全指南:CSS/XPath 选择器、文本匹配、find_similar 与 Filters 搜索
本文系统讲解 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 中,查找元素共有五种主要方式:
- CSS3 选择器(
css方法) - XPath 选择器(
xpath方法) - 基于过滤器/条件的查找(
find/find_all方法) - 按文本内容包含关系查找(
find_by_text方法) - 按正则表达式匹配查找(
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。它的执行路径比表面更值得注意:
- 选择器先经过
_css_to_xpath(selector)转换为 XPath 表达式,再调用self.xpath(...)完成实际匹配——即 CSS 与 XPath 两条入口在底层汇合于 lxml; - 若选择器中包含逗号分隔的组合选择器(如
.a, .b)且启用了自适应功能,会先拆分(split_selectors)、逐段规范化(.canonical())后分别匹配再合并,保证auto_save能正确保存组合选择器的数据; - 语法非法的选择器不会静默失败,而是抛出
SelectorSyntaxError,错误信息中带原始选择器文本,便于排查。
css/xpath 方法还带有若干与自适应功能相关的参数(identifier、adaptive、auto_save、percentage,默认 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 的元素下、包含 Phone 的 h1 的文本:
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_text 与 find_by_regex
Scrapling 提供两种基于元素直接文本内容的选取方式:
- 直接文本包含指定文本的元素 ——
find_by_text,带多种选项 - 直接文本匹配指定正则模式的元素 ——
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_text 与 find_by_regex 都使用了 @overload 做返回类型收窄——first_match=True 时类型注解为 Selector,first_match=False 时为 Selectors,静态检查工具可以据此给出精确提示。另外 find_by_regex 的第一个参数既可以是普通字符串,也可以是 re.compile(...) 编译后的 pattern,Scrapling 会自动识别输入类型(实现中直接复用 TextHandler.re(..., check_match=True),见 scrapling/parser.py)。
查找相似元素:find_similar
这是 Scrapling 最具代表性的新特性之一。其灵感来自 AutoScraper 库,但关键区别是:在 Scrapling 中,它对任何方式找到的元素都可用。典型用法是先通过文本找到某个产品,再用它找出同容器里的其它产品。
工作机制分三步:
- 找出页面中所有与该元素 DOM 树深度相同的元素;
- 逐一检查,淘汰标签名、父标签名、祖父标签名不一致的元素;
- 前两步已保证约 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 计分,并额外对 class、id、href、src 四个关键属性单独做相似度检测,以应对页面结构性大改的情况。一个边界情况也写在源码注释里:当前元素与候选元素都没有属性时,视为 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_first、clean() 的组合拳:先用文本定位一个元素,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_all。find_all 和 find 两个方法都接受多个过滤器,返回同时满足所有过滤器的元素,规则如下:
- 传入的字符串被视为标签名
- 传入的可迭代对象(List/Tuple/Set)被视为标签名集合
- 传入的字典被视为"属性名 → 属性值"映射
- 传入的正则模式用于按内容过滤元素,类似
find_by_regex - 传入的函数用作元素过滤器
- 传入的关键字参数被视为"HTML 元素属性 → 属性值"
方法会收集所有位置参数与关键字参数,按**瀑布式(waterfall)**顺序依次过滤:
- 收集所有匹配所传标签名的元素;
- 在所有匹配所传属性的元素中收集(若已用过前一过滤器,则在前一结果上过滤);
- 在所有匹配所传正则模式的元素中收集(有前置结果则在前面结果上过滤);
- 在所有满足所传函数的元素中收集(有前置结果则在前面结果上过滤)。
两条重要说明:
- 过滤过程总是从过滤顺序中第一个实际存在的过滤器开始。比如没传标签名但传了属性,就从第 2 步开始;
- 参数传入的先后顺序不重要,唯一生效的顺序就是上面列出的 1→2→3→4。
源码视角:find_all 如何工作
从 scrapling/parser.py 的实现可以看出几个关键设计:
- 参数分桶:逐个检查
args,字符串进tags集合、列表/元组/集合校验后并入tags、字典进attributes、re.Pattern进patterns、可调用对象进functions;不合法的类型(如嵌套可迭代、非字符串键值、无参函数)会直接抛出带说明的TypeError,kwargs也要求值必须是字符串。 - Python 关键字白名单:
class、for是 Python 保留字,无法作为关键字参数直接传入,源码中定义了_whitelisted = {"class_": "class", "for_": "for"}(见 scrapling/parser.py),所以写find_all('div', class_='quote')时,尾下划线的class_会被自动还原为class。 - 标签+属性直接编译为 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 为 quote 的 div 和 span 元素(页面上没有这样的 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.py 的 SelectorsGeneration._general_selection)可以看到生成逻辑的完整规则:
- 从目标元素向根方向回溯,途中遇到带
id的元素即以此为终点截断(full_path模式则不截断,继续回到html); - 每一层输出
{标签名},并统计目标元素在同父节点下相同标签的序号,若大于 1 则追加:nth-of-type(n)(CSS)或[n](XPath)保证唯一性; - 作者特意注释掉了基于 class 的生成——因为有些网站在不同元素间共用完全相同的 class,会导致选择器不唯一;
- 注释标明该实现参考了 Firefox 开发者工具的选择器生成逻辑(Mozilla Central 的
css-logic.js)。
选择器配合正则:re 与 re_first
与 parsel/scrapy 类似,Scrapling 提供 re 和 re_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 调用同样可行(find 与 find_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=True。TextHandler 侧的完整方法签名与更多字符串能力(clean、split、getall 等)详见 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.py(css/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.py、tests/parser/test_selectors_filter.py 与 tests/parser/test_general.py。若后续启用 adaptive 自适应选取能力,css/xpath 的 identifier、adaptive、auto_save、percentage 参数将发挥作用,相关内容见 docs/parsing/adaptive.md。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00