使用Xpath从亚马逊获取价格



在以下页面中:

http://www.amazon.com/Jessica-Simpson-Womens-Double-Breasted/dp/B00K65ZMCA/ref=sr_1_4_mc/185-0705108-6790969?s=apparel&ie=UTF8&qid=1413083859&sr=1-4我正在尝试使用表达式获取价格

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

但结果是一个空变量。

有趣的是,在其他亚马逊页面中,例如此页面: http://www.amazon.com/SanDisk-Cruzer-Frustration-Free-Packaging--SDCZ36-032G-AFFP/dp/B007JR532M/ref=sr_1_1?s=pc&ie=UTF8&qid=1413084653&sr=1-1&keywords=usb

我确实有一个有效的表达式

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

但我什至不知道为什么,因为在页面的源代码中我找不到这样的标签......那么它为什么有效呢?我如何在第一页上获得价格?谢谢!

在使用 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 %sn", 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的两个示例地址表明您要查找的信息是包含.listprice.priceLarge#priceBlock

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

这将导致以下输出:

List Price: $48.99
Price: $14.99

如果您缺少某些内容,按照示例中$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">
  +"nn  "
  `<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        nnn    "
    | |   +<span>
    | |   | `"n        n    "
    | |   +"n    nnnnnnnnnn    nnnnnn nnnnn& "
    | |   +<b>
    | |   | `"FREE Shipping"
    | |   +" on orders over $35.nnnn"
    | |   +<a href="/gp/help/customer/display.html/ref=mk_sss_dp_1/191-4381493-1931545?ie=UTF8&no...">
    | |   | `"Details"
    | |   `"nnnnnnnnn    nn    n    nnnnnn      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.00n        (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..."

您可以在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;
    }
}

最新更新