如何在两个HTML标签之间获取所有内容?(用XPath?)

Sup*_*Spy 1 php xpath screen-scraping

编辑:我添加了一个适用于这种情况的解决方案.


我想从页面中提取一个表,我想(可能)使用DOMDocument和XPath.但如果你有更好的主意,请告诉我.

我的第一次尝试就是这个(显然是错误的,因为它将获得第一个关闭表标签):

<?php 
    $tableStart = strpos($source, '<table class="schedule"');
    $tableEnd   = strpos($source, '</table>', $tableStart);
    $rawTable   = substr($source, $tableStart, ($tableEnd - $tableStart));
?>
Run Code Online (Sandbox Code Playgroud)

我很难,这可能是用DOMDocument和/或xpath解决的......


最后,我希望标签之间的所有内容(在本例中为标签)和标签自身.所以所有HTML,不仅仅是值(例如,不只是'价值',而是'价值').并且有一个"捕获"......

  • 该表中包含其他表格.因此,如果您只搜索表格的末尾('tag'),您可能会得到错误的标签.
  • 开始标记有一个类,您可以使用它来识别它(classname ='schedule').

这可能吗?

这是我想要从另一个网站提取的(简化的)源代码:(我还想显示html标签,而不仅仅是值,所以整个表格都带有'schedule'类)

<table class="schedule">
    <table class="annoying nested table">
        Lots of table rows, etc.
    </table> <-- The problematic tag...
    <table class="annoying nested table">
        Lots of table rows, etc.
    </table> <-- The problematic tag...
    <table class="annoying nested table">
        Lots of table rows, etc.
    </table> <-- a problematic tag...

    This could even be variable content. =O =S

</table>
Run Code Online (Sandbox Code Playgroud)

Dim*_*hev 8

首先,请注意XPath基于XML Infopath - 一种XML模型,其中没有"起始标记"和"结束标记",只有节点

因此,不应期望XPath表达式选择"标签" - 它选择节点.

考虑到这一事实,我将这个问题解释为:

我想获得给定的"start"元素和给定的"end元素"之间的所有元素的集合,包括start和end元素.

在XPath 2.0中,这可以通过标准运算符交叉方便地完成.

在XPath 1.0(我假设您正在使用)中,这并不容易.解决方案是使用Kayessian(由@Michael Kay)公式进行节点集交集:

两个节点集合的交集:$ns1$ns2通过评估以下XPath表达式选自:

$ns1[count(.|$ns2) = count($ns2)]
Run Code Online (Sandbox Code Playgroud)

假设我们有以下XML文档(因为您从未提供过):

<html>
    <body>
        <table>
            <tr valign="top">
                <td>
                    <table class="target">
                        <tr>
                            <td>Other Node</td>
                            <td>Other Node</td>
                            <td>Starting Node</td>
                            <td>Inner Node</td>
                            <td>Inner Node</td>
                            <td>Inner Node</td>
                            <td>Ending Node</td>
                            <td>Other Node</td>
                            <td>Other Node</td>
                            <td>Other Node</td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

start元素通过以下方式选择:

//table[@class = 'target']
         //td[. = 'Starting Node']
Run Code Online (Sandbox Code Playgroud)

结束元素选择:

//table[@class = 'target']
         //td[. = Ending Node']
Run Code Online (Sandbox Code Playgroud)

为了获得所有想要的节点,我们将以下两组相交:

  1. 该组由start元素和所有后续元素组成(我们将其命名为$vFollowing).

  2. 由end元素和所有前面的元素组成的集合(我们将其命名为$vPreceding).

它们分别由以下XPath表达式选择:

$ vFollowing:

$vStartNode | $vStartNode/following::*
Run Code Online (Sandbox Code Playgroud)

$ vPreceding:

$vEndNode | $vEndNode/preceding::*
Run Code Online (Sandbox Code Playgroud)

现在我们可以简单地在节点集上应用Kayessian公式,$vFollowing并且$vPreceding:

       $vFollowing
          [count(.|$vPreceding)
          =
           count($vPreceding)
          ]
Run Code Online (Sandbox Code Playgroud)

剩下的就是用各自的表达式替换所有变量.

基于XSLT的验证:

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

 <xsl:variable name="vStartNode" select=
 "//table[@class = 'target']//td[. = 'Starting Node']"/>

 <xsl:variable name="vEndNode" select=
 "//table[@class = 'target']//td[. = 'Ending Node']"/>

 <xsl:variable name="vFollowing" select=
 "$vStartNode | $vStartNode/following::*"/>

 <xsl:variable name="vPreceding" select=
 "$vEndNode | $vEndNode/preceding::*"/>

 <xsl:template match="/">
      <xsl:copy-of select=
          "$vFollowing
              [count(.|$vPreceding)
              =
               count($vPreceding)
              ]"/>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

当应用于上面的XML文档时,将评估XPath表达式并输出所需的,正确的结果选择节点集:

<td>Starting Node</td>
<td>Inner Node</td>
<td>Inner Node</td>
<td>Inner Node</td>
<td>Ending Node</td>
Run Code Online (Sandbox Code Playgroud)