I was attempting to parse example.com’s home page with xpath and cssselect but it seems as though either I don’t know how xpath works, or lxml’s xpath is broken, as it is missing matches.
Here’s the quick and dirty code.
from lxml.html import *
mySearchTree = parse('http://www.example.com').getroot()
for a in mySearchTree.cssselect('tr a'):
print 'found "%s" link to href "%s"' % (a.text, a.get('href'))
print '-'*8 +'Now for Xpath' + 8*'-'
# Find all 'a' elements inside 'tr' table rows with xpath
for a in mySearchTree.xpath('.//tr/*/a'):
print 'found "%s" link to href "%s"' % (a.text, a.get('href'))
Results:
found "About" link to href "/about/"
found "Presentations" link to href "/about/presentations/"
found "Performance" link to href "/about/performance/"
found "Reports" link to href "/reports/"
found "Domains" link to href "/domains/"
found "Root Zone" link to href "/domains/root/"
found ".INT" link to href "/domains/int/"
found ".ARPA" link to href "/domains/arpa/"
found "IDN Repository" link to href "/domains/idn-tables/"
found "Protocols" link to href "/protocols/"
found "Number Resources" link to href "/numbers/"
found "Abuse Information" link to href "/abuse/"
found "Internet Corporation for Assigned Names and Numbers" link to href "http://www.icann.org/"
--------Now for Xpath--------
found "Presentations" link to href "/about/presentations/"
found "Performance" link to href "/about/performance/"
found "Reports" link to href "/reports/"
found "Root Zone" link to href "/domains/root/"
found ".INT" link to href "/domains/int/"
found ".ARPA" link to href "/domains/arpa/"
found "IDN Repository" link to href "/domains/idn-tables/"
found "Abuse Information" link to href "/abuse/"
found "Internet Corporation for Assigned Names and Numbers" link to href "http://www.icann.org/"
Basically the xpath found every link it was supposed to, except for those that were bolded by Example.com. However, shouldn’t the asterisk wildcard have allowed for this in the xpath match ‘.//tr/*/a’?
Possibly something else is going on (I didn’t examine the sample document closely), but your CSS selector and XPath are not equivalent.
CSS
tr ais//tr//ain XPath..//tr/*/ameans (conceptually, not precisely):.: current node//: all descendants of current nodetr: all tr elements among all descendants of current node/: all children of found tr elements*: any element among children of found tr elements/: all children of any child element of found tr elementsa: all a elements which which are element children of element children of a tr elementIn other words, given the following HTML:
//ul/*/awill only match link1.XPath Primer
In reality, an "XPath" is a series of Location Steps separated by slashes. A Location Step consists of:
node(),text())[]. A node is only matched if all the predicates are true.)If we were to decompose
.//tr/*/ainto its Location Steps it would look like this:.tr*aIt’s probably not evident what the heck I am talking about. This is because XPath has an abbreviated syntax. Here is the expression with abbreviations expanded (axis and node-test are separated by a
::, steps by a/):self::node()/descendent-or-self::node()/child::tr/child::*/child::a(Notice that
self::node()is redundant.)Conceptually what happens in a step is:
Note that this is still a simplification. Read the XPath Standard for the gory details if you want them.