
本文深入探讨了在使用puppeteer进行网页抓取时,图片元素选择器失效以及如何正确获取图片`src`属性的常见问题。通过分析具体案例,文章提供了优化css选择器的方法(如使用类选择器或属性前缀匹配),并强调了使用`el.getattribute('src')`而非`el.src`来确保准确获取图片源地址的关键技巧。同时,还介绍了 puppeteer 的最佳实践,如页面等待和资源管理,以提升抓取脚本的稳定性和效率。
在使用Puppeteer进行网页自动化和数据抓取时,开发者常会遇到选择器无法准确命中目标元素,或即使命中也无法正确获取其属性值的问题。这在处理图片src属性时尤为常见。一个看似直观的CSS选择器,如'#mm-preview-outer > div.mm-preview > img'或'img[alt="meme generator image preview"]',可能在实际运行中失效,导致无法获取到图片链接。这通常是由于以下几个原因:
为了提高选择器的健壮性和准确性,我们应该优先使用更稳定、更具识别性的CSS类名或属性。
不推荐的选择器示例:
/* 过于依赖层级,易受页面结构变化影响 */ '#mm-preview-outer > div.mm-preview > img' /* 依赖alt属性,可能不稳定或不存在 */ 'img[alt="meme generator image preview"]'
推荐的优化选择器:
针对目标图片元素,如果它有一个独特的类名,应优先使用该类名。例如,如果图片具有mm-img类:
/* 使用精确的类选择器 */ 'img.mm-img'
如果类名可能包含变体(如mm-img-1, mm-img-2),可以使用属性前缀匹配:
/* 使用属性前缀匹配,^= 表示以...开头 */ 'img[class^=mm-img]'
这种方法更加灵活,能适应类名的小范围变动,同时比复杂的层级选择器更稳定。
在Puppeteer中,当选择器成功命中图片元素后,获取其src属性也需要注意方法。直接使用el.src在某些情况下可能无法返回预期的结果,尤其是在处理动态加载或懒加载的图片时。更可靠的方法是使用el.getAttribute('src')。
示例:从el.src到el.getAttribute('src')的修改
// 原始代码 (可能无法正常工作)
// const imageurl = await page.$eval('img[alt="Imgflip Logo"]', el => el.src);
// 优化后的代码 (推荐)
const imageurl = await page2.$eval('img[class^=mm-img]', el => el.getAttribute('src'));以下是一个结合了优化选择器、正确属性获取以及Puppeteer最佳实践的完整代码示例。它演示了如何遍历一个列表页,进入每个详情页,并抓取目标图片(如模版图片)的src。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true, // 生产环境推荐无头模式
defaultViewport: null, // 允许页面根据内容调整视口
});
const page = await browser.newPage();
// 导航到列表页,等待网络空闲,设置超时时间
await page.goto('https://imgflip.com/memetemplates', { waitUntil: "networkidle2", timeout: 30000 });
// 等待关键选择器出现,确保页面加载稳定
await page.waitForSelector('.mt-box');
// 获取所有模版盒子
const boxes = await page.$$('.mt-box');
for (let box of boxes) {
let page2 = null; // 在循环内部声明,确保每次迭代都是新的页面实例
try {
// 从当前box元素中获取标题和链接
const title = await box.$eval('h3 > a', el => el.textContent);
const link = await box.$eval('a.mt-caption', el => el.getAttribute('href'));
page2 = await browser.newPage(); // 为每个详情页创建新页面
// 导航到详情页,等待网络空闲,设置超时时间
await page2.goto(`https://imgflip.com${link}`, { waitUntil: "networkidle2", timeout: 30000 });
// 等待详情页的关键元素加载
await page2.waitForSelector('body');
// 使用优化后的选择器和getAttribute获取图片src
const imageurl = await page2.$eval('img[class^=mm-img]', el => el.getAttribute('src'));
console.log(`标题: ${title}`);
console.log(`图片URL: ${imageurl}`);
} catch (error) {
console.error(`处理过程中发生错误: ${error.message}`);
} finally {
// 确保每个创建的详情页都被关闭,释放资源
if (page2) {
await page2.close();
}
}
}
await browser.close(); // 关闭浏览器实例
})();代码解释与注意事项:
在某些情况下,图片可能被嵌套在不同的标签中,或者使用data-src等自定义属性进行懒加载。以下示例展示了如何处理这类复杂情况,并抓取与主模版相关的其他模版图片。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true,
defaultViewport: null,
});
const page = await browser.newPage();
await page.goto('https://imgflip.com/memetemplates', { waitUntil: "networkidle2", timeout: 30000 });
await page.waitForSelector('.mt-box');
const boxes = await page.$$('.mt-box');
let allMemesData = [];
for (let box of boxes) {
let page2 = null;
try {
const data = await box.$eval('.mt-title > a', el => {
return { link: el.getAttribute('href'), text: el.textContent };
});
page2 = await browser.newPage();
await page2.goto(`https://imgflip.com${data.link}`, { waitUntil: "networkidle2", timeout: 30000 });
await page2.waitForSelector('body');
// 查找详情页中可能存在的相关模版,使用:has()伪类检查是否存在h2标题
// :has(h2) 确保选择的.base-unit是包含h2标题的有效内容块,而不是空或广告
const relatedMemes = await page2.$$(".base-unit:has(h2)");
let relativeMemesList = [];
for (let m of relatedMemes) {
const titleInfo = await m.$eval('h2 > a', el => {
return { link: el.getAttribute("href"), text: el.textContent };
});
let imageUrl = '';
// 检查图片是在div.base-img中(可能用data-src)还是直接在a标签中(用src)
const divBaseImg = await m.$('div.base-img');
if (divBaseImg) {
// 如果存在div.base-img,尝试获取其data-src属性
imageUrl = await m.$eval('div.base-img', el => el.getAttribute("data-src") || el.getAttribute("src"));
} else {
// 否则,尝试从img标签直接获取src
imageUrl = await m.$eval('img', el => el.getAttribute("src"));
}
if (imageUrl) { // 确保图片URL不为空
relativeMemesList.push({
link: titleInfo.link,
text: titleInfo.text,
image: imageUrl
});
}
}
await page2.close();
allMemesData.push({
link: data.link,
text: data.text,
relative: relativeMemesList
});
} catch (error) {
console.error(`处理过程中发生错误: ${error.message}`);
} finally {
if (page2) {
await page2.close();
}
}
}
await browser.close();
console.dir(allMemesData, { depth: null }); // 打印所有抓取到的数据
})();高级技巧说明:
通过本文的讲解,我们了解到在Puppeteer中进行网页抓取时,解决图片元素选择器失效和正确获取src属性的关键在于:
遵循这些最佳实践,将有助于您编写出更稳定、高效和健壮的Puppeteer抓取脚本。
以上就是Puppeteer网页抓取:解决图片元素选择器失效及src属性获取问题的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号