Puppeteer ,带回空白数组
Puppeteer , bringing back blank array
我正在尝试从 ebay 获取产品并在亚马逊上打开它们。
到目前为止,我已经在亚马逊上搜索了它们,但我正在努力从搜索结果中选择产品。
目前它正在输出一个空白数组,我不确定为什么。在没有 grabTitles 和 for 循环的单独脚本中进行了测试。所以我猜这里面有什么东西导致了问题。
这里有什么我遗漏的东西阻止了 prodResults 的数据返回吗?
const puppeteer = require('puppeteer');
const URL = "https://www.amazon.co.uk/";
const selectors = {
searchBox: '#twotabsearchtextbox',
productLinks: 'span.a-size-base-plus.a-color-base.a-text-normal',
productTitle: '#productTitle'
};
(async() => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.goto('https://www.ebay.co.uk/sch/jmp_supplies/m.html?_trkparms=folent%3Ajmp_supplies%7Cfolenttp%3A1&rt=nc&_trksid=p2046732.m1684');
//Get product titles from ebay
const grabTitles = await page.evaluate(() => {
const itemTitles = document.querySelectorAll('#e1-11 > #ResultSetItems > #ListViewInner > li > .lvtitle > .vip');
var items = []
itemTitles.forEach((tag) => {
items.push(tag.innerText)
})
return items
})
//Search for the products on amazon in a new tab for each product
for (i = 0; i < grabTitles.length; i++) {
const page = await browser.newPage();
await page.goto(URL)
await page.type(selectors.searchBox, grabTitles[i++])
await page.keyboard.press('Enter');
//get product titles from amazon search results
const prodResults = await page.evaluate(() => {
const prodTitles = document.querySelectorAll('span.a-size-medium.a-color-base.a-text-normal');
let results = []
prodTitles.forEach((tag) => {
results.push(tag.innerText)
})
return results
})
console.log(prodResults)
}
})()
您遇到了 Puppeteer 的一个古老问题,知道页面何时完全完成呈现或加载。
您可以尝试添加以下内容:
await page.waitForNavigation({ waitUntil: 'networkidle2' })
await page.waitForTimeout(10000)
通常我发现 networkidle2
并不总是足够可靠,所以我添加了一个任意的额外 waitForTimeout
。您需要调整超时值(10000 = 10 秒)才能得到您想要的东西,我知道这不是很理想,但我还没有找到更好的方法。
脚本存在一些潜在问题:
await page.keyboard.press('Enter');
触发导航,但您的代码从不等待导航完成后再尝试 select 结果元素。使用 waitForNavigation
、waitForSelector
或 waitForFunction
(不是 waitForTimeout
)。
如果您确实要等待导航,则需要使用 Promise.all
的特殊模式来避免竞争条件,如 docs 中所示。
此外,您可以通过自己构建字符串直接转到搜索 URL 来跳过页面加载。这应该会提供显着的加速。
您的代码为每个需要处理的项目生成一个新页面,但这些页面永远不会关闭。我看到 grabTitles.length
为 60。因此您将打开 60 个选项卡。这浪费了很多资源。在我的机器上,它可能会挂起所有东西。我建议制作一个页面并反复浏览它,或者在完成后关闭每一页。如果您想要并行性,请考虑同时执行几页任务 queue 或 运行。
grabTitles[i++]
-- 为什么在这里递增 i
?它已经通过循环递增,因此这似乎跳过了元素,除非你的 select 或你有重复的元素或者你有其他原因要这样做。
span.a-size-medium
对我不起作用,可能是 locality-specific。我看到 a span.a-size-base-plus.a-color-base.a-text-normal
,但您可能需要调整一下才能品尝。
这是一个最小的例子。我将只处理 eBay 数组中的前 2 项,因为它通过得很好。
const puppeteer = require("puppeteer"); // ^13.5.1
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
const ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";
await page.setExtraHTTPHeaders({"Accept-Language": "en-US,en;q=0.9"});
await page.setUserAgent(ua);
const titles = [
"Chloraethyl | Dr. Henning | Spray 175 ml",
"Elmex Decays Prevention Toothpaste 2 x 75ml",
];
for (const title of titles) {
await page.goto("https://www.amazon.co.uk/");
await page.type("#twotabsearchtextbox", title);
await Promise.all([
page.keyboard.press("Enter"),
page.waitForNavigation(),
]);
const titleSel = "a span.a-size-base-plus.a-color-base.a-text-normal";
await page.waitForSelector(titleSel);
const results = await page.$$eval(titleSel, els =>
els.map(el => el.textContent)
);
console.log(title, results.slice(0, 5));
}
})()
.catch(err => console.error(err))
.finally(() => browser?.close())
;
输出:
Chloraethyl | Dr. Henning | Spray 175 ml [
'Chloraethyl | Dr. Henning | Spray 175 ml',
'Wild Fire (Shetland)',
'A Dark Sin: A chilling British detective crime thriller (The Hidden Norfolk Murder Mystery Series Book 8)',
'A POLICE DOCTOR INVESTIGATES: the Sussex murder mysteries (books 1-3)',
'Rites of Spring: Sunday Times Crime Book of the Month (Seasons Quartet)'
]
Elmex Decays Prevention Toothpaste 2 x 75ml [
'Janina Ultra White Whitening Toothpaste (75ml) – Diamond Formula. Extra Strength. Clinically Proven. Low Abrasion. For Everyday Use. Excellent for Stain Removal',
'Elmex Decays Prevention Toothpaste 2 x 75ml',
'Elmex Decays Prevention Toothpaste 2 x 75ml by Elmex',
'Elmex Junior Toothpaste 2 x 75ml',
'Elmex Sensitive Professional 2 x 75ml'
]
请注意,我添加了用户代理和 headers 以便能够使用 headless: true
,但这是上面主要解决方案的附带结果。您可以 return 到 headless: false
或查看 How to avoid being detected as bot on Puppeteer and Phantomjs? and Why does headless need to be false for Puppeteer to work? 等规范线程,如果您有进一步的检测问题。
我正在尝试从 ebay 获取产品并在亚马逊上打开它们。
到目前为止,我已经在亚马逊上搜索了它们,但我正在努力从搜索结果中选择产品。
目前它正在输出一个空白数组,我不确定为什么。在没有 grabTitles 和 for 循环的单独脚本中进行了测试。所以我猜这里面有什么东西导致了问题。
这里有什么我遗漏的东西阻止了 prodResults 的数据返回吗?
const puppeteer = require('puppeteer');
const URL = "https://www.amazon.co.uk/";
const selectors = {
searchBox: '#twotabsearchtextbox',
productLinks: 'span.a-size-base-plus.a-color-base.a-text-normal',
productTitle: '#productTitle'
};
(async() => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.goto('https://www.ebay.co.uk/sch/jmp_supplies/m.html?_trkparms=folent%3Ajmp_supplies%7Cfolenttp%3A1&rt=nc&_trksid=p2046732.m1684');
//Get product titles from ebay
const grabTitles = await page.evaluate(() => {
const itemTitles = document.querySelectorAll('#e1-11 > #ResultSetItems > #ListViewInner > li > .lvtitle > .vip');
var items = []
itemTitles.forEach((tag) => {
items.push(tag.innerText)
})
return items
})
//Search for the products on amazon in a new tab for each product
for (i = 0; i < grabTitles.length; i++) {
const page = await browser.newPage();
await page.goto(URL)
await page.type(selectors.searchBox, grabTitles[i++])
await page.keyboard.press('Enter');
//get product titles from amazon search results
const prodResults = await page.evaluate(() => {
const prodTitles = document.querySelectorAll('span.a-size-medium.a-color-base.a-text-normal');
let results = []
prodTitles.forEach((tag) => {
results.push(tag.innerText)
})
return results
})
console.log(prodResults)
}
})()
您遇到了 Puppeteer 的一个古老问题,知道页面何时完全完成呈现或加载。
您可以尝试添加以下内容:
await page.waitForNavigation({ waitUntil: 'networkidle2' })
await page.waitForTimeout(10000)
通常我发现 networkidle2
并不总是足够可靠,所以我添加了一个任意的额外 waitForTimeout
。您需要调整超时值(10000 = 10 秒)才能得到您想要的东西,我知道这不是很理想,但我还没有找到更好的方法。
脚本存在一些潜在问题:
await page.keyboard.press('Enter');
触发导航,但您的代码从不等待导航完成后再尝试 select 结果元素。使用waitForNavigation
、waitForSelector
或waitForFunction
(不是waitForTimeout
)。如果您确实要等待导航,则需要使用
Promise.all
的特殊模式来避免竞争条件,如 docs 中所示。此外,您可以通过自己构建字符串直接转到搜索 URL 来跳过页面加载。这应该会提供显着的加速。
您的代码为每个需要处理的项目生成一个新页面,但这些页面永远不会关闭。我看到
grabTitles.length
为 60。因此您将打开 60 个选项卡。这浪费了很多资源。在我的机器上,它可能会挂起所有东西。我建议制作一个页面并反复浏览它,或者在完成后关闭每一页。如果您想要并行性,请考虑同时执行几页任务 queue 或 运行。grabTitles[i++]
-- 为什么在这里递增i
?它已经通过循环递增,因此这似乎跳过了元素,除非你的 select 或你有重复的元素或者你有其他原因要这样做。span.a-size-medium
对我不起作用,可能是 locality-specific。我看到a span.a-size-base-plus.a-color-base.a-text-normal
,但您可能需要调整一下才能品尝。
这是一个最小的例子。我将只处理 eBay 数组中的前 2 项,因为它通过得很好。
const puppeteer = require("puppeteer"); // ^13.5.1
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
const ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";
await page.setExtraHTTPHeaders({"Accept-Language": "en-US,en;q=0.9"});
await page.setUserAgent(ua);
const titles = [
"Chloraethyl | Dr. Henning | Spray 175 ml",
"Elmex Decays Prevention Toothpaste 2 x 75ml",
];
for (const title of titles) {
await page.goto("https://www.amazon.co.uk/");
await page.type("#twotabsearchtextbox", title);
await Promise.all([
page.keyboard.press("Enter"),
page.waitForNavigation(),
]);
const titleSel = "a span.a-size-base-plus.a-color-base.a-text-normal";
await page.waitForSelector(titleSel);
const results = await page.$$eval(titleSel, els =>
els.map(el => el.textContent)
);
console.log(title, results.slice(0, 5));
}
})()
.catch(err => console.error(err))
.finally(() => browser?.close())
;
输出:
Chloraethyl | Dr. Henning | Spray 175 ml [
'Chloraethyl | Dr. Henning | Spray 175 ml',
'Wild Fire (Shetland)',
'A Dark Sin: A chilling British detective crime thriller (The Hidden Norfolk Murder Mystery Series Book 8)',
'A POLICE DOCTOR INVESTIGATES: the Sussex murder mysteries (books 1-3)',
'Rites of Spring: Sunday Times Crime Book of the Month (Seasons Quartet)'
]
Elmex Decays Prevention Toothpaste 2 x 75ml [
'Janina Ultra White Whitening Toothpaste (75ml) – Diamond Formula. Extra Strength. Clinically Proven. Low Abrasion. For Everyday Use. Excellent for Stain Removal',
'Elmex Decays Prevention Toothpaste 2 x 75ml',
'Elmex Decays Prevention Toothpaste 2 x 75ml by Elmex',
'Elmex Junior Toothpaste 2 x 75ml',
'Elmex Sensitive Professional 2 x 75ml'
]
请注意,我添加了用户代理和 headers 以便能够使用 headless: true
,但这是上面主要解决方案的附带结果。您可以 return 到 headless: false
或查看 How to avoid being detected as bot on Puppeteer and Phantomjs? and Why does headless need to be false for Puppeteer to work? 等规范线程,如果您有进一步的检测问题。