
XPath函数,说白了,就是你在构建路径表达式时,用来给筛选节点、处理数据加点“魔法”的工具。它能让你在原本只能按层级、按属性找节点的基础上,进行字符串操作、数值计算、布尔逻辑判断,甚至对节点集进行更复杂的处理。在我看来,掌握这些函数,是把XPath从“能用”提升到“好用”的关键一步,尤其是在面对那些结构不那么规整,或者你需要从文本里抠出特定信息的时候。
解决方案使用XPath函数,你通常会把它们嵌入到你的路径表达式中,无论是作为谓词(
[]里的条件),还是作为最终选择器的一部分。基本语法是
函数名(参数)。参数可以是节点集、字符串、数字或布尔值,具体取决于函数的需求。
我们可以大致把XPath函数分为几类,每类都有其独特的应用场景:
-
节点集函数 (Node Set Functions): 这类函数主要用于处理或获取节点集的信息。
count(node-set)
: 返回节点集中的节点数量。- 例如:
count(//li)
会统计页面上所有<li>
元素的数量。
- 例如:
position()
: 返回当前节点在节点集中的位置(从1开始)。- 例如:
//div[position() = 2]
会选择第二个<div>
元素。
- 例如:
last()
: 返回节点集中的最后一个节点的位置。- 例如:
//p[position() = last()]
会选择最后一个<p>
元素。
- 例如:
id(string)
: 根据ID属性值选择元素。- 例如:
id('main-content')会选择ID为main-content
的元素。
- 例如:
-
字符串函数 (String Functions): 用于处理节点或属性的文本内容。
string(object)
: 将任何类型的数据转换为字符串。- 例如:
string(//h1)
会获取第一个<h1>
元素的文本内容。
- 例如:
concat(string1, string2, ...)
: 连接多个字符串。- 例如:
concat('Hello', ' ', 'World')结果是 "Hello World"。
- 例如:
contains(string1, string2)
: 检查string1
是否包含string2
。- 例如:
//a[contains(@href, 'product')]
选择所有href
属性包含 "product" 的链接。
- 例如:
starts-with(string1, string2)
: 检查string1
是否以string2
开头。- 例如:
//div[starts-with(@class, 'item-')]
选择所有class
属性以 "item-" 开头的<div>
。
- 例如:
substring(string, start, length)
: 提取字符串的子串。- 例如:
substring('HelloWorld', 6, 5)结果是 "World"。
- 例如:
string-length(string)
: 返回字符串的长度。- 例如:
//p[string-length(text()) > 50]
选择文本内容长度超过50的<p>
。
- 例如:
normalize-space(string)
: 移除字符串开头和结尾的空格,并将内部连续的空格替换为单个空格。- 这在处理网页上那些格式不统一的文本时简直是神器,比如
normalize-space(//div[@class='description']/text())
。
- 这在处理网页上那些格式不统一的文本时简直是神器,比如
-
布尔函数 (Boolean Functions): 返回
true
或false
,常用于谓词中。not(boolean)
: 对布尔值取反。- 例如:
//a[not(@target='_blank')]
选择所有target
属性不为 "_blank" 的链接。
- 例如:
true()
/false()
: 返回字面布尔值。
-
数值函数 (Number Functions): 用于执行数学计算。
number(object)
: 将对象转换为数字。sum(node-set)
: 计算节点集中所有节点的数值之和。floor(number)
: 向下取整。ceiling(number)
: 向上取整。round(number)
: 四舍五入。
这些函数可以单独使用,也可以嵌套使用,形成非常强大的表达式。比如,你想找一个
class包含 "card" 并且文本内容里有 "优惠" 字样的
div,你可以这样写:
//div[contains(@class, 'card') and contains(text(), '优惠')]。这里的
and也是一个逻辑运算符,它允许你组合多个条件。 常用XPath函数有哪些,它们各自的典型应用场景是什么?
在我日常的网页抓取和XML处理工作中,有那么几个XPath函数是我的“常客”,用得频率非常高,它们各自解决的问题也挺典型的。
首先是
contains()和
starts-with(),这两个简直是处理动态或模糊属性值的利器。很多网站的
class或
id属性会带有一些随机字符串,比如
item-12345或
product-card-abc。如果你只知道
item-或
product-card-这部分固定前缀,
starts-with(@class, 'item-')就能帮你精准定位。而如果关键词可能出现在属性值的任何位置,比如
href属性里包含
category=electronics,那
contains(@href, 'category=electronics')就派上用场了。我个人觉得,这两个函数极大地增强了XPath的鲁棒性,让它在面对前端框架生成的复杂HTML时也能游刃有余。
接着是
normalize-space()。哦,这个函数,简直是文本清洗的救星!网页上的文本内容,特别是那些从数据库里拉出来或者用户输入的内容,经常会带着多余的空格、换行符、制表符。直接获取会很脏,影响后续的数据处理。
normalize-space(text())可以把这些乱七八糟的空白字符清理得干干净净,只保留一个有意义的、规范化的字符串。我很多时候会直接在提取文本时就加上它,省去了后面用Python或JavaScript再做一次清洗的麻烦。
PIA
全面的AI聚合平台,一站式访问所有顶级AI模型
226
查看详情
count()和
position()也很常用,尤其是在你需要处理列表或者重复结构时。比如,你可能想确认某个列表有多少项,或者只想选择列表中的前几项、中间某一项。
count(//ul/li)告诉我列表项的总数,而
//ul/li[position() < 3]就能选出前两项。有时候,网站的结构并不提供直接的索引,但你可以通过
position()来模拟,比如找到某个特定元素后的第三个兄弟节点。
最后,不得不提
text()。虽然它不是一个函数,但经常和函数一起使用。
//div[@class='price']/text()就能直接抓取到价格文本。但如果文本分散在多个子节点中,或者夹杂着其他标签,你可能需要
string(.)或者
normalize-space(.)来获取所有子孙文本的拼接结果,这在提取段落内容时特别有效。 在实际抓取数据时,如何利用XPath函数处理复杂的文本和属性值?
在实际的数据抓取中,复杂的文本和属性值是常态,而不是例外。XPath函数在这里能发挥出巨大的作用,让你的选择器变得更加智能和灵活。
举个例子,假设你要从一个商品详情页抓取价格。价格信息可能不是一个简单的数字,它可能是“¥ 129.99起”或者“原价: 200.00,现价: 150.00”。 如果你的目标是提取“129.99”,你可以先用
normalize-space()清理文本,然后结合
substring-before()或
substring-after()甚至
translate()来“切割”字符串。比如,
substring-before(normalize-space(//div[@class='price']/text()), '起')就能帮你拿到“¥ 129.99”。如果连货币符号也想去掉,你可能需要更进一步,比如
translate(substring-before(normalize-space(//div[@class='price']/text()), '起'), '¥', '')。
再比如,处理属性值。很多网站的图片URL可能藏在
data-src属性里,或者一个元素的
class属性包含多个值,如
class="product-item large active"。如果你只想选择
active的商品项,你可以用
//div[contains(@class, 'active')]。但如果
active只是其中一个状态,你可能还需要结合其他条件,比如
//div[contains(@class, 'product-item') and contains(@class, 'active')]。
有时候,链接的
href属性可能是一个相对路径,或者包含一些你不需要的查询参数。虽然XPath函数不能直接修改URL,但你可以用
starts-with()筛选出你感兴趣的特定路径,或者用
substring-before()截取URL的固定部分,比如
substring-before(@href, '?')来去除问号后的所有参数,当然这只是一个初步的筛选,更精细的URL处理通常在抓取后进行。
我还遇到过一种情况,需要从一个混杂了文字和数字的字符串中提取纯数字。比如“库存:123件”。这时候,
translate()函数就显得特别强大。你可以通过
translate(., '库存:件', '')将这些非数字字符替换为空,从而只留下数字部分。然后,如果你想把它当作数字来计算,可以用
number(translate(., '库存:件', ''))。这种组合使用,确实能解决不少让人头疼的提取问题。 XPath函数在处理多语言内容或不规则HTML结构时有哪些高级用法?
处理多语言内容和不规则HTML结构,是XPath函数真正展现其“高级”之处的场景。这不仅仅是找到元素,更是要以一种灵活且健壮的方式去适应变化。
对于多语言内容,虽然XPath 1.0中没有直接的语言检测函数(XPath 2.0+有
lang()),但我们依然可以通过结合文本内容判断来间接处理。比如,如果你要抓取一个新闻网站的标题,而这个网站有中文和英文版,但HTML结构可能只有微小差异,甚至完全一样,只是内容不同。 你可以通过检查标题文本中是否包含特定语言的关键词来筛选:
//h2[contains(text(), '新闻') or contains(text(), '最新')]针对中文,
//h2[contains(text(), 'News') or contains(text(), 'Latest')]针对英文。这虽然有点笨拙,但在没有
lang属性或者其他明确语言标识时,是一个实用的方法。 更进一步,如果你知道不同语言的特定日期格式或数字格式,也可以利用
matches()(XPath 2.0+) 或
translate()结合
string-length()来进行模式匹配和筛选。比如,检查文本是否符合某种日期格式的正则表达式。
至于不规则HTML结构,这才是XPath函数大显身手的地方。网页开发者并不总是按照W3C标准来写HTML,或者为了快速迭代,结构会变得非常“自由”。
-
相对定位与索引的结合: 当一个元素没有明确的ID或class,但它总是出现在某个已知元素之后(或之前)的某个位置时,
following-sibling::*
或preceding-sibling::*
结合position()
就非常有用了。- 比如,
//h2[text()='产品详情']/following-sibling::p[position()=1]
就能找到标题“产品详情”后的第一个段落。这比硬编码一个绝对路径要灵活得多。
- 比如,
- *泛化选择器 `
结合
name()或
local-name():** 有时候,你不知道具体的标签名是什么,或者标签名可能会变(比如从
div变成
section`),但你知道它一定是某个标题标签(h1-h6)或某个列表项。//*[starts-with(name(), 'h')]
可以选择所有h1
到h6
的标题。这在处理语义化不那么严格的页面时,能帮你抓取所有层级的标题。- 如果页面使用了XML命名空间,
local-name()
函数就显得尤为重要,它会忽略命名空间前缀,只关注标签的本地名称。
-
根据文本内容长度筛选: 遇到很多空标签或者占位符标签时,你可能只想要那些有实际内容的元素。
//p[string-length(normalize-space(text())) > 0]
可以筛选出所有非空的<p>
标签。这在过滤掉那些视觉上不可见但HTML里存在的空标签时非常有效。
-
id()
函数的妙用: 即使id
属性不在你当前路径的直接父级,你也可以通过id('some-id')直接跳到那个元素,然后从那里开始你的相对路径。这在处理大型、复杂且有明确ID标识的页面时,能提供一个快速的“锚点”。
总而言之,面对复杂和不规则的结构,XPath函数提供了一种“以不变应万变”的策略。它们允许你根据元素的行为、内容或相对位置来定位,而不是仅仅依赖于其静态的层级和标签名。这需要一些经验和对HTML结构的洞察,但一旦掌握,你的XPath技能会提升一个档次。
以上就是XPath函数如何使用?的详细内容,更多请关注知识资源分享宝库其它相关文章!
相关标签: php java javascript python html 前端 node go 正则表达式 工具 ai Python JavaScript 正则表达式 html 前端框架 String Boolean Object 运算符 逻辑运算符 count 命名空间 xml 字符串 class Length number 对象 href 选择器 position ul li 数据库 大家都在看: XML如何使用PHP修改内容 php读取XML的四种方法实例详解 php解析xml方法实例(附代码)详细说明 PHP扩展之XML操作(三)——XML解析器使用及相关函数 PHP扩展之XML操作(一)——SimpleXML






发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。