Манипулирование списком из запросов lxml xpath

Сегодня я попробовал lxml, так как получил очень неприятный html-вывод из определенного веб-сервиса, и я не хотел использовать модуль re, просто для изменений и изучения чего-то нового. И я это сделал, просматривая http://codespeak.net/lxml/ и http://stackoverflow.com параллельно

Я не буду пытаться объяснить приведенный выше html-шаблон, но просто для обзора он полон преднамеренно вложенных таблиц.

Я извлек интересующую часть с помощью синтаксического анализатора html, затем find_class() и повторил TR с помощью xpath (и даже в этих TR есть таблицы внутри). Теперь я пытаюсь извлечь пары данных на основе атрибутов класса и идентификатора:

  • имя ребенка имеет класс "название"
  • значение дочернего элемента имеет идентификатор "текст"

Код выглядит примерно так:

fragment = root.find_class('foo')

for node in fragment[0].xpath('table[2]/tr'):
    name = node.xpath('//div[@id="title"]')
    value = node.xpath('//td[@class="text"]')

Проблема в том, что не каждый TR, который я повторяю, имеет эти пары: некоторые только с именем (идентификатор «название»), поэтому позже, когда я пытаюсь их заархивировать, я получаю неправильно спаренные данные.

Я попробовал пару вещей, которые пришли мне в голову, но ничего не получилось: я попытался сравнить длину списка (для имени и значения), и если они не совпадают, пропустить поиск имени, а затем, если они не совпадают, удалить последний элемент списка ( разными способами) но ничего не получалось. Например:

if not len(name) == len(value):
    name.pop()

or

if len(name) == len(value):
    name = node.xpath('//div[@id="title"]')

value = node.xpath('//td[@class="text"]')

Некоторые комментарии от более опытных?


person otrov    schedule 12.08.2010    source источник
comment
Пример того, что вы анализируете, может быть полезен   -  person MattH    schedule 12.08.2010
comment
Я удалил свой ответ, потому что вы явно не знаете, о чем спрашиваете. Выражения, которые я предложил, возвращают те же значения, что и выражения в вашем вопросе.   -  person Dimitre Novatchev    schedule 12.08.2010
comment
Я загрузил слегка измененный образец здесь: pastebin.com/cg5HHJ6x — это повторяющийся TR. @Dimitre Novatchev: Если вы так думаете, пожалуйста, верните свой ответ и обсудите его - 1. В нем была синтаксическая ошибка и 2. Он не работал. Я думаю, что не стоит пытаться давать ответы, если вы не в состоянии за это постоять.   -  person otrov    schedule 12.08.2010
comment
Получил ваш образец. Теперь, что вы хотите извлечь из образца?   -  person MattH    schedule 12.08.2010
comment
Я хочу соединить дочерние элементы ‹div id=title› и дочерние элементы ‹td class=text›, которые находятся внутри основных ветвей ‹tr› (в опубликованном примере их 13, хотя на самом деле они различаются). Не у каждого основного элемента ‹tr› есть и то, и другое, а у некоторых нет ни того, ни другого. Например: первый элемент ‹tr› не имеет ничего, второй элемент ‹tr› имеет только ‹div id=title›, а третий элемент ‹tr› имеет допустимую пару.   -  person otrov    schedule 12.08.2010


Ответы (2)


Как это?

from lxml import etree
doc = etree.HTML(open('test.data').read())

for t in doc.xpath('//table[.//div[@id="title"] and .//td[@class="text"]]'):
    print etree.tostring(t.xpath('.//div[@id="title"]')[0])
    print etree.tostring(t.xpath('.//td[@class="text"]')[0])
    print "--"

Урожайность:

<div id="title">
              <span class="Browse">string</span>
            </div>

<td class="text" style="padding-left:5px;">
            <a href="/***/***.dll?p=***&amp;sql=xxx:yyy">string</a>
          </td>

--
<div id="title">
              <span>string</span>
            </div>

<td class="text" style="padding-left:5px;">
            <a href="/***/***.dll?p=***&amp;sql=xxx:yyy">string</a>
          </td>

--
<div id="title">
              <span>string</span>
            </div>

<td class="text" style="padding-left:5px;">
            Gospodar of Lutaka
          </td>

--
<div id="title">
              <span>string</span>
            </div>

<td class="text" style="padding-left:5px;">
            1986
          </td>

--
<div id="title">
              <span>string</span>
            </div>

<td class="text" style="padding-left:5px;">
            Sep 1985-Dec 1985
          </td>

--
<div id="title">
              <span>string</span>
            </div>

<td class="text" style="padding-left:5px;">
            Elektra
          </td>

--
<div id="title">
              <span>string</span>
            </div>

<td class="text" style="padding-left:5px;">
            54:51
          </td>

--
<div id="title">
              <span>string</span>
            </div>

<td class="text" style="padding-left:5px;">
          </td>

--

Обновление, расширенная начальная часть выражения xpath для устранения нежелательного результата. Спасибо Алехандро за указание на это и предложение исправления, которое, похоже, не сработало для otrov.

from urllib2 import urlopen
from lxml import etree
doc = etree.HTML(urlopen('http://pastebin.com/download.php?i=cg5HHJ6x').read())

for t in doc.xpath('//table/tr/td/table[.//div[@id="title"] and .//td[@class="text"]]'):
    print etree.tostring(t.xpath('.//div[@id="title"]')[0])
    print etree.tostring(t.xpath('.//td[@class="text"]')[0])
    print "--"
person MattH    schedule 12.08.2010
comment
Превосходно! Спасибо за правильный ответ и хороший урок. Теперь я могу продолжить работу с остальной частью кода :) - person otrov; 12.08.2010
comment
@otrov: Добро пожаловать! Лично я нашел xpath крутой кривой обучения, примеры в вопросах на этом сайте очень удобны, на SO скрываются некоторые гуру XLST/XPATH. Спасибо за то, что поставили меня выше 2000! :) - person MattH; 12.08.2010
comment
Эх, как здорово, кажется, ты обеспечил себе билетик в недалекое будущее :) Удачи - person otrov; 12.08.2010
comment
@MattH: Проверьте ответ, я думаю, что что-то не так: нет td[@class="text"] для div[@id="title"][span/@class="Browse"] - person ; 12.08.2010
comment
@ Алехандро, извини, что я сегодня немного туплю. Вы говорите, что это решение не возвращает некоторые данные, которые должны быть возвращены? - person MattH; 12.08.2010
comment
@MattH: Нет. Я говорю, что он возвращает некоторые данные (первая пара), которые не должны возвращаться. - person ; 12.08.2010
comment
@Alejandro: Вы правильно заметили - есть избыточные пары данных! Например: несовпадающее имя из второго элемента TR сочетается со значением из третьего элемента TR, НО - допустимые пары сопоставляются правильно, и это было моей основной проблемой, так как в более позднем коде я итерирую эти пары для данных известного имени, что приводит к хорошим результатам. - person otrov; 13.08.2010

Теперь, с входным образцом, более ясно, что вы спрашиваете.

Только это одно выражение XPath 1.0 возвращает набор узлов с парой div и td (в порядке документа):

/table/tr/td/table[tr/td/div[@id='title']]
                  [tr/td[@class='text']]
                  /tr//*[self::div[@id='title'] or self::td[@class='text']]

В качестве доказательства эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <result>
            <xsl:copy-of 
                 select="/table/tr/td/table[tr/td/div[@id='title']]
                                           [tr/td[@class='text']]
                                           /tr//*[self::div[@id='title'] or
                                                  self::td[@class='text']]"/>
        </result>
    </xsl:template>
</xsl:stylesheet>

Вывод (с правильным входным образцом, потому что вы пропустите закрывающий td):

<result>
    <div id="title">
        <span>string</span>
    </div>
    <td class="text" style="padding-left:5px;">
        <a href="/***/***.dll?p=***&amp;sql=xxx:yyy">string</a>
    </td>
    <div id="title">
        <span>string</span>
    </div>
    <td class="text" style="padding-left:5px;">
            Gospodar of Lutaka
    </td>
    <div id="title">
        <span>string</span>
    </div>
    <td class="text" style="padding-left:5px;">
            1986
    </td>
    <div id="title">
        <span>string</span>
    </div>
    <td class="text" style="padding-left:5px;">
            Sep 1985-Dec 1985
    </td>
    <div id="title">
        <span>string</span>
    </div>
    <td class="text" style="padding-left:5px;">
            Elektra
    </td>
    <div id="title">
        <span>string</span>
    </div>
    <td class="text" style="padding-left:5px;">
            54:51
    </td>
    <div id="title">
        <span>string</span>
    </div>
    <td class="text" style="padding-left:5px;"></td>
</result>
person Community    schedule 12.08.2010
comment
Насчет отсутствующих ‹/td›: я быстро проверил, и действительно есть нечетное количество закрывающих тегов ‹/td› и четное количество открывающих тегов ‹td› - и это именно то, что выдает веб-сервис, чего я не хочу разоблачите здесь, но я могу отправить вам ссылку по электронной почте или аналогично, если вы хотите проверить мое письмо. - person otrov; 13.08.2010
comment
В конце концов, я считаю, что ваше выражение xpath работает нормально, поскольку вы проверили его с помощью XSLT, но не тогда, когда я пытаюсь поместить его в свой код. В качестве примера я взял фрагмент MattH, затем вставил блок for():, я поставил ‹code›node = doc.xpath('/table/tr/td/table[tr/td/div[@id=title]][tr/ td[@class=text]]/tr//*[self::div[@id=title] или self::td[@class=text]]')‹/code›, который не дает результата, аналогично Удаленный ответ Дмитрия. Так что я, вероятно, должен был сделать это с известным модулем регулярных выражений, вместо того, чтобы начинать изучать lxml на таком неудобном примере. - person otrov; 13.08.2010
comment
@otrov, я обновил свое решение более конкретным выражением, вдохновленным Алехандро, надеюсь, оно послужит вам лучше. Честно говоря, любое время, потраченное на использование xpath вместо регулярных выражений для обработки HTML или XML, потрачено с пользой! - person MattH; 13.08.2010
comment
@Matt: ваш первоначальный код был хорош для моего использования, но теперь я использую эту слегка измененную версию, которая в основном обслуживает мой код :) Спасибо за поддержку в XPATH, я пытался получить какой-то смысл в прошлом из XML/XSLT, но потерпел неудачу , я думаю, мне нужно больше внимания уделять этой теме @Alejandro: Спасибо за ответ, я больше посмотрю на перевод общего выражения XPATH в выражение lxml xpath :) - person otrov; 13.08.2010
comment
@otrov: если элемент table не является вашим корневым элементом (как в опубликованном образце ввода), вы можете добавить отсутствующий путь к элементу table (как /html/body/ и т. д.). Я не рекомендую начинать путь с оператором //, потому что он перемещается по всему дереву. - person ; 13.08.2010
comment
да :) это было неловко, мне нужно было поставить /table[2]/ вместо /table/ Ура - person otrov; 13.08.2010