从网页获取产品名称时遇到问题
Posted
技术标签:
【中文标题】从网页获取产品名称时遇到问题【英文标题】:Trouble getting the name of a product from a webpage 【发布时间】:2019-02-23 16:17:55 【问题描述】:我用 php 编写了一个脚本来抓取位于网页右上角的产品的title
。 title
显示为 Gucci
。
当我执行下面的脚本时,它给了我一个错误Notice: Trying to get property 'plaintext' of non-object in C:\xampp\htdocs\runcode\testfile.php on line 16
。
我如何才能从该网页中仅获取名称 Gucci
?
Link to the url
到目前为止我已经写了:
<?php
include "simple_html_dom.php";
$link = "https://www.farfetch.com//bd/shopping/men/gucci-rhyton-web-print-leather-sneaker-item-12964878.aspx";
function get_content($url)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('User-Agent: Mozilla/5.0',));
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$htmlContent = curl_exec($ch);
curl_close($ch);
$dom = new simple_html_dom();
$dom->load($htmlContent);
$itemTitle = $dom->find('#bannerComponents-Container [itemprop="name"]', 0)->plaintext;
echo "$itemTitle";
get_content($link);
?>
顺便说一句,我在脚本中使用的选择器完美无缺。
为了消除混淆,我从 页面源 复制了一大块 html 元素,这些元素既不是动态生成也不是 javascript 加密,所以我找不到任何理由curl 无法处理:
<div class="cdb2b6" id="bannerComponents-Container">
<p class="_41db0e _527bd9 eda00d" data-tstid="merchandiseTag">New Season</p>
<div class="_1c3e57">
<h1 class="_61cb2e" itemProp="brand" itemscope="" itemType="http://schema.org/Brand">
<a href="/bd/shopping/men/gucci/items.aspx" class="fd9e8e e484bf _4a941d f140b0" data-trk="pp_infobrd" data-tstid="cardInfo-title" itemProp="url" aria-label="Gucci">
<span itemProp="name">Gucci</span>
</a>
</h1>
</div>
</div>
后记:很遗憾,我不得不展示一个来自另一种语言的真实生活示例,以确保名称 Gucci
不是动态生成的,因为很少有 cmets 并且答案已经表明
以下脚本是用python编写的(使用无法处理动态内容的requests
模块):
import requests
from bs4 import BeautifulSoup
url = "https://www.farfetch.com//bd/shopping/men/gucci-rhyton-web-print-leather-sneaker-item-12964878.aspx"
with requests.Session() as s:
s.headers["User-Agent"] = "Mozilla/5.0"
res = s.get(url)
soup = BeautifulSoup(res.text,"lxml")
item = soup.select_one('#bannerComponents-Container [itemprop="name"]').text
print(item)
输出它产生:
Gucci
现在,很明显我要查找的内容是静态的。
请查看下图以识别我已经用铅笔标记的title
。
【问题讨论】:
$itemTitle = $dom->find('#bannerComponents-Container [itemprop="name"]', 0);
是否返回一个对象?
输入 HTML 的结构是什么?请将其包含在问题中。
试试[itemProp=name]
(由于某种原因大写P)
请查看编辑。为了清晰起见,我添加了一些材料。
【参考方案1】:
@tmadam 已经解决了这个问题,我只是想补充一点,今天没有充分的理由使用 simple_html_dom,似乎没有维护,开发在 2014 年停止,有很多未解决的错误报告,最重要的是,DOMDocument 和 DOMXPath 可以做到几乎所有 simple_html_dom 都可以并且得到维护,并且是 PHP 的一个集成部分,这意味着没有什么可以包含/捆绑到您的脚本中。用 DOMDocument 和 DOMXPath 解析它看起来像:
$htmlContent = curl_exec($ch);
curl_close($ch);
fclose($cookieFileh); // thanks to tmpfile(), this also deletes the cookie file.
$dom = @DOMDocument::loadHTML($htmlContent);
$xp=new DOMXPath($dom);
$itemTitle = $xp->query('//*[@id="bannerComponents-Container"]//*[@itemprop="name"]')->item(0)->textContent;
echo $itemTitle;
【讨论】:
如果我希望使用 css 选择器而不是 xpath 来满足您的上述方法,那么修改后的部分会是什么样子?感谢您的宝贵意见@hanshenrik。 @asmitu 对不起,afaik 没有任何内置的 PHP 支持 CSS - 但是如果你使用 Symfony,他们有这个CSS-to-xpath converter,在这种情况下你可以运行$converter = new Symfony\Component\CssSelector\CssSelectorConverter(); $itemTitle = $xp->query($converter->toXPath('#bannerComponents-Container [itemprop="name"]'))->item(0)->textContent;
【参考方案2】:
成功的 Python 脚本和 PHP 脚本的主要区别在于会话的使用。您的 PHP 脚本不使用 cookie,这会触发来自服务器的不同响应。
我们有两个选择:
更改选择器。如Mark's answer 中所述,该项目仍在 html 上,但在不同的标记中。我们可以用这个选择器得到它:
'a[itemprop="brand"]'
使用 cookie。如果我们使用CURLOPT_COOKIESESSION
和一个临时文件来写入/读取cookie,我们可以获得与您的Python 脚本相同的响应。
function get_content($url)
$cookieFileh = tmpfile();
$cookieFile=stream_get_meta_data($cookieFileh)['uri'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0');
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieFile);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookieFile);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
$htmlContent = curl_exec($ch);
curl_close($ch);
fclose($cookieFileh); // thanks to tmpfile(), this also deletes the cookie file.
$dom = new simple_html_dom();
$dom->load($htmlContent);
$itemTitle = $dom->find('#bannerComponents-Container [itemprop="name"]', 0)->plaintext;
echo "$itemTitle";
$link = "https://www.farfetch.com/bd/shopping/men/gucci-rhyton-web-print-leather-sneaker-item-12964878.aspx";
get_content($link);
//Gucci
这个脚本执行两个请求;第一个请求将 cookie 写入文件,第二个请求读取并使用它们。
在这种情况下,服务器返回一个压缩响应,所以我使用CURLOPT_ENCODING
来解压缩内容。
由于您仅使用标头来设置用户代理,因此最好使用 CURLOPT_USERAGENT
选项。
我已将 CURLOPT_SSL_VERIFYPEER
设置为 false,因为我尚未设置证书,并且 CURL
无法使用 HTTPS。如果您可以与 HTTPS 站点通信,出于安全原因,最好不要使用此选项。如果没有,您可以使用CURLOPT_CAINFO
设置证书。
【讨论】:
你一直是我的救星。您的解决方案永远不会误入歧途。非常感谢。我有两个小问题: 1. 您的解决方案确实与Notice: tempnam(): file created in the system's temporary directory in C:\xampp\htdocs\runcode\testfile.php on line 5
这一行一起获得了正确答案。我怎样才能摆脱这个错误。 2.有什么方法可以让我使用php
以编程方式获取page source
或html content
,就像我们在python中使用res = requests.get(url) ; print(res.text)
一样?
这不是错误,而是通知我们有关临时文件的通知。你可以用@
压制它,例如:$cookieFile = @tempnam("/cookies", "CURLCOOKIE");
。关于您的第二个问题,您可以直接回显响应内容,如果这就是您的意思,例如echo $htmlContent;
。
请查看this post 了解我获取源代码或html 内容的意思。我删除了该帖子,但现在为您取消删除。 顺便说一句,echoing $htmlContent;
我可以看到脚本只是打开了那个网页,就像我们在浏览器中打开一个网页而不是 html 的东西一样。你应该得到赏金。不过,只要它还活着,我们就等着吧。
好的,我想我明白你的意思了。您正在尝试在浏览器上打印 HTML 代码,对吗?问题在于,Web 浏览器旨在解释 HTML 代码,因此您只能看到文本。对不起,我不能帮你。但是让我搜索答案,如果我发现任何有用的信息,我会告诉你。
如果您无权访问 /cookies 文件夹会怎样?使用 tmpfile。除了特殊的临时文件夹(由 sys_get_temp_dir() 返回 - 但 tmpfile() 将负责为您定位文件夹并负责清理句柄关闭/或脚本终止时的文件。)【参考方案3】:
您的选择器确实可以在浏览器中使用,但是当您使用 curl 获取页面源时,您的选择器不存在。
尝试将卷曲的页面保存在终端中,您会发现页面结构与您在浏览器中看到的不同。
对于大多数现代网站来说都是如此,因为它们大量使用 Javascript,而 curl 不会为您运行 javascript。
我将 curl 结果保存到一个文件中,品牌信息如下所示:
<a itemprop="brand" class="generic" data-tstid="Label_ItemBrand" href="/bd/shopping/men/gucci/items.aspx" dir="ltr">Gucci</a>
【讨论】:
以上是关于从网页获取产品名称时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章
我从所有 $Users 获取信息到邮件时遇到问题,只能从 1 台机器获取信息
在 UIAutomation 中从 UIAElementArray 中按名称获取元素时遇到问题