preceding-sibling轴用于选择与当前节点同父且在文档顺序中位于其前的所有同级节点,例如在html中定位同一父元素下排在当前节点前面的兄弟元素;与preceding轴不同,后者范围更广,包含文档中所有非祖先的前置节点,而不仅限于同级;通过添加位置谓语[1]可精确选取紧邻的前一个同级节点,如preceding-sibling::*[1];该轴常用于网页抓取中关联上下文信息、提取列表前置数据、定位表单标签或处理错误提示,适用于通过相对位置关联数据的非规整结构场景。
XPath的
preceding-sibling轴,说白了,就是让你能往回找,找那些跟你在同一个爹妈生的、又比你早出生的兄弟姐妹。它帮你定位到当前节点在文档流中排在它前面的、并且拥有同一个父节点的所有同级元素。 解决方案
preceding-sibling轴用于选择当前节点(上下文节点)的所有先行同级节点。这意味着它会查找与当前节点共享相同父节点,并且在文档顺序中出现在当前节点之前的节点。
它的基本用法是:
当前节点路径/preceding-sibling::目标节点类型。
举个例子,如果你有一个HTML结构:
<div> <p>这是第一段</p> <span>这是一个span</span> <p id="current">这是当前段落</p> <em>这是一个em</em> </div>
如果你当前选中的是
<p id="current">这个节点,那么使用
preceding-sibling::p就会选中前面的
<p>这是第一段</p>。而
preceding-sibling::*则会选中
<p>这是第一段</p>和
<span>这是一个span</span>。
它只关心“同级”和“之前”,不关心层级关系。这一点非常关键,因为它限定了查找的范围,让你的XPath表达式更加精确。很多时候,我们就是需要这种特定方向上的“邻居”信息。
preceding-sibling 和 preceding 有什么区别?这是个特别容易混淆的地方,但理解了就豁然开朗了。简单来说,
preceding-sibling更“亲近”,而
preceding更“广阔”。
preceding-sibling轴,就像前面说的,它只找“同父同母”的兄弟姐妹,而且必须是排在你前面的。它的搜索范围被严格限制在当前节点的同级节点中。
而
preceding轴,它的范围就大多了。它会选择文档中所有在当前节点之前出现的节点,只要它们不是当前节点的祖先节点、属性节点或命名空间节点。这意味着它不仅会包括你的前同级兄弟姐妹,还会包括你叔叔阿姨家的孩子(也就是你父辈的同级节点下的子节点),甚至更远,只要它们在文档顺序上排在你前面。
来个直观的例子:
<div id="container"> <p>外层段落1</p> <div> <span>内层span</span> <p id="target">目标段落</p> </div> <p>外层段落2</p> </div>
如果我们的上下文节点是
<p id="target">:
- 使用
./preceding-sibling::*
,只会选中<span>内层span</span>
,因为它在同一个div
父节点下,且排在<p id="target">
之前。 - 使用
./preceding::*
,则会选中<span>内层span</span>
、<p>外层段落1</p>
,因为它寻找的是文档中所有在<p id="target">
之前出现的非祖先节点。你看,<p>外层段落1</p>
虽然不是<p id="target">
的同级,但它在文档顺序上确实在<p id="target">
之前,所以也会被选中。
所以,如果你明确知道要找的是同一个父节点下的前一个或前几个兄弟元素,用
preceding-sibling会更精准、更高效。如果需要更广泛地向前查找,那就考虑
preceding。 如何精确选择前一个同级节点?
很多时候,我们并不想选择所有前面的同级节点,而是仅仅想抓取紧挨着当前节点的前一个同级节点。这在处理数据列表或者表单元素时特别常见。
要实现这个,其实很简单,就是在
preceding-sibling后面加上一个位置谓语
[1]。
例如:
当前节点路径/preceding-sibling::*[1]
这里的
*表示任何元素类型,
[1]则表示选择结果集中的第一个。由于
preceding-sibling轴默认是从离当前节点最近的那个同级节点开始“倒序”排列的,所以
[1]就恰好选中了紧挨着当前节点、并且在它前面的那个同级节点。
如果你知道具体要找的元素类型,可以更精确:
当前节点路径/preceding-sibling::div[1],这会选择紧邻的、且类型为
div的前一个同级节点。
举个例子:
<ul> <li>Item A</li> <li>Item B</li> <li id="current">Item C</li> <li>Item D</li> </ul>
如果你的上下文是
<li id="current">Item C</li>,那么:
./preceding-sibling::li
会选中<li>Item A</li>
和<li>Item B</li>
。./preceding-sibling::li[1]
就只会选中<li>Item B</li>
,因为它就是紧挨着Item C
的前一个li
同级。
这种方法在处理那些结构上紧密相连,但又没有直接父子关系的元素时,简直是神器。比如,一个价格标签后面跟着一个货币符号,或者一个输入框前面紧跟着它的标签文本。
preceding-sibling 在实际网页抓取中有什么应用场景?preceding-sibling轴在实际的网页抓取和数据提取中,简直是高频工具,尤其是在那些网页结构并不那么“规矩”的时候。
-
关联上下文信息: 很多网页设计为了视觉效果,会把相关联的数据放在同级元素里,而不是严格的父子结构。比如,你可能找到一个商品的描述,而它的标题或SKU信息却在它前面的一个同级
div
里。这时,你可以先定位到描述div
,然后用preceding-sibling::div[1]
来获取前面的标题。<div class="product-name">大号T恤</div> <div class="product-price">$29.99</div> <div class="product-description">纯棉,宽松版型...</div>
如果你定位到
//div[@class='product-price']
,想获取商品名,就可以用/preceding-sibling::div[1]
。 处理列表或表格的“前置”数据: 在一些复杂的表格或列表渲染中,某个单元格或列表项的数据可能依赖于它前面同级的某个标识符或分类。例如,一个表格行中的某个数据单元格,它的分类信息可能在它前面的一个同级
<td>
中。-
动态内容与标签的关联: 网站上的表单元素经常是这样:一个
<label>
标签后面紧跟着一个<input>
或<textarea>
。当你想获取某个输入框的标签文本时,如果你能定位到输入框,就可以用preceding-sibling::label[1]
来获取其对应的标签。<label for="username">用户名:</label> <input type="text" id="username"> <label for="password">密码:</label> <input type="password" id="password">
如果你定位到
//input[@id='password']
,想获取它的标签,直接/preceding-sibling::label[1]
就能拿到“密码:”。 处理错误信息或提示: 有些表单验证错误信息会出现在对应的输入框之后,但有时候,为了布局,它会出现在输入框之前,且是同级。比如一个
<span>
提示错误,紧挨着它后面才是input
。反过来,如果你定位到错误信息,想知道是哪个输入框出了问题,preceding-sibling
也能帮上忙。
总之,
preceding-sibling轴提供了一种非常灵活且强大的方式来逆向遍历和定位元素,尤其适用于那些在文档流中通过相对位置来定义相互关系的场景。它让你在面对不那么“整齐”的HTML结构时,也能游刃有余地提取所需数据。
以上就是XPath的preceding-sibling轴如何选择前同级?的详细内容,更多请关注知识资源分享宝库其它相关文章!
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。