如何在XPath中使用contains(text(), )获取兄弟节点
今天我接触到了xpath,感觉它非常强大。不过我搜索了很久,还是没找到在使用contains的时候,怎么获取兄弟节点(通过following-sibling和preceding-sibling)。
text = """
<html>
<head>
<title>This tag includes 'some_text'</title>
<h2>A h2 tag</h2>
</head>
</html>
"""
import lxml.html
doc = lxml.html.fromstring(text)
a = doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*")
这个方法返回的是[]
。当然,我期待的结果是能获取到h2标签。
不过,使用*[contains(text(),'name')]
可以如我所愿地获取到title
元素。同样,如果我不使用following-sibling这个轴(我想这就是它的叫法),而是用//parent::*
,也能成功。
那么,在这种情况下,我该怎么获取兄弟节点呢?
提前谢谢大家。
4 个回答
1
<?xml version="1.0" ?>
<html>
<head>
<title>This tag includes 'some_text'</title>
<h2>A h2 tag</h2>
</head>
</html>
//*[contains(text(),'some_text')]/following-sibling::*
Array
(
[0] => SimpleXMLElement Object
(
[0] => A h2 tag
)
)
我使用了PHP的SimpleXMLElement,不过xpath应该是一样的。
1
在回答这个问题之前,有几个事情需要先说明:
- 使用following-sibling会返回所有后面的兄弟节点,而不仅仅是紧挨着的一个。所以如果在这个节点后面还有其他节点,它们也会被返回。
- HTML和XML是不同的。虽然LXML会帮你清理源代码,但如果你不能保证输入的HTML是干净的,那么你的XPath可能会出错。例如,我认为在HTML中,title标签不需要闭合标签,所以根据源代码的情况,LXML可能会错误地把某个节点当作title的子节点,这样就会导致XPath出问题。
- 标题标签不能有子元素,这可能会影响LXML的清理方式(比如在它们之间添加一个body标签等等)。
在XML编辑器中测试这个XPath显示它是有效的,但我在LXML中测试时却缺少元素,这可能意味着它以某种方式改变了XML(不过我没有去检查)。
我建议你重新考虑一下XPath是否适合这个任务,特别是如果你是想用它来抓取网页或类似的内容。
你也可以考虑重写XPath语句,让它更易读一些。
//*[contains(text(),'some_text')]/following-sibling::*
这个表达的意思是:找出任何包含“某些文本”的元素,然后获取它的所有后续兄弟节点。
//*[preceding-sibling::*[position()=1 and contains(text(),'some_text') and ]]
而这个表达的意思是:找出第一个前面的兄弟节点包含“某些文本”的元素。
这可能是风格问题,但我觉得后者更容易理解。
7
你有个有趣的HTML示例。
import lxml
text = """
<html>
<body>
<span>This tag includes 'some_text'</span>
<h2>A h2 tag</h2>
</body>
</html>
"""
doc = lxml.etree.fromstring(text, parser=lxml.etree.HTMLParser())
doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*")
# [<Element h2 at 102eee100>]
doc = lxml.html.fromstring(text)
doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*")
# [<Element h2 at 102f6f188>]
更新:
在这里,我没有使用html
解析器和它的验证规则,而是把输入当作随机的XML来处理:
text = """
<html>
<head>
<title>This tag includes 'some_text'</title>
<h2>A h2 tag</h2>
</head>
</html>
"""
doc = lxml.etree.fromstring(text)
doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*[1]")
# [<Element h2 at 102eeef70>]