【问题标题】:An empty attribute in DOM returns an unexpected fallback valueDOM 中的空属性返回意外的回退值
【发布时间】:2016-06-14 22:13:31
【问题描述】:

我已检索到此网页http://www.dw.com/ar/تقرير-استخباري-اميركي-القاعدة-تسيطر-على-غرب-العراق/a-2251369的内容并将其保存到$webpage

请注意

在此网页中,有许多<meta> 标签。这些元标记之一是罪魁祸首,并导致了一些问题。这个元标签是<meta property="og:description" content="" />。注意content 的值是一个空字符串。

我正在阅读网页内容如下:

<?php

$url = 'http://www.dw.com/ar/تقرير-استخباري-اميركي-القاعدة-تسيطر-على-غرب-العراق/a-2251369';

$webpage = file_get_contents($url);

$og_entry_title = "";
$og_entry_content = "";

$doc = new DOMDocument;
$doc->loadHTML($webpage);

$meta_tags = $doc->getElementsByTagName('meta');

foreach ($meta_tags as $meta_tag) {

    if ($meta_tag->getAttribute('property') == 'og:title') {
        $og_entry_title = $meta_tag->getAttribute('content');
    }

    if ($meta_tag->getAttribute('property') == 'og:description') {
        $og_entry_content = $meta_tag->getAttribute('content');
    }

}

// print the results
echo
'$og_entry_title: ' . $og_entry_title
.PHP_EOL.
'$og_entry_content: ' . $og_entry_content;

完成后,$og_entry_title$og_entry_content 的值如下:

$og_entry_title: TOP STORIES | DW.COM
$og_entry_content: News and analysis of the top international and European topics Current affairs and background information on poltics, business, science, culture, globalization and the environment.

请注意结果中的以下内容:

$og_entry_title是正确的,包含页面标题,所以这里没有问题

$og_entry_content 给出的值与我的预期不同。我希望在$og_entry_content 中保存一个空字符串;但是字符串“国际和欧洲热门话题的新闻和分析时事和政治、商业、科学、文化、全球化和环境的背景信息。” 被保存。此字符串似乎是一个备用值(或默认值),只要元标记包含空字符串,就会返回该值。

经过进一步调查,发现go:description 是从http://www.dw.com 网页获取其元标记值。似乎是因为我的网页包含一个空字符串,返回值是从网站的根页面检索的。

我有以下关于$og_entry_content的问题:

  1. 如何确保将空字符串(不是备用值)保存到$og_entry_content

  2. 为什么仍然返回来自根页面的这个备用值?

谢谢。

【问题讨论】:

  • 我无法重现此内容。对我来说,在脚本的末尾 var_dump($og_entry_content); 导致 string(0) ""
  • 你没有尝试过替代get_meta_tags,看看这个端,这应该是一个空字符串
  • @RodrigoDuterte - get_meta_tags 导致同样的问题。
  • @JeffPuckettII - 感谢您的评论。我编辑了问题以更能反映问题。现在你应该可以重现它了。

标签: php dom domdocument getattribute


【解决方案1】:

回答

您的网址中有特殊字符,需要为URL encoded


说明

首先,假设...

$og_entry_title是正确的,包含页面标题,所以这里没有问题

...错了。

这个标题:

<meta property="og:title" content="تقرير استخباري اميركي: القاعدة تسيطر على غرب العراق | أخبار | DW.COM | 28.11.2006" />

和这个标题不一样:

<meta property="og:title" content="TOP STORIES | DW.COM" />

其次,大多数现代浏览器都非常棒,可以即时进行 URL 编码,并且仍然在地址栏中显示特殊字符。

您可以通过网络服务器see the response headers 了解更多信息。

<?php
$url = 'http://www.dw.com/ar/تقرير-استخباري-اميركي-القاعدة-تسيطر-على-غرب-العراق/a-2251369';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$url");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
$response = curl_exec($ch);

// Then, after your curl_exec call:
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
echo '
header
------
'.substr($response, 0, $header_size);

结果显示它无法识别 URL 与该页面之间的关联:

header
------
HTTP/1.1 301 Moved Permanently
Server: Apache-Coyote/1.1
Location: /
Content-Length: 0
Accept-Ranges: bytes
X-Varnish: 99639238
Date: Thu, 16 Jun 2016 15:42:51 GMT
Connection: keep-alive

HTTP Response Code 301 是(永久)重定向到另一个页面的通知。 Location: / 表示您应该只是转到主页。这是一种常见的草率做法,只是在某人不知道如何处理您时将其发送到主页。

默认情况下,Curl 不会跟随重定向,这就是我们能够检查 301 响应标头的方式。但是file_get_contents 将遵循重定向,这就是为什么您会得到与预期不同的内容。 (可能的例外情况:有一个bug report 有人注意到它并不总是遵循重定向。)

请注意,主页确实在其og:description 中有content

<?php
echo file_get_contents('http://www.dw.com/ar/تقرير-استخباري-اميركي-القاعدة-تسيطر-على-غرب-العراق/a-2251369');

这个输出的结果:

...

<meta property="og:description" content="News and analysis of the top international and European topics Current affairs and background information on poltics, business, science, culture, globalization and the environment. " />

...

<meta property="og:title" content="TOP STORIES | DW.COM" />

...


解决方案

你需要做的第一件事是rawurlencode网址:

$url = rawurlencode($url);

然后意识到rawurlencode 的命名很糟糕,因为valid URL 将包含HTML 协议http://https://,并且还可能包含用于分隔部分的斜线。这是有问题的,因为rawurlencode 会将冒号: 转换为%3A 并将斜线/ 转换为%2F,这会导致像http%3A%2F%2Fwww.dw.com%2Far%2F... 这样的无效URL。它应该被命名为 rawurlencode_parts_of_URL,但他们没有问我 :) 并引用 Phil Karlton 为他们辩护:

计算机科学中只有两件难事:缓存失效和命名

所以将斜杠和冒号转换回原来的形式:

$url = str_replace('%3A',':',str_replace('%2F','/',$url));

最后,您需要做的最后一件事是send a header to your clients to let them know what kind of font encoding to expect

header("content-type: text/html; charset=utf-8");

否则,您的客户可能正在阅读一些看起来像这样的gobbledygook

تÙ,رير استخباري اميركي: الÙ,اعدة تسيطر على غر ب العراÙ


最终产品

<?php

// let's see error output on screen while in development
// remove these lines for production, and use log files only
error_reporting(-1);
ini_set('display_errors', 'On');

$url = 'http://www.dw.com/ar/تقرير-استخباري-اميركي-القاعدة-تسيطر-على-غرب-العراق/a-2251369';

// URL encode special chars
$url = rawurlencode($url);

// fix colons and slashses for valid URL
$url = str_replace('%3A',':',str_replace('%2F','/',$url));

// make request
$webpage = file_get_contents($url);

$og_entry_title = "";
$og_entry_content = "";

$doc = new DOMDocument;
$doc->loadHTML($webpage);

$meta_tags = $doc->getElementsByTagName('meta');

foreach ($meta_tags as $meta_tag) {

    if ($meta_tag->getAttribute('property') == 'og:title') {
        $og_entry_title = $meta_tag->getAttribute('content');
    }

    if ($meta_tag->getAttribute('property') == 'og:description') {
        $og_entry_content = $meta_tag->getAttribute('content');
    }

}

// set the character set for the client
header("content-type: text/html; charset=utf-8");

// print the results
echo
'$og_entry_title: ' . $og_entry_title
.PHP_EOL.
'$og_entry_content: ' . $og_entry_content;

这个输出的结果:

$og_entry_title: تقرير استخباري اميركي: القاعدة تسيطر على غرب العراق | أخبار | DW.COM | 28.11.2006
$og_entry_content:

附录

如果您正在查看您的 error logs,并且您真的应该始终在开发时查看您的错误日志,那么您会注意到一连串的警告:

Warning: DOMDocument::loadHTML(): htmlParseStartTag: misplaced <html> tag in Entity, line: 4 in ...

Warning: DOMDocument::loadHTML(): htmlParseStartTag: misplaced <html> tag in Entity, line: 5 in ...

Warning: DOMDocument::loadHTML(): htmlParseStartTag: misplaced <html> tag in Entity, line: 6 in ...

Warning: DOMDocument::loadHTML(): htmlParseStartTag: misplaced <html> tag in Entity, line: 7 in ...

Warning: DOMDocument::loadHTML(): ID topMetaInner already defined in Entity, line: 300 in ...

Warning: DOMDocument::loadHTML(): ID langSelectTrigger already defined in Entity, line: 315 in ...

Warning: DOMDocument::loadHTML(): htmlParseEntityRef: no name in Entity, line: 546 in ...

Warning: DOMDocument::loadHTML(): htmlParseEntityRef: no name in Entity, line: 546 in ...

Warning: DOMDocument::loadHTML(): htmlParseEntityRef: no name in Entity, line: 548 in ...

Warning: DOMDocument::loadHTML(): htmlParseEntityRef: no name in Entity, line: 548 in ...

这是因为您尝试使用带有in-valid HTML and not well-formed XML documents 的DOMDocument 类。但这是一个不同问题的主题。

【讨论】:

  • 感谢您提供惊人的详细答案。我已经完成了你提到的所有事情,但我仍然遇到这个问题。我认为这是服务器没有将正确的页面发回给我的问题。我会进一步调查。
  • 真的吗?您没有得到与我在运行“最终产品”脚本时显示的相同的输出结果?我已更新答案以在屏幕上显示错误。你的输出是什么?
猜你喜欢
  • 1970-01-01
  • 2019-07-24
  • 2020-06-24
  • 2017-12-05
  • 1970-01-01
  • 2021-12-14
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多