node.js - 使用 Puppeteer 刮取亚马逊价格
问题描述
我试图抓取亚马逊页面以获取产品的价格,但抓取结果给我的金额与实际浏览器中显示的金额不同。我检查了很多次,但无法得到正确的结果。它给了我 89.99 美元,而在实际网站上,该产品的价格为 58.95 美元。亚马逊是故意混淆网络爬虫和爬虫还是我的错?我在 NodeJS 中使用了 Puppeteer 和 JSDom。
节点代码:
const puppeteer = require('puppeteer');
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const url = 'https://www.amazon.com/Razer-DeathAdder-Chroma-Multi-Color-Comfortable/dp/B00MYTSDU4/ref=sr_1_2?dchild=1&keywords=Deathadder%2BChroma&qid=1625425444&sr=8-2&th=1';
async function configureBrowser() {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
return page;
}
async function pageContent() {
let page = await configureBrowser();
// await page.reload();
let html = await page.evaluate(() => document.body.innerHTML);
await page.close();
console.log(new JSDOM(html).window.document.querySelector('#priceblock_ourprice').textContent);
// return new JSDOM(html).window.document.querySelector('#priceblock_ourprice').textContent;
}
module.exports = pageContent;
解决方案
将 JSDom 与 Puppeteer 结合起来很奇怪。Puppeteer 已经拥有一整套选择器,并且可以在网页内的实际合法 DOM 上工作,因此使用像 JSDom 这样的模拟 DOM 转储和重新解析整个 HTML 有点像买一辆全新的自行车,然后随身携带而不是骑它。
笑话:如果您使用 JSDom 和 Puppeteer,您将被收取额外的 30 美元...
当页面动态注入内容时,只需单独使用 Puppeteer:
const puppeteer = require("puppeteer");
const url = "https://www.amazon.com/Razer-DeathAdder-Chroma-Multi-Color-Comfortable/dp/B00MYTSDU4/ref=sr_1_2?dchild=1&keywords=Deathadder%2BChroma&qid=1625425444&sr=8-2&th=1";
let browser;
(async () => {
browser = await puppeteer.launch();
const [page] = await browser.pages();
await page.goto(url, {waitUntil: "networkidle0"});
const selector = "#priceblock_ourprice";
await page.waitForSelector(selector);
const price = await page.$eval(selector, el => el.innerText);
console.log(price); // => $58.95
})()
.catch(err => console.error(err))
.finally(async () => await browser.close())
;
由于在这种情况下,您想要的价格似乎直接融入了静态 HTML,因此您可以跳过 Puppeteer 并使用 JSDom 以及基本的 HTTP 请求来获取数据:
<span id="priceblock_ourprice" class="a-size-medium a-color-price priceBlockBuyingPriceString">$58.95</span>
const axios = require("axios");
const {JSDOM} = require("jsdom");
const url = "https://www.amazon.com/Razer-DeathAdder-Chroma-Multi-Color-Comfortable/dp/B00MYTSDU4/ref=sr_1_2?dchild=1&keywords=Deathadder%2BChroma&qid=1625425444&sr=8-2&th=1";
(async () => {
const {data: html} = await axios.get(url, {
headers: {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Accept-Encoding": "gzip",
"Accept-Language": "en-US,en;q=0.9,es;q=0.8",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36",
"Referer": "https://www.google.com/"
}
});
const price = new JSDOM(html)
.window
.document
.querySelector("#priceblock_ourprice")
.textContent
;
console.log(price); // => $58.95
})()
.catch(err => console.error(err))
;
亚马逊一直在改变价格,可能存在地区差异。根据您运行此程序的地点和时间,您可能无法获得 58.95 美元。您的方法为我在加利福尼亚运行它提供了正确的价格。您可能在远程服务器上执行它,亚马逊根据位置或其他因素提供不同的价格。
最后,您的方法在返回时放弃了对浏览器对象的引用 configureBrowser
,这意味着您有内存泄漏并且进程可能会挂起。跟踪浏览器对象并.close()
在完成后调用它。
推荐阅读
- python - 仅获取列表中的唯一项目并通过将其放入字典来跟踪更改 - Pandas Python
- vhdl - 当可以约束类型时,子类型的意义何在?
- android - 旋转回纵向并调用 notifyDataSetChanged 后,Android RecyclerViewAdapter 项目宽度错误
- c++ - 如何在两棵树共有的函数中创建参数?
- ansible - 如何在ansible中循环条件字典
- python - 爆炸和拆分列导致数据不匹配
- java - OpenGLES“顶点属性索引超出边界”错误,尽管渲染效果很好
- doctrine-orm - 使用计数进行子查询
- python - 我已经包含了我遇到的错误图像
- node.js - 多对多关联抛出“A 未关联到 B”错误