@iSensetivity
бухгалтер, програміст-самоук

Почему не работает xpath?

Хочу спарсить таблицу, код
$fileByUrl = 'http://w1.c1.rada.gov.ua/pls/z7503/a002';
$referer = 'http://rada.gov.ua/';

	$ch=curl_init();
	curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_REFERER, $referer);
    curl_setopt($ch, CURLOPT_USERAGENT, "Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.9.168 Version/11.51");
	curl_setopt($ch, CURLOPT_URL, $fileByUrl);
	curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookie.txt');
	curl_setopt($ch, CURLOPT_COOKIEFILE,  'cookie.txt');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_ENCODING,'gzip');
	$str = curl_exec($ch);
	$info = curl_getinfo($ch);
	curl_close($ch);

$code = $info['http_code'];
	if($code == 200){
		$doc = new DOMDocument;
		$doc->load($str);
		
		$xpath = new DomXPath($doc);
		$res = $xpath->query('//*[@id="content-all"]/div[2]/div/table/tbody/tr[3]');
		foreach($res as $obj) {
			echo $obj->nodeValue;
        }

echo не вьіводит ничего.
  • Вопрос задан
  • 4258 просмотров
Решения вопроса 2
Lafafm
@Lafafm
Development lead, Full stack generalist
Лично я не знаю как правильно, но почему бы не использовать file_get_contents, и после получения страницы с помощью простого регулярного выражения взять таблицу?

Вот решение:
$html = file_get_contents("http://w1.c1.rada.gov.ua/pls/z7503/a002");
preg_match('#<table  class="striped Centered" WIDTH="100%" cellspacing=0 cellpadding=3>(.+?)</table>#is', $html, $arr);
echo $arr[0];
Ответ написан
nowm
@nowm
В первую очередь — из-за этого:

$doc->load($str);

«load» — это для загрузки файлов и в качестве параметра ей нужно давать путь к файлу. Если вы хотите загрузить строку, нужно использовать функцию «loadHTML».

Дальше у вас появится куча предупреждений. Если появится сообщение про то, что непонятки с кодировкой появились, от него можно избавиться, если поправить строку с loadHTML:

$doc->loadHTML(mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8'));


Кроме строки про кодировку будет ещё куча предупреждений, вроде:

Warning: DOMDocument::loadHTML(): Opening and ending tag mismatch: li and div in Entity
Warning: DOMDocument::loadHTML(): htmlParseEntityRef: expecting ';' in Entity
Warning: DOMDocument::loadHTML(): Opening and ending tag mismatch: td and b in Entity


Чтобы эти уведомления не засоряли эфир, можно добавить символ «@» при вызове «loadHTML»:

@$doc->loadHTML($str);

Дальше, чтобы удостовериться, что те узлы, которые вы пытаетесь искать, всё-таки существуют, можно вывести список вообще всех узлов, вот так:

$res = $xpath->query('.//*');
foreach($res as $obj) {
	echo $obj->getNodePath() . "\n\r";
}


Из листинга будет видно, что упоминание связки «table/tbody/tr» некорректно. «TBODY» там нет. Такой XPath-запрос сработает нормально в FirePath из Firefox, например. И работает он из-за того, что Firefox самостоятельно достраивает DOM документа до идеального по его мнению состояния — например, добавляет «TBODY», где его нет, закрывает незакрытые теги и так далее.

В ситуации с DomDocument и DomXPath лучше смотреть чистый исходный код страницы и строить запросы именно по исходному коду, а не по сгенерированному браузером DOM.

В вашей ситуации нужно из запроса просто убрать «tbody». Получится такой запрос:

//*[@id="content-all"]/div[2]/div/table/tr[3]

Как я вижу, решение уже появилось, но, вообще, такой подход, который я описал, поможет искать ошибки в подобных ситуациях.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
19 апр. 2024, в 23:00
5000 руб./за проект
19 апр. 2024, в 20:43
20000 руб./за проект