Kabuktan XPath tek katmanları nasıl çalıştırılır?


192

Ubuntu ve / veya CentOS için, bir XPath tek satırını çalıştırabilen foo //element@attribute filename.xmlveya foo //element@attribute < filename.xmlsonuçları satır satır döndürebilen bir komut satırı aracına sahip bir paket var mı?

Sadece apt-get install fooya da yum install foosonra kutudan çıkar çıkmama izin verecek bir şey arıyorum , ambalaj yok ya da başka bir uyarlamaya gerek yok.

Yaklaşan şeylere bazı örnekler:

Nokogiri. Bu sarmalayıcıyı yazarsam, sarmalayıcıyı yukarıda açıklanan şekilde çağırabilirim:

#!/usr/bin/ruby

require 'nokogiri'

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

XML :: XPath. Bu sarmalayıcı ile çalışır:

#!/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");
}

xpathXML :: XPath'dan çok fazla gürültü geliyor -- NODE --ve attribute = "value".

xml_grep from XML :: Twig öğeleri döndürmeyen ifadeleri işleyemez, bu nedenle daha fazla işlem yapmadan öznitelik değerlerini ayıklamak için kullanılamaz.

DÜZENLE:

echo cat //element/@attribute | xmllint --shell filename.xmlbenzer gürültüyü döndürür xpath.

xmllint --xpath //element/@attribute filename.xmldöner attribute = "value".

xmllint --xpath 'string(//element/@attribute)' filename.xml istediğimi döndürüyor, ama sadece ilk maç için.

Soruyu neredeyse tatmin eden başka bir çözüm için, rastgele XPath ifadelerini değerlendirmek için kullanılabilecek bir XSLT (dyn gerektirir: XSLT işlemcideki desteği değerlendirin):

<?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>

İle çalıştırın xsltproc --stringparam pattern //element/@attribute --stringparam value . arbitrary-xpath.xslt filename.xml.


Her biri yeni bir satıra birden çok sonuç yazdırmak için basit ve güvenilir bir yol bulma hakkındaki beyin fırtınası için +1
Gilles Quenot

1
"Gürültü" xpathSTDOUT değil, STDERR üzerinde olduğunu unutmayın .
miken32

@ miken32 Hayır. Yalnızca çıktı için değer istedim. hastebin.com/ekarexumeg.bash
clacke

Yanıtlar:


271

Bu araçları denemelisiniz:

  • xmlstarlet : düzenleyebilir, seçebilir, dönüştürebilir ... Varsayılan olarak yüklenmez, xpath1
  • xmllint: genellikle varsayılan olarak libxml2-utilsxpath1 ile kurulur ( çok eski sürümleri ve sınırlandırılmış çıktıları sınırlandırmak için sarma makinemi kontrol edin --xpath(v <2.9.9)
  • xpath: perl modülü XML::XPath, xpath1 üzerinden kurulur
  • xml_grep: perl modülü ile kurulmuş XML::Twig, xpath1 (sınırlı xpath kullanımı)
  • xidel: xpath3
  • saxon-lint : kendi projem, Michael Kay'ın Saxon-HE Java kütüphanesi, xpath3

xmllintile birlikte gelir libxml2-utils( --shellanahtarla etkileşimli kabuk olarak kullanılabilir )

xmlstarletolduğunu xmlstarlet.

xpath perl modülü ile birlikte gelir XML::Xpath

xml_grep perl modülü ile birlikte gelir XML::Twig

xidel dır-dir xidel

saxon-lintkullanılarak SaxonHE 9.6 , XPath 3.x (+ geriye uyumluluk)

Örn:

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

.


7
Mükemmel! xmlstarlet sel -T -t -m '//element/@attribute' -v '.' -n filename.xmltam olarak istediğimi yapıyor!
clacke

2
Not: xmlstarlet'in terk edileceği söylendi, ancak şimdi tekrar aktif geliştirme aşamasında.
clacke

6
Not: Bazı eski sürümleri xmllintkomut satırı bağımsız değişkenini desteklemez --xpath, ancak çoğu destekliyor gibi görünür --shell. Hafif kir çıktısı, ancak yine de bir bağlanmada yararlıdır.
kevinarpe

Hala bir öznitelik değil, düğüm içeriğini sorgulamada sorun var gibi görünüyor. Birisi buna bir örnek verebilir mi? Nedense, hala xmlstarlet anlamak ve eşleştirme, değer, sadece belge yapısını görüntülemek için kök vb arasında doğru almak zor buluyorum sel -t -m ... -v .... Bu sayfadan ilk örnek olsa bile : arstechnica.com/information-technology/2005 / 11 / linux-20051115/2 , son düğüm hariç tümünü eşleştirme ve bunu kullanım durumum gibi değer ifadesi için kaydetme, hala göremiyorum, sadece boş çıktı
alıyorum

xpath sürümü üzerinde güzel bir - Ben sadece başka türlü mükemmel xmllint bu sınırlama koşmak
JonnyRaa

20

Ayrıca Xidel'imi de deneyebilirsiniz . Depodaki bir pakette değil, ancak sadece web sayfasından indirebilirsiniz (bağımlılığı yoktur).

Bu görev için basit bir sözdizimi vardır:

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

Ve XPath 2'yi destekleyen bu araçlardan nadir görülenlerden biridir.


2
Xidel oldukça havalı görünüyor, ancak muhtemelen bu aracın önerdiğiniz yazarı olduğunuzu da belirtmelisiniz.
SinirliWithFormsDesigner

1
Sakson ve sakson-tiftik kullanımı xpath3;)
Gilles Quenot

Xidel (0..8.win32.zip), Virustotal'da kötü amaçlı yazılım olduğunu gösteriyor. Kendi riskiniz altında
JGFMK

harika - Kişisel anahtar alet kutusuna
xidel'i ekleyeceğim

15

Bir sisteme kurulması muhtemel bir paket zaten python-lxml. Eğer öyleyse, bu ekstra bir paket kurmadan mümkündür:

python -c "from lxml.etree import parse; from sys import stdin; print '\n'.join(parse(stdin).xpath('//element/@attribute'))"

1
Dosya adı nasıl aktarılır?
Ramakrishnan Kannan

4
Bu çalışıyor stdin. Bu, zaten oldukça uzun bir tek astar dahil etme open()ve close()içine alma ihtiyacını ortadan kaldırır . Bir dosyayı ayrıştırmak için çalıştırın python -c "from lxml.etree import parse; from sys import stdin; print '\n'.join(parse(stdin).xpath('//element/@attribute'))" < my_file.xmlve kabuğunuzun dosya arama, açma ve kapama işlemesine izin verin.
clacke

10

Maven pom.xml dosyalarını sorgulamak için yaptığım aramada bu soruya rastladım. Ancak, aşağıdaki sınırlamalar vardı:

  • platformlar arası çalıştırılmalıdır.
  • herhangi bir ek modül kurulumu olmadan tüm büyük linux dağıtımlarında bulunmalıdır
  • maven pom.xml dosyaları gibi karmaşık xml dosyalarını işlemelidir
  • basit sözdizimi

Yukarıdakilerin çoğunu başarıyla denedim:

  • python lxml.etree standart python dağıtımının bir parçası değildir
  • xml.etree ancak karmaşık maven pom.xml dosyalarını iyi işlemez, yeterince derin kazmadı
  • python xml.etree, maven pom.xml dosyalarını bilinmeyen bir nedenden dolayı işlemez
  • xmllint de çalışmıyor, ubuntu 12.04 "çekirdek dökümü sık sık xmllint: libxml sürüm 20708 kullanarak"

Karşılaştığım çözüm kararlı, kısa ve birçok platformda çalışıyor ve olgunlaşmış olan yakutta rexml lib yerleşik:

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

Bunu bulmam için bana ilham veren şey şu makalelerdi:


1
Bu sorudan bile daha dar kriterler, bu yüzden kesinlikle bir cevap olarak uyuyor. Durumunuza giren birçok insana araştırmanızda yardımcı olacağından eminim. xmlstarletKabul edilen cevap olarak saklıyorum , çünkü daha geniş kriterlerime uyuyor ve gerçekten düzenli . Ama muhtemelen çözümünüzü zaman zaman kullanacağım.
clacke

2
Ben o eklersiniz sonucu çevresindeki tırnak önlemek kullanmak putsyerine pYakut komuta.
TomG

10

Saxon bunu sadece XPath 2.0 için değil, aynı zamanda XQuery 1.0 ve (ticari versiyonda) 3.0 için de yapacak. Bir Linux paketi olarak değil, bir kavanoz dosyası olarak gelir. Sözdizimi (basit bir komut dosyasında kolayca sarabilirsiniz)

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

2020 GÜNCELLEME

Saxon 10.0, etkileşimli olarak veya komut satırından toplu olarak kullanılabilen Gizmo aracını içerir. Örneğin

java net.sf.saxon.Gizmo -s:source.xml
/>show //element/@attribute
/>quit

SaxonB Ubuntu, paket libsaxonb-java, ama ben çalışırsam , örneğin ile aynı sorunu saxonb-xquery -qs://element/@attribute -s:filename.xmlalıyorum . SENR0001: Cannot serialize a free-standing attribute nodexml_grep
clacke

3
Bu sorgu tarafından seçilen öznitelik düğümünün tüm ayrıntılarını görmek istiyorsanız, komut satırında -wrap seçeneğini kullanın. Yalnızca özniteliğin dize değerini istiyorsanız, sorguya / string () ekleyin.
Michael Kay

Teşekkürler. / String () eklemek yaklaşıyor. Ancak bir XML başlığı çıkarır ve tüm sonuçları bir satıra koyar, bu yüzden hala puro olmaz.
clacke

2
XML üstbilgisi istemiyorsanız! Method = text seçeneğini ekleyin.
Michael Kay

İsim alanını kullanmak için şunu ekleyin -qs:'-qs:declare namespace mets="http://www.loc.gov/METS/";/mets:mets/mets:dmdSec'
igo

5

Ayrıca ilginizi çekebilir xsh . Dokümanla istediğiniz her şeyi yapabileceğiniz etkileşimli bir moda sahiptir:

open 1.xml ;
ls //element/@id ;
for //p[@class="first"] echo text() ;

Bir paket olarak mevcut görünmüyor, en azından Ubuntu'da yok.
clacke

1
@clacke: Değil, ancak tarafından CPAN'dan kurulabilir cpan XML::XSH2.
choroba

@choroba, OS X'te denedim, ancak bir tür makefile hatasıyla yüklenemedi.
cnst

@cnst: Yüklü XML :: LibXML var mı?
choroba

@choroba, bilmiyorum; ama benim açımdan, cpan XML::XSH2hiçbir şey kuramaz.
cnst

5

clacke'nin cevabı harika ama bence sadece kaynağınız normal HTML değil iyi biçimlendirilmiş XML ise işe yarıyor.

Normal Web içeriği için aynısını yapmak için, mutlaka iyi biçimlendirilmiş XML olması gerekmeyen HTML dokümanları:

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'))"

Bunun yerine html5lib kullanmak için (Web tarayıcılarıyla aynı ayrıştırma davranışını elde etmenizi sağlamak için - tarayıcı ayrıştırıcıları gibi, html5lib de HTML spesifikasyonundaki ayrıştırma gereksinimlerine uygundur).

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'))

Evet, soruda XPath'ın XML'i ima ettiği varsayımından düştüm. Bu cevap, buradakileri iyi bir şekilde tamamlıyor ve bana html5lib hakkında bilgi verdiğiniz için teşekkürler!
clacke

3

Mike ve clacke'nin cevaplarına benzer şekilde, pom.xml dosyalarının normalde bir dtd'si olmadığı gerçeğini ortaya çıkaran bir pom.xml dosyasından derleme sürümünü almak için python tek astar (python> = 2.5 kullanarak). varsayılan ad alanı, bu nedenle libxml için iyi biçimlendirilmiş olarak görünmeyin:

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 ve Linux'ta test edilmiştir ve ek paketlerin yüklenmesi gerekmez.


2
Bunu bugün kullandım! Yapı sunucularımızda lxmlne xmllint, ne de Ruby vardı. Kendi cevabımdaki biçimin ruhuna göre, bash'ta olduğu gibi yazdım python3 -c "from xml.etree.ElementTree import parse; from sys import stdin; print(parse(stdin).find('.//element[subelement=\"value\"]/othersubelement').text)" <<< "$variable_containing_xml". .getroot()gerekli görünmüyor.
clacke

2

XML :: XSH ve XML :: XSH2'ye ek grepolarak App::xml_grep2ve gibi emmek gibi bazı yardımcı programlar da vardır XML::Twig(bunun xml_grepyerine xml_grep2). Bunlar, hızlı onelinerler veya Makefilehedefler için büyük veya çok sayıda XML dosyası üzerinde çalışırken oldukça yararlı olabilir . XML::Twigözellikle perlsizin $SHELLve xmllint xstlprocsunduğunuzdan biraz daha fazla işlem yapmak istediğinizde komut dosyası oluşturma yaklaşımı için çalışmak güzeldir .

Uygulama adlarındaki numaralandırma şeması, "2" sürümlerinin, diğer modüllerin (veya perlkendisinin) sonraki sürümlerini gerektirebilecek temelde aynı aracın daha yeni / sonraki sürümleri olduğunu gösterir .


xml_grep2 -t //element@attribute filename.xmlçalışır ve beklediğim şeyi yapar ( xml_grep --root //element@attribute --text_only filename.xmlyine de, "tanınmayan bir ifade" hatası döndürür). Harika!
clacke

Ne olmuş xml_grep --pretty_print --root '//element[@attribute]' --text_only filename.xml? Orada neler olup bittiğinden veya XPath'in []bu durumda ne söylediğinden emin değilim , ancak @attributeköşeli parantezleri çevrelemek xml_grepve için çalışıyor xml_grep2.
G. Cito

Yani //element/@attribute, değil //element@attribute. Görünüşe göre düzenlenemiyor, ancak bu tartışmanın geçmişini karıştırmamak için silmek + değiştir yerine orada bırakmak.
clacke

//element[@attribute]elementözniteliği olan tür öğelerini seçer attribute. Öğeyi istemiyorum, sadece niteliği. <element attribute='foo'/>bana vermeli foo, dolu değil <element attribute='foo'/>.
clacke

... ve --text_onlybu bağlamda, <element attribute='foo'/>içinde metin düğümü olmayan bir öğe durumunda bana boş bir dize veriyor .
clacke


2

Birkaç komut satırı XPath yardımcı programını denedim ve çok fazla zaman harcadığımı ve nasıl çalıştıklarını anladığımı fark ettiğimde, Python'da mümkün olan en basit XPath ayrıştırıcısını yazdım.

Aşağıdaki komut dosyası, XPath ifadesi bir dizeyi değerlendirirse dize değerini veya sonuç bir düğümse tüm XML alt düğümünü gösterir:

#!/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))

lxmlStandart python kütüphanesinde bulunmayan C ile yazılmış hızlı bir XML ayrıştırıcı kullanır . İle yükleyin pip install lxml. Linux / OSX üzerinde önek ile gerekebilir sudo.

Kullanımı:

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

lxml ayrıca bir URL'yi giriş olarak kabul edebilir:

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

Url özelliğini bir muhafaza düğümü altında ayıklayın, yani <enclosure url="http:...""..>):

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

Google Chrome'da Xpath

İlişkisiz bir yan not olarak: Şans eseri bir web sayfasının işaretlemesine karşı bir XPath ifadesi çalıştırmak istiyorsanız, bunu doğrudan Chrome cihazlarından yapabilirsiniz: Chrome'daki sayfayı sağ tıklayın> İncele'yi seçin ve ardından DevTools'ta konsol XPath ifadenizi olarak yapıştırın $x("//spam/eggs").

Bu sayfadaki tüm yazarları edinin:

$x("//*[@class='user-details']/a/text()")

Değil bir tek satırlık ve lxmlzaten sözü edilen iki diğer cevaplar seninkinden önce yıllar.
clacke

2

İşte elem1, elem2 iç içe öğelerinden bu XML türünden bir metin satırına (ad alanlarının nasıl işleneceğini de gösteren) veri ayıklamak için bir xmlstarlet kullanım örneği:

<?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>

Çıktı

0.586 10.586 cue-in outro

Bu snippet'te, -m iç içe elem2 ile eşleşir, -v çıkışları öznitelik değerleri (ifadeler ve göreli adresleme ile), -o değişmez metin, -n yeni satır ekler:

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'den daha fazla öznitelik gerekiyorsa, bunu şu şekilde yapabilirsiniz (concat () işlevini de gösterir):

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

Nppx ve xmlstarlet'ten neredeyse vazgeçmemi ve hızlı ve geçici bir dönüştürücü yazmamı sağlayan ad alanları (ns, -N ile bildirilen) (IMO gereksiz) komplikasyonuna dikkat edin.


xmlstarlet harika, ancak kabul edilen ve ana sıralama cevabı bundan bahsediyor. Ad alanlarının nasıl işleneceğine ilişkin bilgiler, varsa yorum olarak alakalı olabilir. İsim alanları ve xmlstarlet ile ilgili sorunlar
yaşayan herkes

2
Elbette, @clacke, xmlstarlet'den birkaç kez bahsedildi, aynı zamanda kavramanın zor olduğu ve belgelenmediği. Bir saat boyunca iç içe geçmiş elemanlardan nasıl bilgi alınacağını tahmin ediyordum. Keşke bu örneği alsaydım, bu yüzden zaman kaybından başkalarından kaçınmak için buraya gönderiyorum (ve örnek bir yorum için çok uzun).
diemo

2

Python betiğim xgrep.py tam olarak bunu yapıyor. Dosyalardaki attributeöğelerin tüm özniteliklerini aramak için öğeyi aşağıdaki gibi çalıştırırsınız:elementfilename.xml ...

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

Çıkışı kontrol etmek -ciçin eşleşmelerin sayılması -i, eşleşen parçaların girintilenmesi ve -lyalnızca dosya adlarının çıkarılması gibi çeşitli anahtarlar vardır .

Komut dosyası Debian veya Ubuntu paketi olarak mevcut değildir, ancak tüm bağımlılıkları vardır.


Ve kaynakta barınıyorsun! Güzel!
clacke


1

HTML XPath sorguları için Python tek satırlarından memnun değildim, bu yüzden kendim yazdım. python-lxmlPaketi kurduğunuzu veya çalıştırdığınızı varsayar 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 }

Elinize geçtikten sonra, bu örnekte olduğu gibi kullanabilirsiniz:

> curl -s https://slashdot.org | htmlxpath '//title/text()'
Slashdot: News for nerds, stuff that matters

0

BaseX veritabanını yükleyin , ardından "bağımsız komut satırı modu" nu şu şekilde kullanın :

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

veya

basex -i filename.xml //element@attribute

Sorgu dili aslında XPath değil XQuery (3.0), ancak XQuery XPath'ın bir üst kümesi olduğundan, fark etmeden XPath sorgularını kullanabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.