
本文深入探讨了在使用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')。
语鲸
AI智能阅读辅助工具
314
查看详情
示例:从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属性获取问题的详细内容,更多请关注其它相关文章!
相关文章:
动漫岛观看全网网 动漫岛在线正版动漫入口
铁路12306的积分有效期是多久_铁路12306积分有效期说明
c++如何实现一个简单的软件渲染器_c++从零开始的3D图形学
三星ZFold5多任务卡顿_Samsung ZFold5流畅度提升
抖音网页版平台入口 抖音网页版官网在线访问教程
漫蛙2(台版)官方入口地址 漫蛙2(台版)正版漫画网页端
海棠电脑版入口_通过电脑访问海棠官网阅读
2025年云电脑操作系统体验 | 无需本地硬件,随时随地使用高性能PC
Sublime怎么配置Nim语言环境_Sublime Nim代码高亮与补全
b站怎么删除评论_b站评论管理与删除操作
高德地图怎么看全景照片_高德地图全景照片浏览教程
PHP教程:将数据库查询结果动态展示到HTML Textarea的最佳实践
如何将HTML表格多行数据保存到Google Sheets
如何在J*a中使用Locale处理多语言环境
UC浏览器如何安装插件 UC浏览器添加扩展程序详细教程【进阶】
整合Supabase认证与Django模型:跨模式迁移的解决方案
WooCommerce 购物车显示所有交叉销售商品教程
J*a如何实现并发下载文件_J*a多线程IO性能优化案例
Win11如何使用Windows Sandbox Win11沙盒功能开启与使用教程【详解】
纯CSS与HTML网格布局的HTML精简策略:SVG与JS方案解析
知音漫客官网漫画下载_知音漫客网页版阅读记录
Selenium Python中处理点击后新窗口加载冻结问题的策略与实践
在J*a中如何使用BigDecimal进行高精度计算_BigDecimal类应用指南
曝R星经典之作开发图 设计简陋但信息密集!
excel怎么提取文本中数字 excel函数提取技巧
将JSON对象数组转置为键值对列表的实用指南
抓大鹅解压小游戏 抓大鹅摸鱼解压入口
汽车之家官方网站官网入口_汽车之家网页版直接进入
妖精漫画网页版登录入口免费_妖精漫画官网主页直接阅读漫画
Python实现多节点属性重叠度分析教程
Composer的 COMPOSER_PROCESS_TIMEOUT 配置项有什么用_解决因执行时间过长而失败的Composer脚本
抖音极速版最新版本 抖音极速版官方下载地址
Basecamp怎样用留言钉固定重点_Basecamp用留言钉固定重点【重点标记】
MAC如何将整个网页截长图_MAC使用Safari的导出为PDF或第三方工具
漫蛙2正版漫画站 漫蛙2网页版快速访问入口
抖音网页版企业服务中心登录入口_抖音网页版企业登录平台
Go语言中Map值调用指针接收器方法的限制与应对
淘宝网网页版登录入口 淘宝官方网页版快捷登录
age动漫网站入口 age动漫官网直接访问入口
动漫共和国防屏蔽稳定域名-动漫共和国官方正版直达通道
铁路12306改签能改到更早的车次吗_铁路12306改签提前车次规则
Python Sounddevice 音频卡顿问题解析与队列数据安全处理
c++如何使用折叠表达式(Fold Expressions)_c++17可变参数模板新技巧
小红书商家版怎样在笔记嵌入商品卡路径_小红书商家版在笔记嵌入商品卡路径【挂载教程】
解决Tabulator日期时间排序问题的专业指南
Excel文件在线转换快速入口 Excel在线格式转换网站
反效果?《战地6》免费试玩开启后玩家数不升反降
Yandex官方入口网址 Yandex俄罗斯搜索引擎最新在线地址
探索高级语言到C/C++的转译路径:以Go为例及内存管理策略
Golang如何使用bytes.Split分割字节切片_Golang bytes切片分割方法