Пустой атрибут в DOM возвращает неожиданное резервное значение.

Я получил содержимое этой веб-страницы 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. Почему это резервное значение все равно возвращается с корневой страницы?

Спасибо.


person Greeso    schedule 14.06.2016    source источник
comment
Я не могу воспроизвести это. для меня в конце скрипта var_dump($og_entry_content); получается string(0) ""   -  person Jeff Puckett    schedule 15.06.2016
comment
ты не пробовал альтернативу get_meta_tags, если посмотреть на этот конец, это должна быть пустая строка   -  person Kevin    schedule 15.06.2016
comment
@RodrigoDuterte - get_meta_tags вызывает ту же проблему.   -  person Greeso    schedule 15.06.2016
comment
@JeffPuckettII - Спасибо за ваш комментарий. Я отредактировал вопрос, чтобы он больше отражал проблему. Теперь вы должны быть в состоянии воспроизвести его.   -  person Greeso    schedule 15.06.2016


Ответы (1)


Отвечать

Ваш веб-адрес содержит специальные символы, которые должны быть закодированы URL.


Объяснение

Во-первых, предположение, что...

$og_entry_title правильно и содержит заголовок страницы, так что здесь нет проблем

...неправильно.

Это название:

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

не совпадает с этим заголовком:

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

Во-вторых, большинство современных браузеров достаточно хороши, чтобы кодировать URL-адреса на лету и при этом отображать специальные символы в адресной строке.

Вы можете просмотреть заголовки ответов с веб-сервера для получения дополнительной информации.

<?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 301 — это уведомление о (постоянной) переадресации на другую страницу. Location: / указывает, что вам следует просто перейти на главную страницу. Это обычная небрежная практика — просто отправлять кого-то на домашнюю страницу, когда они не знают, что с вами делать.

Curl по умолчанию не следует перенаправлениям, поэтому мы можем проверить заголовок ответа 301. Но file_get_contents будет следовать перенаправлениям, поэтому вы получаете другой контент, чем ожидали. (С возможными исключениями: существует отчет об ошибке, где некоторые отмечают, что он не не всегда следуйте перенаправлениям.)

Обратите внимание, что на главной странице есть content в og:description:

<?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 неудачно, потому что действительный URL будет содержать протокол HTML http:// или https:// и также может содержать косую черту для разделения частей. Это проблематично, потому что rawurlencode преобразует двоеточие : в %3A, а косую черту / в %2F, что делает URL-адрес недействительным, например http%3A%2F%2Fwww.dw.com%2Far%2F.... Он должен был называться rawurlencode_parts_of_URL, но меня не спросили :) И процитирую Фила Карлтона в их защиту:

В компьютерных науках есть только две сложные вещи: аннулирование кеша и именование вещей.

Поэтому верните косую черту и двоеточие в исходную форму:

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

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

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

В противном случае ваши клиенты могут читать тарабарщину, которая может выглядеть что-то вроде этого:

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


Конечный продукт

<?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:

Приложение

Если вы просматриваете свои журналы ошибок и вы на самом деле следует всегда просматривать журналы ошибок при разработке, тогда вы заметите множество предупреждений:

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 ...

Это связано с тем, что вы пытаетесь использовать класс DOMDocument с в - действительный HTML и неправильно сформированные XML-документы. Но это тема для другого вопроса.

person Jeff Puckett    schedule 16.06.2016
comment
Спасибо за удивительный подробный ответ. Я сделал все, что вы упомянули, но у меня все еще есть эта проблема. Я думаю, это проблема с тем, что сервер не отправляет мне нужную страницу с самого начала. Я буду исследовать дальше. - person Greeso; 17.06.2016
comment
В самом деле? вы не получаете тот же результат вывода, который я показываю при запуске скрипта конечного продукта? Я обновил ответ, чтобы показать ошибки на экране. каков ваш выход? - person Jeff Puckett; 17.06.2016