xmlstarlet无法提取Firefox开发者工具中存在的XPath元素(无输出)求助
兄弟,这种明明在浏览器开发者工具里能精准定位到元素,但命令行里的xmlstarlet就是死活找不到的情况,我太懂那种抓狂感了!咱们一步步拆解问题,肯定能找到原因~
先搞懂核心差异:浏览器DOM vs 静态HTML
首先你得明白:Firefox开发者工具里看到的DOM不是curl抓下来的原始静态HTML!浏览器会自动补全HTML结构、加载动态资源(比如某些link可能是JS动态插入的)、修复不规范的标签,这些都会导致DOM里的元素数量/顺序和你curl到的原始内容完全不一样。你用XPath link[106] 依赖的是元素的索引顺序,这在静态HTML里大概率不成立。
第一步:先排查tidy处理后的XML里到底有多少个link
先别着急用索引,先看看tmp.html里head下实际有多少个link元素:
xmlstarlet sel -t -v 'count(/html/head/link)' tmp.html
如果输出的数字远小于106,那实锤了:Firefox里的DOM因为动态加载多了很多link,但你curl抓的静态HTML里根本没这么多,自然找不到link[106]。
第二步:检查XML命名空间的坑
tidy用-asxml转换后的HTML,大概率会自动加上XHTML的命名空间,打开tmp.html看看开头是不是这样:
<html xmlns="http://www.w3.org/1999/xhtml">
如果是的话,xmlstarlet作为严格的XML工具,必须指定命名空间才能找到元素(Firefox的XPath会自动忽略命名空间,但命令行工具不可以)。你需要用-N参数声明命名空间,比如:
xmlstarlet sel -N x=http://www.w3.org/1999/xhtml -t -c '/x:html/x:head/x:link[106]' tmp.html
第三步:修复你的sed替换错误
你的sed命令sed -i 's/nbsp;/#160;/m' tmp.html有问题:nbsp的正确XML实体是 ,你少了&!替换成#160;会导致XML里出现无效的内容,可能让xmlstarlet解析异常,虽然没报错,但会影响元素识别。改成:
sed -i 's/nbsp;/ /g' tmp.html
(把m换成g做全局替换,更合理)
更可靠的替代方案:别用索引,用元素属性定位
依赖索引link[106]是非常脆弱的,只要页面结构微调(比如加/减一个link)就会失效。不如找目标link的唯一属性特征,比如它的rel、href或者title。比如你要的link如果是某个样式表,href里包含特定字符串,你可以这么写:
xmlstarlet sel -t -c '/html/head/link[contains(@href, "lalbero-del-riccio")]' tmp.html
或者如果它的rel是alternate,就用:
xmlstarlet sel -t -c '/html/head/link[@rel="alternate"]' tmp.html
这样不管元素顺序怎么变,只要属性不变,就能精准定位。
最后验证:直接查看处理后的XML结构
如果还是搞不清,直接格式化XML来浏览:
xmlstarlet format tmp.html | less
这样你能直观看到head下所有link的真实顺序和属性,对比Firefox里的DOM,就能明白差异在哪里了。
内容来源于stack exchange

