boxmoe_header_banner_img

Hello! 欢迎来到悠悠畅享网!

文章导读

XPath的contains()方法怎么用?有哪些应用场景?


avatar
站长 2025年8月15日 4

“淘宝第一个程序员”蔡景现(花名多隆)已从阿里巴巴离职,结束25年任职生涯。作为淘宝初创核心工程师,他构建了淘宝交易系统,以技术实力闻名,曾以26亿身家登上胡润富豪榜,其阿里内外状态已显示为“退隐江湖”。

XPath的contains()方法怎么用?有哪些应用场景?

XPath的

contains()

方法,简单来说,就是用来判断一个字符串是否包含另一个特定的子字符串。它在处理那些内容不完全固定、但部分特征可识别的网页元素时,简直是神器。比如,你想找一个按钮,它的类名总是变,但里面肯定包含“submit”这个词,或者一个文本段落,它总是有“订单号”这几个字,但后面的数字每次都不同,这时候

contains()

就能派上大用场。

解决方案

contains()

方法的语法非常直观:

contains(string, substring)

。第一个参数是你想要检查的字符串(比如元素的文本内容或某个属性的值),第二个参数是你想要查找的子字符串。

举几个实际例子:

  • 查找文本中包含特定内容的元素: 假设你想找到所有包含“最新消息”这几个字的

    div

    元素。

    //div[contains(text(), '最新消息')]

    这里,

    text()

    函数获取元素的文本内容,然后

    contains()

    判断其是否包含“最新消息”。

  • 查找属性值中包含特定内容的元素: 如果有一个链接,它的

    href

    属性值总是包含“product”这个词,但后面可能跟着不同的ID。

    //a[contains(@href, 'product')]
    @href

    表示获取

    href

    属性的值。

  • 处理动态类名: 很多前端框架会生成动态的CSS类名,比如

    btn-primary-dsf23k

    ,但核心部分

    btn-primary

    是不变的。

    //button[contains(@class, 'btn-primary')]

    这比用

    @class='btn-primary-dsf23k'

    这种完全匹配要灵活得多。

  • 结合其他条件使用: 你也可以将

    contains()

    与其他XPath表达式结合起来,进行更精确的定位。

    //div[contains(@id, 'item') and contains(text(), '详情')]

    这会找到所有

    id

    包含“item”且文本内容包含“详情”的

    div

    元素。

XPath

contains()

starts-with()

ends-with()

有什么区别

说实话,XPath这东西,用好了就是利器,但刚上手时,

contains()

starts-with()

ends-with()

这几个哥们儿确实容易让人混淆。它们都是字符串匹配,但侧重点完全不一样。

  • starts-with(string, substring)

    :顾名思义,它只检查一个字符串是否指定的子字符串开头。 比如,你有一堆ID是

    user_1

    user_2

    admin_1

    的元素,你想找所有用户相关的元素,用

    starts-with(@id, 'user')

    就非常精准。它不会匹配到

    super_user_1

    这种ID。

  • ends-with(string, substring)

    :这个方法在XPath 2.0及更高版本中才支持,它检查一个字符串是否指定的子字符串结尾。 如果你想找所有以

    .png

    结尾的图片链接,

    //img[ends-with(@src, '.png')]

    就很方便。在一些老旧的系统或者XPath 1.0的环境里,你可能得想别的办法,比如用

    substring()

    string-length()

    组合来模拟。

  • contains(string, substring)

    :而

    contains()

    就宽松多了,它不关心子字符串在原字符串的开头、结尾还是中间,只要有,它就认为匹配。 这就像是“模糊匹配”的王者。比如,一个元素的类名可能是

    active-item

    ,也可能是

    item-active

    ,或者

    main-item-active-status

    ,只要里面有

    item

    ,你用

    contains(@class, 'item')

    都能抓到。

什么时候用哪个?

  • 当你明确知道目标字符串的前缀时,用
    starts-with()

  • 当你明确知道目标字符串的后缀时(且环境支持),用
    ends-with()

  • 当目标子字符串可能出现在任何位置,或者你只想进行一个宽泛的包含匹配时,
    contains()

    是你的首选。

contains()

方法在处理动态类名或文本时有哪些实战技巧?

很多时候,我们抓取网页或者自动化测试时,会遇到一些让人头疼的动态元素。它们的ID、类名或者文本内容,可能每次加载都变一部分。这时候,

contains()

的灵活就体现出来了。

  1. 动态类名处理的“万金油”: 设想一下,一个按钮,今天它的类名是

    btn-primary-randomhash123

    ,明天可能变成

    btn-primary-anotherhash456

    。你不可能每次都去 inspect 元素拿到完整的类名。但核心的

    btn-primary

    通常是固定的。

    //button[contains(@class, 'btn-primary')]

    这招屡试不爽,能大幅提升你的XPath表达式的健壮性。甚至有些元素可能有多个类名,比如

    <div class="item active selected">

    ,如果你只想找到所有“活跃”的项,而不管它是不是“选中”的,

    //div[contains(@class, 'active')]

    就能精准命中。

  2. 处理部分变化的文本内容: 网页上经常有这种文本:“您的订单号是:ABC123456789”,或者“当前库存:150件”。其中“ABC123456789”和“150”是动态变化的。

    //p[contains(text(), '您的订单号是:')]
    //span[contains(text(), '当前库存:') and contains(text(), '件')]

    (这里用两个

    contains

    来确保匹配的精确性,避免只匹配到“当前库存”或“件”的意外情况) 这样,即使订单号或库存量变了,你的XPath依然能准确找到目标元素。

  3. 应对大小写不敏感的需求(XPath 1.0 局限):

    contains()

    是大小写敏感的。这意味着

    contains(text(), 'hello')

    不会匹配到“Hello”。在XPath 1.0中,要实现大小写不敏感,你需要借助

    translate()

    函数,将字符串中的大写字母转换为小写,然后再进行

    contains()

    判断。

    //div[contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'hello')]

    这看起来有点长,但确实是XPath 1.0里比较通用的做法。如果你的环境支持XPath 2.0,那就可以用

    lower-case()

    函数,会简洁很多:

    //div[contains(lower-case(text()), 'hello')]

    。了解这些,能让你在遇到实际问题时,知道如何变通。

使用

contains()

时可能遇到哪些常见问题和性能考量?

contains()

虽然好用,但用起来也有些小坑和需要注意的地方。

  1. 大小写敏感性陷阱: 这是最常见的“坑”之一。很多人以为

    contains()

    会智能地忽略大小写,结果发现怎么也匹配不上。比如

    contains(@class, 'active')

    不会匹配到

    Active

    。遇到这种情况,要么老老实实写精确的大小写,要么就得用上面提到的

    translate()

    或者

    lower-case()

    来处理。

  2. 过度匹配的风险:

    contains()

    的“模糊”特性,有时也会带来过度匹配的问题。如果你想找一个

    class

    item-list

    的元素,却写了

    //div[contains(@class, 'item')]

    ,那么可能

    item-detail

    sub-item

    等所有包含“item”的元素都会被匹配到,这显然不是你想要的。 解决办法是:尽可能结合其他更精确的定位方式来缩小范围,比如先用ID或更具体的父级路径,再使用

    contains()

    。 例如,

    //div[@id='main-content']//li[contains(@class, 'product-item')]

    就比

    //li[contains(@class, 'product-item')]

    更精准,因为它限定了只在

    id

    main-content

    div

    内部查找。

  3. 性能考量(虽然通常不是大问题): 对于现代浏览器和XPath引擎来说,

    contains()

    的性能通常不是瓶颈,但在极端情况下,比如处理非常庞大、复杂的XML文档,或者在循环中大量使用

    contains()

    ,还是值得注意一下。 相比于直接的属性匹配(如

    @id='someId'

    )或

    starts-with()

    contains()

    需要遍历整个字符串来查找子串,理论上会稍微慢一点点。不过,这种差异在大多数网页抓取或自动化场景中几乎可以忽略不计。 真正的性能杀手往往是那些过于宽泛的XPath,比如

    //*[contains(text(), '某个词')]

    。这种表达式会扫描整个文档树,效率会比较低。所以,尽量从一个更具体的父节点开始,缩小搜索范围,总是一个好习惯。

总的来说,

contains()

是一个非常实用的工具,掌握它的用法和特性,能让你在处理动态网页内容时更加游刃有余。



评论(已关闭)

评论已关闭