python lxml различный результат на windows и linux
Linux
>>> from lxml import etree
>>> html='''<td><a href=''>a1</a></td>
... <td><a href=''>a2</a></td>
... '''
>>> p=etree.HTML(html)
>>> a=p.xpath("//a[1]")
>>> for i in a:
... print i.text
...
a1
a2
Окна.
>>> html='''<td><a href=''>a1</a></td>
... <td><a href=''>a2</a></td>
... '''
>>> from lxml import etree
>>> p=etree.HTML(html)
>>> a=p.xpath("//a[1]")
>>> for i in a:
... print i.text
...
a1
>>> b=p.xpath("//a[2]")
>>> for i in b:
... print i.text
...
a2
В Windows, я могу легко использовать a[1]
и a[2]
, чтобы получить эти два значения.
Но в Linux xpath //a[1]
объединяет эти два текста ссылок.
Это делает программу не столь совместимой в этих ОС. Мне приходится модифицировать код на разных ОС. Это ошибка модуля lxml ? Есть какое-нибудь решение для этого ?
1 ответ:
Я могу подтвердить тот же результат на Linux, что и вы. Он возвращает список из двух элементов вместо одного элемента.
Чего требует xpath
//a[1]
Он запрашивает любой элемент
a
, который является первым в его контексте.Как у вас есть
a
элемент, встроенный вtd
,td
есть контекст для расчета позиции и есть два вхождения такой ситуации.Изменение xpath на
"(//a)[1]"
решает проблему.Цитирование от MSDN по операторам и специальным символам
Операторы шаблона фильтра ([]) имеют более высокий приоритет, чем операторы пути (/ и //). Например, выражение / / comment () [3] выбирает все комментарии с индексом, равным 3 относительно родительского комментария в любом месте документа. Это отличается от выражения (//comment ()) [3], которое выбирает третий комментарий из множества всех комментариев относительно родителя. Первое выражение может возвращать больше чем один комментарий, в то время как последний может вернуть только один комментарий.Понизить сломанную версию Windows lxml 3.3.5
Xpath
//a[1]
, возвращающий только один элемент предоставленного документа, просто неверен и должен быть сообщен авторам lxml.Состояние lxml на разных платфомах и ОС:
- Win: lxml 2.3.0-OK
- Win: lxml 3.3.5-BUG
- Lin: lxml 3.3.5-OK
- Lin: lxml 2.3.0-OK
Чтобы принять решение portable, вам потребуется
lxml==2.3.0
, так как эта версия ведет себя на Windows, а также на Linux правильно (возможно, есть другая версия, хорошо работающая на обеих платформах, я больше не тестировал).Бонус-тестовый набор
Предполагая, что вы установили
nose
$ pip install nose
Вы можете использовать следующие
test_xpath.py
:from lxml import etree import nose print "==================================" print "lxml version: ", etree.__version__ print "==================================" def test_html(): html_str = """ <td><a href=''>a1</a></td> <td><a href=''>a2</a></td> """ doc = etree.HTML(html_str.strip()) elms = doc.xpath("//a[1]") assert len(elms) == 2, """xpath `//a[1]` shall return 2 elements""" assert all(elm.tag == "a" for elm in elms), "all returned elements shall be `a`" assert elms[0].text == "a1" assert elms[1].text == "a2" def test_xml(): xml_str = """ <root> <td><a href=''>a1</a></td> <td><a href=''>a2</a></td> </root> """ doc = etree.fromstring(xml_str.strip()) elms = doc.xpath("//a[1]") assert len(elms) == 2, """xpath `//a[1]` shall return 2 elements""" assert all(elm.tag == "a" for elm in elms), "all returned elements shall be `a`" assert elms[0].text == "a1" assert elms[1].text == "a2" nose.main()
И быстро выполните тест:
$ python test_xpath.py -v ================================== lxml version: 2.3.0 ================================== test_xpath.test_html ... ok test_xpath.test_xml ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.002s OK