Братья и сестры с дом / xpath - PullRequest
       34

Братья и сестры с дом / xpath

0 голосов
/ 16 сентября 2010

Несколько дней пытался разобрать следующий HTML-код (обратите внимание, что здесь нет реальной иерархической древовидной структуры). Все в значительной степени на одном уровне.

<p><span class='one'>week number</span></p>

<p><span class='two'>day of the week</span></p>
<table class='spreadsheet'>
table data
</table>

<p><span class='two'>another day of the week</span></p>
<table class='spreadsheet'>
table data
</table>

<p><span class='one'>another week number</span></p>
ETC

Что я в основном хочу сделать, так это просмотреть каждый элемент dom, проверить, является ли это неделя, если она есть, добавить все дни недели к этой конкретной неделе и добавить все данные таблицы в соответствующие день недели. Так что-то следующей структуры:

array {
31 => array {
    monday => array {
        data => table data
    }
    tuesday => array {
        data => table data
    }   
}

32 => array {
    monday => array {
        data => table data
    }
    tuesday => array {
        data => table data
    }   
}
}

Это мой PHP-код, который у меня есть.

$d = new DomDocument;
@$d->loadHtml($html);
$xp = new DomXpath($d);

$res = $xp->query( "//*[@class='one' or @class='two' or @class='spreadsheet']" ); 

foreach ($res as $dn) {
    $nodes = $dn->childNodes;
    foreach ($nodes as $node) {
        if ($node->nodeValue != "") {
            echo $node->nodeValue;
        }

    }
}

Некоторые люди, работающие здесь, в stackoverflow, рекомендовали использовать Xpath для достижения этой цели, приведенный выше код обрабатывает каждый узел отдельно. Я думаю, что мне нужно сделать, это получить все «недельные» узлы, а затем получить их следующий брат и сестра, проверить, какой это день, если это так, добавить это в этот массив, если это «недельный» узел, создать новый массив и т. д.

Последние несколько дней я рвал на себе волосы, поэтому любая помощь / толчок в правильном направлении была бы очень признательна !!!

Cheers, Dandoen

Ответы [ 2 ]

1 голос
/ 16 сентября 2010

Обновлен;см. ниже.

Было бы полезно, если бы вы сообщили нам, каков вывод кода, который вы пробовали до сих пор.Это поможет нам узнать, что уже работает, а что все еще сломано.Тем не менее, вот что я вижу, глядя на ваше использование XPath и DOM.(Отказ от ответственности: мой опыт в XPath и DOM, а не в PHP.)

$res = $xp->query( "//*[@class='one' or @class='two' or @class='spreadsheet']" ); 

Этот запрос XPath даст вам все узлы <span> и <table> в вашем образце, потому что это элементы, которыеесть классы, которые вы просили.

foreach ($res as $dn) {

Итерации по элементам span и table.Внутри этого цикла вы, вероятно, захотите сказать if ($dn->getAttribute("class") == "one") ... и, если это так, начните новую неделю в вашей структуре массива;если класс «два», добавьте новый день недели к текущей неделе и т. д.

$nodes = $dn->childNodes;

Здесь вы запрашиваете дочерние узлы текущего диапазона или элемента таблицы.Для промежутка единственный показанный вами дочерний узел - это текстовый узел, такой как «другой день недели».Для элемента таблицы мы предполагаем, что есть tr элементов и т. Д.

foreach ($nodes as $node) {

Итерации по одному текстовому узлу в диапазоне (или дочерним элементам таблицы):

    if ($node->nodeValue != "") {
        echo $node->nodeValue;
    }

Печатать текстовое содержимое текстового узла (дочернего элемента span);или 'null', если мы смотрим на элемент (например, tr дочерний элемент table).

Так, похоже, и делает приведенный выше код.Если это не так, как описано, опубликуйте информацию о фактическом выводе , и мы сможем помочь.Если он работает так, как описано, но вам нужна помощь в части создания недельных элементов массива, сообщите нам об этом.

Обновление:

Я бы предложил использовать этоЗапрос XPath:

$weeks = $xp->query( "//*[@class='one']" ); 

, чтобы получить номера узлов недели.Затем выполните итерации по ним:

foreach ($weeks as $week) {
    $weekNum = $week->firstChild->nodeValue;

Получает номер недели из первого дочернего элемента (текстового узла) промежутка недели.

Создайте запись массива для новой недели.Затем выберите потенциальные узлы дня недели для этой недели:

$spans = $xp->query( "following::span[@class='one' or @class='two']", $week );

Второй аргумент $xp->query() - это узел контекста, с которого начинается ось following::.

Итерируйте по этим:

foreach ($spans as $span) {

Когда вы переходите на другую неделю, остановитесь:

    if ($span->getAttribute("class") == "one") break;

В противном случае дважды проверьте, что это будний день:

    if ($span->getAttribute("class") == "two") {

, затем добавьте новыйдень недели для вашего массива.Чтобы получить данные таблицы (исправлена ​​ошибка) :

        $table = $xp->query("following-sibling::table[1]", $span->parentNode);

Обновление: Чтобы получить данные таблицы, вам нужно настроить больше цикловкак и выше.Что-то вроде:

    $rows = $xp->query("tr", $table);

, чтобы получить строки таблицы.Затем переберите те, у которых есть foreach, и внутри них,

    $cells = $xp->query("td", $row);

И когда вы будете перебирать ячейки, ваши данные будут

    $cell->firstChild->nodeValue

, то есть текстом дочернего текстового узла.Обратите внимание, что это не будет работать должным образом, если у вас есть элементы внутри ячеек <td>.

Если вам нужна помощь в создании и заполнении массивов в PHP, я не тот человек, который бы советовал вам об этом, поскольку я 'Я не являюсь разработчиком PHP.

Обратите внимание, что все это не проверено.НТН.

0 голосов
/ 16 сентября 2010

Другой подход, с этим входом:

<html>
    <p>
        <span class='one'>week number</span>
    </p>
    <p>
        <span class='two'>day of the week</span>
    </p>
    <table class='spreadsheet'>
        <tr>
            <td>Some data</td>
        </tr>
    </table>
    <p>
        <span class='two'>another day of the week</span>
    </p>
    <table class='spreadsheet'>
        <tr>
            <td>Other data</td>
        </tr>
    </table>
    <p>
        <span class='one'>another week number</span>
    </p>
</html>

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kWeekByNumber" match="span[@class='one']" use="."/>
    <xsl:key name="kDayByWeek" match="span[@class='two']"
             use="generate-id(preceding::span[@class='one'][1])"/>
    <xsl:template match="text()"/>
    <xsl:template match="html">
        <weeks>
            <xsl:apply-templates/>
        </weeks>
    </xsl:template>
    <xsl:template match="span[@class='one']
                             [count(.|key('kWeekByNumber',.)[1])=1]">
        <week number="{.}">
            <xsl:apply-templates select="key('kDayByWeek',generate-id())"
                                     mode="days"/>
        </week>
    </xsl:template>
    <xsl:template match="span[@class='two']" mode="days">
        <day number="{.}">
            <xsl:copy-of select="following::table[1]"/>
        </day>
    </xsl:template>
</xsl:stylesheet>

Выход:

<weeks>
    <week number="week number">
        <day number="day of the week">
            <table class="spreadsheet">
                <tr>
                    <td>Some data</td>
                </tr>
            </table>
        </day>
        <day number="another day of the week">
            <table class="spreadsheet">
                <tr>
                    <td>Other data</td>
                </tr>
            </table>
        </day>
    </week>
    <week number="another week number"></week>
</weeks>

Примечание : Возможно, вы могли бы проанализировать этот вывод с помощью SimpleXML, чтобы получить массив ...

...