Как выполнить поиск Python XPath без учета регистра с помощью lxml?

Я пытаюсь сопоставить страну или страну, используя функцию lower-case в XPath. translate немного запутан, поэтому использование строчных букв, а моя версия Python 2.6.6 имеет поддержку XPath 2.0, я полагаю, поскольку строчные буквы доступны только в XPath 2.0.

То, как я могу использовать строчные буквы в моем случае, - это то, что я ищу. Надеюсь, пример понятен. Я ищу ['USA', 'US'] в качестве вывода (обе страны за один раз, что может произойти, если строчные буквы оценивают страну и страну как одинаковые).

HTML: doc.htm

<html>
    <table>
        <tr>
            <td>
                Name of the Country : <span> USA </span>
            </td>
        </tr>
        <tr>
            <td>
                Name of the country : <span> UK </span>
            </td>
        </tr>
</table>

Питон:

import lxml.html as lh

doc = open('doc.htm', 'r')
out = lh.parse(doc)
doc.close()

print out.xpath('//table/tr/td[text()[contains(. , "Country")]]/span/text()')
# Prints : [' USA ']
print out.xpath('//table/tr/td[text()[contains(. , "country")]]/span/text()')
# Prints : [' UK ']

print out.xpath('//table/tr/td[lower-case(text())[contains(. , "country")]]/span/text()')
# Prints : [<Element td at 0x15db2710>]

Обновление:

out.xpath('//table/tr/td[text()[contains(translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz") , "country")]]/span/text()')

Теперь остается вопрос, могу ли я сохранить часть перевода как глобальную переменную «handlecase» и печатать эту глобальную переменную всякий раз, когда я выполняю XPath?

Что-то вроде этого работает:

handlecase = """translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")"""

out.xpath('//table/tr/td[text()[contains(%s , "country")]]/span/text()' % (handlecase))

Но для простоты и удобочитаемости я хочу запустить его так:

out.xpath('//table/tr/td[text()[contains(handlecase , "country")]]/span/text()')

person ThinkCode    schedule 27.06.2012    source источник
comment
Из документации lxml XPath: lxml supports XPath 1.0; таким образом, с lxml вы застряли с переводом.   -  person Martijn Pieters    schedule 27.06.2012
comment
В этом случае я не уверен, почему он не жалуется, когда я использую нижний регистр. Мне не очень повезло с «переводом» в этом примерном сценарии. Спасибо!   -  person ThinkCode    schedule 27.06.2012
comment
Возможный дубликат   -  person JWiley    schedule 27.06.2012
comment
Спасибо за ссылку. Это скорее обсуждение «нижнего регистра», чем перевод. На самом деле я получил перевод для работы, выполнив: текст()') . Моды могут закрыть это, если в этом случае нельзя применить нижний регистр. Спасибо!   -  person ThinkCode    schedule 27.06.2012
comment
Но lxml ДЕЙСТВИТЕЛЬНО жалуется, если вы используете lower-case(): lxml.etree.XPathEvalError: незарегистрированная функция. Код после того, как я фактически заставил перевод работать, выполнив..., не может быть правильным.   -  person mzjn    schedule 27.06.2012
comment
Оно работает! handlecase = 'translate(., ABCDEFGHIJKLMNOPQRSTUVWXYZ, abcdefghijklmnopqrstuvwxyz)' out.xpath('//table/tr/td[text()[содержит(%s, страна)]]/span/text()' % (handlecase))   -  person ThinkCode    schedule 27.06.2012


Ответы (2)


Использование:

   //td[translate(substring(text()[1], string-length(text()[1]) - 9),
                  'COUNTRY :',
                  'country'
                  )
        =
         'country'
       ]
        /span/text()

Проверка на основе XSLT:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  "//td[translate(substring(text()[1], string-length(text()[1]) - 9),
                  'COUNTRY :',
                  'country'
                  )
        =
         'country'
       ]
        /span/text()
       "/>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному XML-документу:

<html>
        <table>
            <tr>
                <td>
                    Name of the Country : <span> USA </span>
                </td>
            </tr>
            <tr>
                <td>
                    Name of the country : <span> UK </span>
                </td>
            </tr>
        </table>
</html>

выражение XPath оценивается, и два выбранных текстовых узла копируются в выходные данные:

 USA  UK 

Пояснение:

  1. Мы используем конкретный вариант выражения XPath 1.0, который реализует стандартную функцию XPath 2.0 ends-with($text, $s): это:

.....

$s = substring($text, string-length($text) - string-length($s) +1)

.2. Следующий шаг — с помощью функции translate() преобразовать окончательную длинную строку из 10 символов в нижний регистр, удалив все пробелы или символы «:».

.3. Если результатом является строка (все в нижнем регистре) «страна», то мы выбираем дочерние текстовые узлы (в данном случае только один) дочернего элемента s=span этого td.

person Dimitre Novatchev    schedule 28.06.2012

Я считаю, что проще всего получить то, что вы хотите, это просто написать функцию расширения XPath.

Сделав это, вы можете либо написать функцию lower-case(), либо поиск без учета регистра.


Подробную информацию можно найти здесь: http://lxml.de/extensions.html

person stranac    schedule 27.06.2012
comment
очень хороший ответ, но без примера не победить - person mykhal; 25.07.2012
comment
Я не пытался победить, просто помочь. Думал привести пример, но мне просто показалось, что по ссылке достаточно примеров. - person stranac; 26.07.2012