Я тестирую следующий тестовый документ:
<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
<title>hi there</title>
</head>
<body>
<img class = "foo" src = "bar.png"/>
</body>
</html>
Если я проанализирую документ с помощью lxml.html, я могу легко получить IMG с xpath:
>>> root = lxml.html.fromstring(doc)
>>> root.xpath("//img")
[<Element img at 1879e30>]
Однако, если я разбираю документ как XML и пытаюсь получить тег IMG, я получаю пустой результат:
>>> tree = etree.parse(StringIO(doc))
>>> tree.getroot().xpath("//img")
[]
Я могу перейти к элементу напрямую:
>>> tree.getroot().getchildren()[1].getchildren()[0]
<Element {http://www.w3.org/1999/xhtml}img at f56810>
Но, конечно, это не помогает мне обрабатывать произвольные документы. Я также ожидал, что смогу запросить etree, чтобы получить выражение xpath, которое будет напрямую идентифицировать этот элемент, что технически я могу сделать:
>>> tree.getpath(tree.getroot().getchildren()[1].getchildren()[0])
'/*/*[2]/*'
>>> tree.getroot().xpath('/*/*[2]/*')
[<Element {http://www.w3.org/1999/xhtml}img at fa1750>]
Но этот xpath, опять же, явно бесполезен для анализа произвольных документов.
Очевидно, я упускаю здесь какую-то ключевую проблему, но не знаю, что это такое. Я предполагаю, что это как-то связано с пространствами имен, но единственное определенное пространство имен является значением по умолчанию, и я не знаю, что еще мне может потребоваться рассмотреть в отношении пространств имен.
Итак, что мне не хватает?






Проблема в пространствах имен. При синтаксическом анализе как XML тег img находится в пространстве имен http://www.w3.org/1999/xhtml, поскольку это пространство имен по умолчанию для элемента. Вы запрашиваете тег img без пространства имен.
Попробуй это:
>>> tree.getroot().xpath(
... "//xhtml:img",
... namespaces = {'xhtml':'http://www.w3.org/1999/xhtml'}
... )
[<Element {http://www.w3.org/1999/xhtml}img at 11a29e0>]
Если вы хотите выполнить поиск с помощью компактных выражений xpath в пространстве имен по умолчанию корневого элемента, вы можете использовать уловку, которая работает для xhtml или других схем, например: nsmap = {'h': tree.getroot().nsmap[None]}; elem.xpath('//h:img', namespaces=nsmap, что упрощает компактное написание запроса.
XPath считает, что все имена без префикса находятся в "без пространства имен".
В частности, в спецификации говорится:
"QName в тесте узла расширяется в расширенное имя с использованием объявлений пространства имен из контекста выражения. Это тот же самый способ, которым расширение выполняется для имен типов элементов в начальных и конечных тегах, за исключением того, что пространство имен по умолчанию, объявленное с xmlns, является не используется: если QName не имеет префикса, тогда URI пространства имен является нулевым (таким же образом раскрываются имена атрибутов) ".
См. Эти два подробных объяснения проблемы и ее решения: здесь и здесь. Решение состоит в том, чтобы связать префикс (с используемым API) и использовать его для префикса любого имени без префикса в выражении XPath.
Надеюсь, это помогло.
Ваше здоровье,
Димитр Новачев
Если вы собираетесь использовать теги только из одного пространства имен, как я вижу выше, вам гораздо лучше использовать lxml.objectify.
В вашем случае это было бы похоже на
from lxml import objectify
root = objectify.parse(url) #also available: fromstring
Вы можете получить доступ к узлам как
root.html
body = root.html.body
for img in body.img: #Assuming all images are within the body tag
Хотя это может быть не очень полезно в html, это может быть очень полезно в хорошо структурированном xml.
Для получения дополнительной информации посетите http://lxml.de/objectify.html
Цитирование из codepeak.net/lxml/xpathxslt.html << При желании вы можете предоставить аргумент ключевого слова namespaces, который должен быть словарем, отображающим префиксы пространства имен, используемые в выражении XPath, в URI пространства имен >>