我使用Curl,XPath和PHP来从HTML源代码中删除产品名称和价格。这是一个类似于我正在检查的源代码的示例:
<div class="Gamesdb">
<p class="media-title">
<a href="/Games/Console/4-/105/Bluetooth-Headset/">Bluetooth Headset</a>
</p>
<p class="sub-title"> Console </p>
<p class="rating star-50">
<a href="/Games/Console/4-/105/Bluetooth-Headset/ProductReviews.html">(1)</a>
</p>
<p class="mt5">
<span class="price-preffix">
<a href="/Games/Console/4-/105/Bluetooth-Headset/">1 New</a>
from
</span>
<a class="wt-link" href="/Games/Console/4-/105/Bluetooth-Headset/">
<span class="price">
<em>£34</em>
.99
</span>
<span class="free-delivery"> FREE delivery</span>
</a>
</p>
<p class="mt10">
<a class="primary button" href="/Games/Console/4-/105/Bluetooth-Headset/">
Product Details
<span style="color: rgb(255, 255, 255); margin-left: 6px; font-size: 16px;">»</span>
</a>
</p>
</div>
我想提取媒体标题,即:
<p class="media-title">
<a href="/Games/Console/4-/105/Bluetooth-Headset/">Bluetooth Headset</a>
</p>
仅当以下价格等级出现时:
<span class="price">
<em>£34</em>
.99
</span>
列出的许多其他产品都不包含它。 我需要提取产品名称和价格,或者根本不提取任何内容,然后转到下一个产品。
以下是我目前正在使用的代码示例,该示例无论其他条件如何都能有效获取所有结果:
$results=file_get_contents('SCRAPEDHTML.txt');
$html = new DOMDocument();
@$html->loadHtml($results);
$xpath = new DOMXPath($html);
$nodelist = $xpath->query('//p[@class="media-title"]|//span[@class="price"]');
foreach ($nodelist as $n){
$results2[]=$n->nodeValue;
}
我相信使用正确的xpath查询是可能的,但到目前为止还无法实现。非常感谢提前。
答案 0 :(得分:0)
您不能拥有一个同时返回产品名称及其价格的XPath。我的建议是首先获得包含两个信息的所有div
个节点:
//div[p[@class='media-title'] and //span[@class='price']]
(所有div
个节点,其p
子节点具有类media-title
,span
后代节点具有类price
');然后循环所有返回的节点,并使用另外两个XPath来提取产品名称和价格:
p[@class='media-title']
和
//span[@class='price']
答案 1 :(得分:0)
我假设每div.Gamesdb
只有一个“项目”。如果没有,源html中可能没有足够的结构来单独使用xpath。您可能需要索引产品名称并查找匹配产品名称附近的价格。
您可以使用单个巨型XPath执行此操作,但我建议您使用多个XPath。我将展示两种方式。
首先创建您的DOMXPath
并注册帮助以匹配类名。
// This helper is the equivalent to the XPath:
// contains(concat(' ',normalize-space(@attr),' '), ' $token ')
// It's not necessary, but it's a bit easier to read and more
// bulletproof than @ATTR="TOKEN"
function has_token($attr, $token)
{
$attr = $attr[0];
$regex = '/(?:^|\s)'.preg_quote($token,'/').'(?:\s|$)/Su';
return (bool) preg_match($regex, $attr->value);
}
$xp = new DOMXPath($d);
$xp->registerNamespace("php", "http://php.net/xpath");
$xp->registerPHPFunctions("has_token");
然后您可以使用巨型XPath:
$xp_container = '/html/body//div[php:function("has_token", @class, "Gamesdb")]';
$xp_title = 'p[php:function("has_token", @class, "media-title")]';
$xp_price = '//span[php:function("has_token", @class, "price")]';
$xp_titles_prices = "$xp_container[{$xp_title}][{$xp_price}]/{$xp_title} | $xp_container[{$xp_title}][{$xp_price}]{$xp_price}";
$nodes = $xp->query($xp_items);
$items = array();
$i = 0; // enumerator
foreach ($nodes as $node) {
$key = ($node->nodeName==='p') ? 'title' : 'price';
$value = '';
switch ($key) {
case 'price':
// remove inner whitespace
$value = preg_replace('/\s+/Su', '', trim($node->textContent));
break;
case 'title':
$value = preg_replace('/\s+/Su', ' ', trim($node->textContent));
break;
}
$items[(int) floor($i/2)][$key] = $value;
$i += 1;
}
然而,整体代码很脆弱且不清楚。 XPath联合运算符(|
)按文档顺序返回节点,因此我们无法将列表平分。 PHP代码必须遍历节点列表中的每个项目,并使用DOM确定哪个字段对应于此数据。如果您想扩展代码以收集第三项(例如价格),请考虑您必须进行的更改。现在想象一下,从现在起三个月后进行这些更改,这段代码不再是你的想法了。
我建议您使用多个XPath调用,并使用PHP而不是XPath执行“我们是否有价格和标题的数据”:
$xpitems = '/html/body//div[php:function("has_token", @class, "Gamesdb")]';
// below use $xpitems context:
$xptitle = 'normalize-space(p[php:function("has_token", @class, "media-title")])';
$xpprice = 'normalize-space(//span[php:function("has_token", @class, "price")])';
$nodeitems = $xp->query($xpitems);
$items = array();
foreach ($nodeitems as $nodeitem) {
$item = array(
'title' => $xp->evaluate($xptitle, $nodeitem),
'price' => str_replace(' ', '', $xp->evaluate($xpprice, $nodeitem)),
);
// Only add this item if we have data for *all* fields:
if (count(array_filter($item)) === count($item)) {
$items[] = $item;
}
}
这更容易阅读和理解,并且将来更容易扩展。