Управление списком из запросов lxml xpath - PullRequest
2 голосов
/ 12 августа 2010

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

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

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

  • name child имеет класс "title"
  • значение child имеет идентификатор "text"

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

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, который я повторяю, имеет эти пары: некоторые имеют только имя (id "title"), поэтому позже, когда я пытаюсь сжать их, я получаю неправильно спаренные данные.

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

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

или

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

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

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

Ответы [ 2 ]

4 голосов
/ 12 августа 2010

Как это?

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 для устранения нежелательного результата. Спасибо Алехандро за то, что он указал на это и предложил исправление, которое, кажется, не сработало для Отрова.

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 "--"
0 голосов
/ 12 августа 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>
...