How to use XPath in Python?
Asked Answered
S

11

258

What are the libraries that support XPath? Is there a full implementation? How is the library used? Where is its website?

Slobbery answered 12/8, 2008 at 11:28 Comment(4)
I have this sneaky suspicion that the answers to this question are a bit stale now.Nursemaid
The answer by @gringo-suave looks like a good update. https://mcmap.net/q/109357/-how-to-use-xpath-in-pythonIntermigration
Scrapy offers XPath selectors.Nashoma
As @WarrenP says, most of the answers here are extremely stale old Python-2.x, really out of date. Maybe this question should be tagged python-2.xLowercase
W
137

libxml2 has a number of advantages:

  1. Compliance to the spec
  2. Active development and a community participation
  3. Speed. This is really a python wrapper around a C implementation.
  4. Ubiquity. The libxml2 library is pervasive and thus well tested.

Downsides include:

  1. Compliance to the spec. It's strict. Things like default namespace handling are easier in other libraries.
  2. Use of native code. This can be a pain depending on your how your application is distributed / deployed. RPMs are available that ease some of this pain.
  3. Manual resource handling. Note in the sample below the calls to freeDoc() and xpathFreeContext(). This is not very Pythonic.

If you are doing simple path selection, stick with ElementTree ( which is included in Python 2.5 ). If you need full spec compliance or raw speed and can cope with the distribution of native code, go with libxml2.

Sample of libxml2 XPath Use


import libxml2

doc = libxml2.parseFile("tst.xml")
ctxt = doc.xpathNewContext()
res = ctxt.xpathEval("//*")
if len(res) != 2:
    print "xpath query: wrong node set size"
    sys.exit(1)
if res[0].name != "doc" or res[1].name != "foo":
    print "xpath query: wrong node set value"
    sys.exit(1)
doc.freeDoc()
ctxt.xpathFreeContext()

Sample of ElementTree XPath Use


from elementtree.ElementTree import ElementTree
mydoc = ElementTree(file='tst.xml')
for e in mydoc.findall('/foo/bar'):
    print e.get('title').text

Whoa answered 26/8, 2008 at 13:6 Comment(4)
using python 2.7.10 on osx I had to import ElementTree as from xml.etree.ElementTree import ElementTreeAdditive
because it is a C wrapper you might find difficulty deploying it to AWS Lambda unless you compile on an EC2 instance or a Docker image of AWS LinuxFlinger
Strictness is not a downside.Dasi
libxml2 isn't built-in to Python, is it? How would one do this with the built-in xml libraries?Screech
K
87

The lxml package supports xpath. It seems to work pretty well, although I've had some trouble with the self:: axis. There's also Amara, but I haven't used it personally.

Khasi answered 12/8, 2008 at 11:40 Comment(3)
amara's pretty nice, and one doesn't always need xpath.Ripe
Please add some basic details about how to use XPath with lxml.Waller
There's a standard library solution. I prefer fewer dependencies. See Gringo Suave's answer.Trudge
O
82

Sounds like an lxml advertisement in here. ;) ElementTree is included in the std library. Under 2.6 and below its xpath is pretty weak, but in 2.7+ and 3.x much improved:

import xml.etree.ElementTree as ET

root = ET.parse(filename)
result = ''

for elem in root.findall('.//child/grandchild'):
    # How to make decisions based on attributes:
    if elem.attrib.get('name') == 'foo':
        result = elem.text
        break
Offwhite answered 22/11, 2012 at 1:5 Comment(0)
S
40

Use LXML. LXML uses the full power of libxml2 and libxslt, but wraps them in more "Pythonic" bindings than the Python bindings that are native to those libraries. As such, it gets the full XPath 1.0 implementation. Native ElemenTree supports a limited subset of XPath, although it may be good enough for your needs.

Splash answered 13/11, 2009 at 23:11 Comment(0)
B
32

Another option is py-dom-xpath, it works seamlessly with minidom and is pure Python so works on appengine.

import xpath
xpath.find('//item', doc)
Beestings answered 23/1, 2010 at 9:30 Comment(3)
Easier than lxml and libxml2 if you're already working with minidom. Works beautifully and is more "Pythonic". The context in the find function let you use another xpath result as a new search context.Sweetheart
I too have been using py-dom-xpath as I write a plugin, because it's pure python. But I don't think it's maintained anymore, and be aware of this bug ("Cannot access an element whose name is 'text'"): code.google.com/p/py-dom-xpath/issues/detail?id=8Open
py-dom-xpath seems to have been mothballed years ago in 2010, please at minimum edit this into your answer.Lowercase
L
15

You can use:

PyXML:

from xml.dom.ext.reader import Sax2
from xml import xpath
doc = Sax2.FromXmlFile('foo.xml').documentElement
for url in xpath.Evaluate('//@Url', doc):
  print url.value

libxml2:

import libxml2
doc = libxml2.parseFile('foo.xml')
for url in doc.xpathEval('//@Url'):
  print url.content
Lumen answered 23/8, 2010 at 13:0 Comment(1)
when I try the PyXML code, I got ImportError: No module named ext from from xml.dom.ext.reader import Sax2Falciform
F
12

You can use the simple soupparser from lxml

Example:

from lxml.html.soupparser import fromstring

tree = fromstring("<a>Find me!</a>")
print tree.xpath("//a/text()")
Falciform answered 15/11, 2015 at 5:31 Comment(2)
What difference does using soupparser make?Bioplasm
It's just an alternativeFalciform
R
9

The latest version of elementtree supports XPath pretty well. Not being an XPath expert I can't say for sure if the implementation is full but it has satisfied most of my needs when working in Python. I've also use lxml and PyXML and I find etree nice because it's a standard module.

NOTE: I've since found lxml and for me it's definitely the best XML lib out there for Python. It does XPath nicely as well (though again perhaps not a full implementation).

Ranson answered 14/8, 2008 at 9:48 Comment(1)
ElementTree's XPath support is currently minimal at best. There are huge gaping holes in functionality, such as the lack of attribute selectors, no non-default axes, no child indexing, etc. Version 1.3 (in alpha) adds some of these features, but it's still an unashamedly partial implementation.Tine
S
9

If you want to have the power of XPATH combined with the ability to also use CSS at any point you can use parsel:

>>> from parsel import Selector
>>> sel = Selector(text=u"""<html>
        <body>
            <h1>Hello, Parsel!</h1>
            <ul>
                <li><a href="http://example.com">Link 1</a></li>
                <li><a href="http://scrapy.org">Link 2</a></li>
            </ul
        </body>
        </html>""")
>>>
>>> sel.css('h1::text').extract_first()
'Hello, Parsel!'
>>> sel.xpath('//h1/text()').extract_first()
'Hello, Parsel!'
Spelter answered 16/12, 2017 at 22:16 Comment(2)
how should my Xpath look like if I want to get "Link 1" and "Link 2"?Loris
for getting the text, it should be something like //li/a/text()Spelter
P
3

Another library is 4Suite: http://sourceforge.net/projects/foursuite/

I do not know how spec-compliant it is. But it has worked very well for my use. It looks abandoned.

Petrol answered 23/8, 2010 at 12:57 Comment(0)
T
2

PyXML works well.

You didn't say what platform you're using, however if you're on Ubuntu you can get it with sudo apt-get install python-xml. I'm sure other Linux distros have it as well.

If you're on a Mac, xpath is already installed but not immediately accessible. You can set PY_USE_XMLPLUS in your environment or do it the Python way before you import xml.xpath:

if sys.platform.startswith('darwin'):
    os.environ['PY_USE_XMLPLUS'] = '1'

In the worst case you may have to build it yourself. This package is no longer maintained but still builds fine and works with modern 2.x Pythons. Basic docs are here.

Temptation answered 12/8, 2008 at 19:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.