【问题标题】:Extracting information via XPath in PHP在 PHP 中通过 XPath 提取信息
【发布时间】:2015-06-09 06:46:13
【问题描述】:

只是想从 AEC 网站上提取一些信息(例如 http://apps.aec.gov.au/eSearch/LocalitySearchResults.aspx?filter=3977&filterby=Postcode)。我正在运行的 XPath 查询是“//x:tbody/x:tr/x:td[4]/x:a”,我已经在 XPath Checker(Firefox 扩展)中对其进行了测试,它会提取相关的位置数据。

然后我使用 PHP 加载页面,执行查询,然后遍历结果。

$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$html = curl_exec($ch);
curl_close($ch);

# Create a DOM parser object
$dom = new DOMDocument();
libxml_use_internal_errors(true);


 $dom->loadHTML($html);

$xpath = new DOMXpath($dom);

$elements = $xpath->query( '//tbody/tr/td[4]/a');


foreach ($elements as $element) {
     echo $element;
}

然后我得到:

Warning: Invalid argument supplied for foreach() in /home/givesh5/public_html/dig/electoratesearch.php on line 41

查询似乎返回某种布尔值而不是查询的匹配列表?

相关标记如下:

<table cellspacing="0" rules="all" border="1" id="ContentPlaceHolderBody_gridViewLocalities" style="border-collapse:collapse;">
        <tr class="headingLink">
            <th scope="col"><a href="javascript:__doPostBack(&#39;ctl00$ContentPlaceHolderBody$gridViewLocalities&#39;,&#39;Sort$StateAb&#39;)">State</a></th><th scope="col"><a href="javascript:__doPostBack(&#39;ctl00$ContentPlaceHolderBody$gridViewLocalities&#39;,&#39;Sort$LocalityNm&#39;)">Locality/Suburb</a></th><th scope="col"><a href="javascript:__doPostBack(&#39;ctl00$ContentPlaceHolderBody$gridViewLocalities&#39;,&#39;Sort$Postcode&#39;)">Postcode</a></th><th scope="col"><a href="javascript:__doPostBack(&#39;ctl00$ContentPlaceHolderBody$gridViewLocalities&#39;,&#39;Sort$DivisionNm&#39;)">Electorate</a></th><th scope="col"><a href="javascript:__doPostBack(&#39;ctl00$ContentPlaceHolderBody$gridViewLocalities&#39;,&#39;Sort$DivisionNmRedistributed&#39;)">Redistributed Electorate</a></th><th scope="col">Other Locality(s)</th>
        </tr><tr>
            <td>VIC</td><td>BOTANIC RIDGE</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Flinders&amp;filterby=Electorate&amp;divid=211">Flinders</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>CANNONS CREEK</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Flinders&amp;filterby=Electorate&amp;divid=211">Flinders</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>CRANBOURNE</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Holt&amp;filterby=Electorate&amp;divid=216">Holt</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>CRANBOURNE EAST</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Flinders&amp;filterby=Electorate&amp;divid=211">Flinders</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>CRANBOURNE EAST</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Holt&amp;filterby=Electorate&amp;divid=216">Holt</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>CRANBOURNE NORTH</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Holt&amp;filterby=Electorate&amp;divid=216">Holt</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>CRANBOURNE SOUTH</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Flinders&amp;filterby=Electorate&amp;divid=211">Flinders</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>CRANBOURNE WEST</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Holt&amp;filterby=Electorate&amp;divid=216">Holt</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>DEVON MEADOWS</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Flinders&amp;filterby=Electorate&amp;divid=211">Flinders</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>FIVEWAYS</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Flinders&amp;filterby=Electorate&amp;divid=211">Flinders</a></td><td></td><td><a href="LocalitySearchResults.aspx?filter=DEVON+MEADOWS&amp;filterby=LocalityorSuburb&amp;state=VIC">DEVON MEADOWS</a></td>
        </tr><tr>
            <td>VIC</td><td>JUNCTION VILLAGE</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Flinders&amp;filterby=Electorate&amp;divid=211">Flinders</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>SANDHURST</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Isaacs&amp;filterby=Electorate&amp;divid=219">Isaacs</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>SKYE</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Dunkley&amp;filterby=Electorate&amp;divid=210">Dunkley</a></td><td></td><td>&nbsp;</td>
        </tr><tr>
            <td>VIC</td><td>SKYE</td><td><a href="LocalitySearchResults.aspx?filter=3977&amp;filterby=Postcode">3977</a></td><td><a href="LocalitySearchResults.aspx?filter=Isaacs&amp;filterby=Electorate&amp;divid=219">Isaacs</a></td><td></td><td>&nbsp;</td>
        </tr>
    </table>

【问题讨论】:

  • DOMXpath 如果表达式格式错误或上下文节点无效,则返回 false
  • 您能否提供您正在解析的标记的相关部分。从 Firefox 派生的 XPath 来自可以包含隐含标记的实时 DOM。因此,以这种方式获得它们是不可靠的。另外,您到底想获取什么?
  • 已经用标记更新了 OP,谢谢。在这种情况下,尝试获取本地的链接文本(例如 Text)。例如,在前两个单元格中,这将是“Flinders”。

标签: php html xml xpath


【解决方案1】:

该 HTML 中没有 tbody
浏览器会在需要的地方插入tbody 元素,但我们没有使用浏览器,我们使用的是DOMDocument,它不会插入tbody 元素。

相反,tr 元素是表的直接子元素

$elements = $xpath->query( '//table/tr/td[4]/a');

foreach ($elements as $element) {
     echo $dom->saveHTML($element);
}

【讨论】:

  • // 应该匹配文档中途的选择吗?从这个意义上说,如果 table/tr/td 是唯一的选择器,那么我们可以简单地省略路径的前面部分,仍然可以通过 //table/tr/td[4] 访问相同的信息。这不正确吗?
  • @Edward - 是的,没错,我只是从控制台复制了路径,但测试它//table/tr/td[4]/a 也可以,但是你得到的//tbody/tr/td[4]/a 不起作用跨度>
  • 可能是因为没有tbody,呵呵。
  • 嗯,好的。这就说得通了。至少我现在得到了一个节点列表,但不幸的是它里面有 0 个节点。
【解决方案2】:

查询似乎返回某种布尔值而不是查询的匹配列表?

是的,它可以返回一个布尔值,那么它将是FALSE。它表示运行 xpath 查询时出现错误。这可能是由传递给 DOMXpath::query()Php Manual 的两个参数之一引起的,xpath 表达式上下文节点

在您的情况下,您只使用一个参数,因此这表明 xpath 表达式是错误的。但是,您使用的那个没有错,也不会导致布尔值FALSE。但是当您遇到该错误时,我认为可能还有其他问题,因此 xpath 对象可能未完全初始化,但即使没有或部分下载我模拟我也无法重现该错误。这可能与PHP版本有所不同?我不知道。

对于实际的 xpath 表达式,它应用 adeneoGordon 已经编写的内容,将 &lt;tbody&gt;-element 插入到 DOM通过 Firefox,PHP 中的 DOMDocument 实现在这里表现不同。您可以在这里模仿 Firefox(更多工作) - 或者 - 您只需搜索实际的表格元素,然后它就可以正常工作。这是一个工作示例:

$url = 'http://apps.aec.gov.au/eSearch/LocalitySearchResults.aspx?filter=3977&filterby=Postcode';

# Create a DOMDocument to parse HTML
$doc    = new DOMDocument();
$saved  = libxml_use_internal_errors(true);
$result = $doc->loadHTMLFile($url);
libxml_use_internal_errors($saved);
if (false === $result) {
    throw new UnexpectedValueException(sprintf('Failed to create DOMDocument from url %s', var_export($url, true)));
}

# Create a DOMXPath to get data from HTML document
$xpath = new DOMXpath($doc);

$expression = '//table/tr/td[4]/a';
$elements   = $xpath->query($expression);
if (false === $elements) {
    throw new UnexpectedValueException(sprintf('The xpath expression %s failed', var_export($expression, true)));
}

foreach ($elements as $index => $element) {
    printf("#%02d: %s\n", $index + 1, trim($element->textContent));
}

示例输出:

#01: Flinders
#02: Flinders
#03: Holt
#04: Flinders
#05: Holt
#06: Holt
#07: Flinders
#08: Holt
#09: Flinders
#10: Flinders
#11: Flinders
#12: Isaacs
#13: Dunkley
#14: Isaacs

【讨论】:

    猜你喜欢
    • 2010-12-05
    • 1970-01-01
    • 1970-01-01
    • 2012-07-02
    • 1970-01-01
    • 2013-10-29
    • 1970-01-01
    • 1970-01-01
    • 2021-06-28
    相关资源
    最近更新 更多