与dom / xpath的兄弟姐妹

时间:2010-09-16 13:54:27

标签: php dom xpath screen-scraping

已经尝试了几天解析下面的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来实现这一点,上面的代码分别处理每个节点。我认为我需要做的是获取所有“周”节点,然后获取他们的下一个兄弟节点,从那里检查它是否是一天,如果是这样,将它添加到该数组,如果它是“周”节点,创建一个新的数组等

过去几天我一直在用头发撕掉头发,所以任何帮助/推动正确的方向都会非常感激!!!

干杯, Dandoen

2 个答案:

答案 0 :(得分:1)

<强>更新;见下文。

如果您希望到目前为止已尝试过tell us what the output is代码,那将会有所帮助。这将有助于我们了解已经有效的东西和仍然存在的东西。但是,这就是我看到你对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;

在这里,您要求当前span或table元素的子节点。对于跨度,您显示的唯一子节点是文本节点,例如“一周中的另一天”。对于table元素,我们假设有tr个元素等。

foreach ($nodes as $node) {

迭代span中的单个文本节点(或表的子元素):

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

打印文本节点的文本内容(span元素的子元素);或'null' if we're looking at an element(与tr的{​​{1}}子项一样。)

这就是上面的代码似乎正在做的事情。如果它的行为与描述不符,请发布有关实际输出的信息,我们可能会提供帮助。如果它的行为如上所述,但您需要有关创建周数组元素的部分的帮助,请告诉我们。

<强>更新

我建议您使用此XPath查询:

table

获取周数节点。然后迭代它们:

$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);

获取表格行。然后用foreach迭代那些,在那些内部,

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

当你遍历单元格时,你的数据将是

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

即。子文本节点的文本。请注意,如果 $cell->firstChild->nodeValue 单元格中包含元素,则无法正常工作。

如果您需要有关在PHP中创建和填充数组的帮助,我不是建议您的人,因为我不是PHP开发人员。

注意这都是未经测试的。 HTH。

答案 1 :(得分:0)

使用此输入的其他方法:

<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解析该输出以获取数组...