215

是否有适用于 Ubuntu 和/或 CentOS 的软件包,它有一个命令行工具,可以像执行 XPath 单行一样foo //element@attribute filename.xmlfoo //element@attribute < filename.xml逐行返回结果?

我正在寻找可以让我开箱即用的东西,不需要包装器或其他改编apt-get install fooyum install foo

以下是一些接近的例子:

诺科切里。如果我编写这个包装器,我可以按上述方式调用包装器:

#!/usr/bin/ruby

require 'nokogiri'

Nokogiri::XML(STDIN).xpath(ARGV[0]).each do |row|
  puts row
end

XML::XPath。可以使用这个包装器:

#!/usr/bin/perl

use strict;
use warnings;
use XML::XPath;

my $root = XML::XPath->new(ioref => 'STDIN');
for my $node ($root->find($ARGV[0])->get_nodelist) {
  print($node->getData, "\n");
}

xpathfrom XML::XPath 返回太多噪音,-- NODE --并且attribute = "value".

xml_grepfrom XML::Twig 不能处理不返回元素的表达式,因此不能在没有进一步处理的情况下用于提取属性值。

编辑:

echo cat //element/@attribute | xmllint --shell filename.xml返回类似于 的噪声xpath

xmllint --xpath //element/@attribute filename.xml返回attribute = "value"

xmllint --xpath 'string(//element/@attribute)' filename.xml返回我想要的,但仅适用于第一场比赛。

对于几乎满足该问题的另一个解决方案,这里是一个 XSLT,可用于评估任意 XPath 表达式(需要 XSLT 处理器中的 dyn:evaluate 支持):

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:dyn="http://exslt.org/dynamic" extension-element-prefixes="dyn">
  <xsl:output omit-xml-declaration="yes" indent="no" method="text"/>
  <xsl:template match="/">
    <xsl:for-each select="dyn:evaluate($pattern)">
      <xsl:value-of select="dyn:evaluate($value)"/>
      <xsl:value-of select="'&#10;'"/>
    </xsl:for-each> 
  </xsl:template>
</xsl:stylesheet>

运行xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml

4

18 回答 18

302

你应该试试这些工具:

  • xmlstarlet: 可以编辑、选择、变换... 默认没有安装,xpath1
  • xmllint:通常默认安装libxml2-utils,xpath1(检查我的包装器--xpath打开非常旧的版本和换行符分隔的输出(v < 2.9.9)
  • xpath: 通过 perl 的模块安装XML::XPath,xpath1
  • xml_grep:通过 perl 的模块安装XML::Twig,xpath1(有限的 xpath 使用)
  • xidel: xpath3
  • saxon-lint:我自己的项目,包装 @Michael Kay 的 Saxon-HE Java 库,xpath3

xmllint自带libxml2-utils(可与--shell开关交互shell)

xmlstarletxmlstarlet

xpath自带perl的模块XML::Xpath

xml_grep自带perl的模块XML::Twig

xidelxidel

saxon-lint使用SaxonHE 9.6 , XPath 3.x (+retro 兼容性)

前任 :

xmllint --xpath '//element/@attribute' file.xml
xmlstarlet sel -t -v "//element/@attribute" file.xml
xpath -q -e '//element/@attribute' file.xml
xidel -se '//element/@attribute' file.xml
saxon-lint --xpath '//element/@attribute' file.xml

.

于 2013-03-17T14:19:46.490 回答
24

你也可以试试我的Xidel。它不在存储库中的包中,但您可以从网页下载它(它没有依赖项)。

它具有用于此任务的简单语法:

xidel filename.xml -e '//element/@attribute' 

它是支持 XPath 2 的少数工具之一。

于 2013-03-17T15:08:48.880 回答
16

一个很可能已经安装在系统上的软件包是python-lxml. 如果是这样,则无需安装任何额外的软件包即可:

python -c "from lxml.etree import parse; from sys import stdin; print('\n'.join(parse(stdin).xpath('//element/@attribute')))"
于 2013-03-18T07:05:58.277 回答
10

Saxon 不仅对 XPath 2.0 这样做,而且对 XQuery 1.0 和(在商业版本中)3.0 也这样做。它不是作为 Linux 软件包提供的,而是作为 jar 文件提供的。语法(您可以轻松地将其包装在一个简单的脚本中)是

java net.sf.saxon.Query -s:source.xml -qs://element/attribute

2020 更新

Saxon 10.0 包括 Gizmo 工具,可以从命令行交互或批量使用。例如

java net.sf.saxon.Gizmo -s:source.xml
/>show //element/@attribute
/>quit
于 2013-03-17T16:28:28.710 回答
10

在我搜索查询 maven pom.xml 文件时,我遇到了这个问题。但是我有以下限制:

  • 必须跨平台运行。
  • 必须存在于所有主要的 linux 发行版上,无需任何额外的模块安装
  • 必须处理复杂的 xml 文件,例如 maven pom.xml 文件
  • 简单的语法

我已经尝试了上述许多方法但没有成功:

  • python lxml.etree 不是标准 python 发行版的一部分
  • xml.etree 是但不能很好地处理复杂的 maven pom.xml 文件,还没有深入挖掘
  • python xml.etree 不处理 maven pom.xml 文件,原因不明
  • xmllint 也不起作用,核心转储经常在 ubuntu 12.04 “xmllint:使用 libxml 版本 20708”

我遇到的稳定、简短且可在许多平台上工作且成熟的解决方案是 ruby​​ 中内置的 rexml lib:

ruby -r rexml/document -e 'include REXML; 
     puts XPath.first(Document.new($stdin), "/project/version/text()")' < pom.xml

启发我找到这篇文章的是以下文章:

于 2014-05-13T17:50:33.010 回答
5

您可能还对xsh感兴趣。它具有交互模式,您可以在其中对文档进行任何您喜欢的操作:

open 1.xml ;
ls //element/@id ;
for //p[@class="first"] echo text() ;
于 2013-03-17T14:34:46.000 回答
5

clacke 的回答很好,但我认为只有当您的源代码是格式良好的 XML 而不是普通的 HTML 时才有效。

所以要对普通的 Web 内容做同样的事情——HTML 文档不一定是格式良好的 XML:

echo "<p>foo<div>bar</div><p>baz" | python -c "from sys import stdin; \
from lxml import html; \
print '\n'.join(html.tostring(node) for node in html.parse(stdin).xpath('//p'))"

并且改为使用 html5lib(以确保您获得与 Web 浏览器相同的解析行为——因为与浏览器解析器一样,html5lib 符合 HTML 规范中的解析要求)。

echo "<p>foo<div>bar</div><p>baz" | python -c "from sys import stdin; \
import html5lib; from lxml import html; \
doc = html5lib.parse(stdin, treebuilder='lxml', namespaceHTMLElements=False); \
print '\n'.join(html.tostring(node) for node in doc.xpath('//p'))
于 2016-02-17T01:44:21.817 回答
4

与 Mike 和 clacke 的答案类似,这里是 python one-liner(使用 python >= 2.5)从 pom.xml 文件中获取构建版本,它绕过了 pom.xml 文件通常没有 dtd 或默认命名空间,所以在 libxml 中看起来格式不正确:

python -c "import xml.etree.ElementTree as ET; \
  print(ET.parse(open('pom.xml')).getroot().find('\
  {http://maven.apache.org/POM/4.0.0}version').text)"

在 Mac 和 Linux 上测试,不需要安装任何额外的软件包。

于 2015-03-20T23:53:24.977 回答
2

除了XML::XSHXML::XSH2 之外,还有一些类似grep- 的实用程序很像App::xml_grep2and XML::Twig(包括xml_grep而不是xml_grep2)。Makefile在处理大型或大量 XML 文件以实现快速单线或目标 时,这些可能非常有用。当您想要比您提供的更多处理时,使用脚本方法特别XML::Twig好。perl$SHELLxmllint xstlproc

应用程序名称中的编号方案表明“2”版本是本质上相同工具的更新/更新版本,可能需要其他模块(或perl本身)的更新版本。

于 2014-03-05T16:37:31.380 回答
2

值得一提的是 nokogiri 本身附带了一个命令行工具,该工具应与gem install nokogiri.

您可能会发现这篇博文很有用

于 2015-12-23T07:29:57.510 回答
2

我已经尝试了几个命令行 XPath 实用程序,当我意识到我花了太多时间在谷歌上搜索并弄清楚它们是如何工作的,所以我用 Python 编写了最简单的 XPath 解析器,它可以满足我的需要。

如果 XPath 表达式计算为字符串,则下面的脚本显示字符串值,如果结果是节点,则显示整个 XML 子节点:

#!/usr/bin/env python
import sys
from lxml import etree

tree = etree.parse(sys.argv[1])
xpath = sys.argv[2]

for e in tree.xpath(xpath):

    if isinstance(e, str):
        print(e)
    else:
        print((e.text and e.text.strip()) or etree.tostring(e))

它使用lxml- 一个用 C 语言编写的快速 XML 解析器,它不包含在标准 python 库中。安装它pip install lxml。在 Linux/OSX 上可能需要前缀sudo.

用法:

python xmlcat.py file.xml "//mynode"

lxml 也可以接受一个 URL 作为输入:

python xmlcat.py http://example.com/file.xml "//mynode" 

提取附件节点下的 url 属性,即<enclosure url="http:...""..>)

python xmlcat.py xmlcat.py file.xml "//enclosure/@url"

谷歌浏览器中的 Xpath

作为一个不相关的附注:如果您想对网页的标记运行 XPath 表达式,那么您可以直接从 Chrome 开发工具中执行:右键单击 Chrome 中的页面 > 选择检查,然后在开发工具中控制台将您的 XPath 表达式粘贴为$x("//spam/eggs").

获取此页面上的所有作者:

$x("//*[@class='user-details']/a/text()")
于 2017-11-07T19:48:26.630 回答
2

这是一个 xmlstarlet 用例,用于从嵌套元素 elem1、elem2 中提取数据到此类 XML 中的一行文本(还展示了如何处理命名空间):

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<mydoctype xmlns="http://xml-namespace-uri" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xml-namespace-uri http://xsd-uri" format="20171221A" date="2018-05-15">

  <elem1 time="0.586" length="10.586">
      <elem2 value="cue-in" type="outro" />
  </elem1>

</mydoctype>

输出将是

0.586 10.586 cue-in outro

在这个片段中,-m 匹配嵌套的 elem2,-v 输出属性值(带有表达式和相对寻址),-o 文字文本,-n 添加一个换行符:

xml sel -N ns="http://xml-namespace-uri" -t -m '//ns:elem1/ns:elem2' \
 -v ../@time -o " " -v '../@time + ../@length' -o " " -v @value -o " " -v @type -n file.xml

如果需要来自 elem1 的更多属性,可以这样做(也显示 concat() 函数):

xml sel -N ns="http://xml-namespace-uri" -t -m '//ns:elem1/ns:elem2/..' \
 -v 'concat(@time, " ", @time + @length, " ", ns:elem2/@value, " ", ns:elem2/@type)' -n file.xml

请注意命名空间(ns,用 -N 声明)的(IMO 不必要的)复杂性,这让我几乎放弃了 xpath 和 xmlstarlet,并编写了一个快速的 ad-hoc 转换器。

于 2018-05-15T11:21:17.020 回答
2

我的 Python 脚本xgrep.py正是这样做的。为了搜索filesattribute中元素的所有属性,您可以按如下方式运行它:elementfilename.xml ...

xgrep.py "//element/@attribute" filename.xml ...

有各种用于控制输出的开关,例如-c计数匹配、-i缩进匹配部分以及-l仅输出文件名。

该脚本不能作为 Debian 或 Ubuntu 软件包使用,但它的所有依赖项都是可用的。

于 2019-08-22T17:27:42.013 回答
2

安装BaseX数据库,然后像这样使用它的“独立命令行模式”

basex -i - //element@attribute < filename.xml

或者

basex -i filename.xml //element@attribute

查询语言实际上是 XQuery (3.0),而不是 XPath,但由于 XQuery 是 XPath 的超集,您可以使用 XPath 查询而无需注意。

于 2020-06-29T08:34:48.870 回答
1

由于这个项目显然是相当新的,请查看https://github.com/jeffbr13/xq,似乎是一个包装器lxml,但这就是你真正需要的(并在其他答案中使用 lxml 发布了临时解决方案)

于 2018-04-20T00:06:40.007 回答
1

我对用于 HTML XPath 查询的 Python 单行代码不满意,所以我自己编写了代码。假设您安装了python-lxml包或运行pip install --user lxml

function htmlxpath() { python -c 'for x in __import__("lxml.html").html.fromstring(__import__("sys").stdin.read()).xpath(__import__("sys").argv[1]): print(x)' $1 }

一旦你有了它,你可以像在这个例子中一样使用它:

> curl -s https://slashdot.org | htmlxpath '//title/text()'
Slashdot: News for nerds, stuff that matters
于 2020-03-17T22:19:52.043 回答
0

很抱歉成为战斗中的另一个声音。我尝试了这个线程中的所有工具,发现它们都不能满足我的需求,所以我自己写了。你可以在这里找到它:https ://github.com/charmparticle/xpe

它已上传到 pypi,因此您可以使用 pip3 轻松安装它,如下所示:

sudo pip3 install xpe

安装后,您可以使用它来针对各种输入运行 xpath 表达式,其灵活性与在 selenium 或 javascript 中使用 xpath 所获得的灵活性相同。是的,你可以使用 xpaths 来对抗 HTML。

于 2021-02-04T21:02:56.007 回答
0

即使在顶部存在命名空间声明时也可以使用的解决方案:

如果 xml 在顶部声明了命名空间,则答案中提出的大多数命令都不能开箱即用。考虑一下:

输入xml:

<elem1 xmlns="urn:x" xmlns:prefix="urn:y">
    <elem2 attr1="false" attr2="value2">
        elem2 value
    </elem2>
    <elem2 attr1="true" attr2="value2.1">
        elem2.1 value
    </elem2>    
    <prefix:elem3>
        elem3 value
    </prefix:elem3>        
</elem1>

不工作:

xmlstarlet sel -t -v "/elem1" input.xml
# nothing printed
xmllint -xpath "/elem1" input.xml
# XPath set is empty

解决方案:

# Requires >=java11 to run like below

# Prints the contents and self of "elem1"
java ExtractXpath.java "/elem1" input.xml

# Prints the value of the attribute: "value2.1"
java ExtractXpath.java "/elem1/elem2/@attr2" input.xml

# Prints the values inside elemt3: "elem3 value"
java ExtractXpath.java "/elem1/elem3/text()" input.xml

提取Xpath.java:

import java.io.File;
import java.io.FileInputStream;
import java.io.StringWriter;
import java.util.Iterator;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ExtractXpath {

    public static void main(String[] args) throws Exception {
        assertThat(args.length==2, "Wrong number of args");
        String xpath = args[0];
        File file = new File(args[1]);
                
        assertThat(file.isFile(), file.getAbsolutePath()+" is not a file.");
        FileInputStream fileIS = new FileInputStream(file);
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = builderFactory.newDocumentBuilder();
        Document xmlDocument = builder.parse(fileIS);
        XPath xPath = XPathFactory.newInstance().newXPath();
        String expression = xpath;
        NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
        
        for (int i = 0; i < nodeList.getLength(); i++) {
            System.out.println("==MATCH_START==\n" + convertNodeToString(nodeList.item(i)) + "\n==MATCH_END==");
        }        
    }

    private static String convertNodeToString(Node node) throws TransformerConfigurationException, TransformerException {
            short nType = node.getNodeType();
        switch (nType) {
            case Node.ATTRIBUTE_NODE , Node.TEXT_NODE -> {
                return node.getNodeValue();
            }
            case Node.ELEMENT_NODE -> {
                StringWriter writer = new StringWriter();
                Transformer trans = TransformerFactory.newInstance().newTransformer();
                trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
                trans.setOutputProperty(OutputKeys.INDENT, "yes");
                trans.transform(new DOMSource(node), new StreamResult(writer));
                return writer.toString();
            }
            default -> {
                System.err.println("WARNING: FIXME: Node type:"+nType+" could possibly be handled in a better way.");
                return node.getNodeValue();
            }
                
        }
    }

    
    private static void assertThat(boolean b, String msg) {
        if(!b){
            System.err.println(msg+"\n\nUSAGE: program xpath xmlFile");
            System.exit(-1);
        }
    }
}

@SuppressWarnings("unchecked")
class NamespaceResolver implements NamespaceContext {
    //Store the source document to search the namespaces
    private final Document sourceDocument;
    public NamespaceResolver(Document document) {
        sourceDocument = document;
    }

    //The lookup for the namespace uris is delegated to the stored document.
    @Override
    public String getNamespaceURI(String prefix) {
        if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
            return sourceDocument.lookupNamespaceURI(null);
        } else {
            return sourceDocument.lookupNamespaceURI(prefix);
        }
    }

    @Override
    public String getPrefix(String namespaceURI) {
        return sourceDocument.lookupPrefix(namespaceURI);
    }

    @SuppressWarnings("rawtypes")
    @Override
    public Iterator getPrefixes(String namespaceURI) {
        return null;
    }
}

为简单起见:

xpath-extract命令:

#!/bin/bash
java ExtractXpath.java "$1" "$2"

于 2022-02-10T17:41:56.990 回答