Получение цены от Amazon с помощью Xpath

на следующей странице:

http://www.amazon.com/Jessica-Simpson-Womens-Double-Breasted/dp/B00K65ZMCA/ref=sr_1_4_mc/185-0705108-6790969?s=apparel&т = UTF8&QID = 1413083859&ср = 1-4
Я пытаюсь получить цену с выражением

'//span[@id="priceblock_ourprice"]'

но результат — пустая переменная.

интересная часть в том, что на других страницах Amazon, как этот
: http://www.amazon.com/SanDisk-Cruzer-Frustration-Free-Packaging—SDCZ36-032G-AFFP/dp/B007JR532M/ref=sr_1_1?s=pc&т = UTF8&QID = 1413084653&ср = 1-1&Ключевые слова = USB

У меня есть выражение, которое работает

'//b[@class="priceLarge"]'

Но я даже не знаю почему, потому что в источнике страницы я не могу найти такой тег …
Так почему же это работает? и как мне узнать цену на первой странице?
Спасибо!

0

Решение

При работе с PHP вы не можете просто принять то, что вы видите в исходном тексте браузера как должное.

Вместо этого вам сначала нужно извлечь контент с помощью PHP, а затем посмотреть на источник там:

$url    = 'http://www.amazon.com/ ... ';
$buffer = file_get_contents($url);

Переменная $buffer затем содержит HTML-код, который вы будете очищать.

Сделано, что с вашего примера ссылки покажут, что для первого и второго адреса оба имеют элемент .priceLarge содержит, вероятно, то, что вы ищете:

<span class="priceLarge">$168.00</span>
<b class="priceLarge">$14.99</b>

Узнав, где находятся данные, которые вы ищете, вы можете создать DOMDocument:

$doc          = new DOMDocument();
$doc->recover = true;
$saved        = libxml_use_internal_errors(true);
$doc->loadHTML($buffer);

Вас также могут заинтересовать ошибки разбора:

/** @var array|LibXMLError[] $errors */
$errors = libxml_get_errors();
foreach ($errors as $error) {
printf(
"%s: (%d) [%' 3d] #%05d:%' -4d %s\n", get_class($error), $error->level, $error->code, $error->line,
$error->column, rtrim($error->message)
);
}
libxml_use_internal_errors($saved);

так как это способ, которым DOMDocument говорит вам, где возникли проблемы. Например, дубликаты значений ID.

После загрузки буфера в DOMDocument Вы можете создать DOMXPath:

$xp = new DOMXPath($doc);

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

Например, эти два примера адресов в HTML показали, что искомая информация #priceBlock оба содержащие .listprice а также .priceLarge:

$priceBlock = $doc->getElementById('priceBlock');
printf(
"List Price: %s\nPrice: %s\n", $xp->evaluate('string(.//*[@class="listprice"])', $priceBlock)
, $xp->evaluate('string(.//*[@class="priceLarge"])', $priceBlock)
);

Что приведет к следующему выводу:

List Price: $48.99
Price: $14.99

Если вам что-то не хватает, получение родительского элемента section в переменную как $priceBlock в этом примере позволяет не только использовать относительные пути для Xpath, но также может помочь с отладкой в ​​случае, если вам не хватает более подробной информации:

echo $doc->saveHTML($priceBlock);

Это выводит весь <div> который содержит всю информацию о ценах, например.

Если вы настроите себе некоторые вспомогательные классы, вы можете в дальнейшем использовать это для получения другой полезной информации из документа для ее очистки, например, показа всех комбинаций тегов / классов в ценовом блоке:

// you can find StringCollector at the end of the answer
$tagsWithClass = new StringCollector();
foreach ($xp->evaluate('.//*/@class', $priceBlock) as $class) {
$tagsWithClass->add(sprintf("%s.%s", $class->parentNode->tagName, $class->value));
}
echo $tagsWithClass;

Затем выводится список собранных строк и их количество, которое является здесь тэгами с их значениями атрибута класса:

table.product (1)
td.priceBlockLabel (3)
span.listprice (1)
td.priceBlockLabelPrice (1)
b.priceLarge (1)
tr.youSavePriceRow (1)
td.price (1)

Как вы можете видеть, это из первого примера URL, потому что .pricelarge с <b> элемент.

Это относительно простой помощник, для которого вы можете сделать больше, например, отобразить всю HTML-структуру в виде дерева.

DomTree::dump($priceBlock);

Это даст вам следующий результат, который учитывает лучшее потребление, чем просто DOMDocument::saveHTML($node):

`<div id="priceBlock" class="buying">
+"\n\n  "`<table class="product">
+<tr>
| +<td class="priceBlockLabel">
| | `"List Price:"| +"\n    "| +<td>
| | `<span id="listPriceValue" class="listprice">
| |   `"$48.99"| `"\n  "+<tr id="actualPriceRow">
| +<td id="actualPriceLabel" class="priceBlockLabelPrice">
| | `"Price:"| +"\n    "| +<td id="actualPriceContent">
| | +<span id="actualPriceValue">
| | | `<b class="priceLarge">
| | |   `"$14.99"| | +"\n    "| | `<span id="actualPriceExtraMessaging">
| |   +"\n        \n\n\n    "| |   +<span>
| |   | `"\n        \n    "| |   +"\n    \n\n\n\n\n\n\n\n\n\n    \n\n\n\n\n\n \n\n\n\n\n& "| |   +<b>
| |   | `"FREE Shipping"| |   +" on orders over $35.\n\n\n\n"| |   +<a href="/gp/help/customer/display.html/ref=mk_sss_dp_1/191-4381493-1931545?ie=UTF8&no...">
| |   | `"Details"| |   `"\n\n\n\n\n\n\n\n\n    \n\n    \n    \n\n\n\n\n\n      \n"| `"\n"+<tr id="dealPriceRow">
| +<td id="dealPriceLabel" class="priceBlockLabel">
| | `"Deal Price: "| +"\n  "| +<td id="dealPriceContent">
| | +"\n    "| | +<span id="dealPriceValue">
| | +"\n    "| | +<span id="dealPriceExtraMessaging">
| | `"\n  "| `"\n"+<script>
| `[XML_CDATA_SECTION_NODE (4)]
+<tr id="youSaveRow" class="youSavePriceRow">
| +<td id="youSaveLabel" class="priceBlockLabel">
| | `"You Save:"| +"\n    "| +<td id="youSaveContent" class="price">
| | +<span id="youSaveValue">
| | | `"$34.00\n        (69%)"| | `"\n    "| `"\n  "`<tr>
+<td>
`<td>
`<span>
`"o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o..."

Вы можете найти его в ответ на Отладка объекта DOMDocument в PHP И в другой. код доступен на GitHub в виде гист.


StringCollector вспомогательный класс

/**
* Class StringCollector
*
* Collect strings and count them
*/
class StringCollector implements IteratorAggregate
{
private $array;

public function add($string)
{
$entry = & $this->array[$string];
$entry++;
}

public function getIterator()
{
return new ArrayIterator($this->array);
}

public function __toString()
{
$buffer = '';
foreach ($this as $string => $count) {
$buffer .= sprintf("%s (%d)\n", $string, $count);
}
return $buffer;
}
}
2

Другие решения

Других решений пока нет …