Puppeteer爬取脚本无法将抓取数据保存到本地文件的问题排查
Puppeteer脚本问题排查
你的脚本存在多处API误用和逻辑错误,具体如下:
- 并发配置传参位置错误:
concurrency: 3是bluebird.map的配置项,你错把它当成参数传给了page.evaluate,既没有实现预期的并发控制,还向页面上下文注入了无用参数。 - DOM取值逻辑完全错误:
document.querySelectorAll返回的是DOM节点的类数组集合,直接访问集合的href属性永远拿不到值;且目标元素是img标签,资源地址存放在src属性下,不存在href属性。- 没有做元素存在性校验,只要目标选择器匹配不到元素,直接访问属性就会抛出空引用错误,中断整个爬取流程。
- 回调无返回值:
bluebird.map的处理回调没有return爬取到的数据,最终results数组会全是undefined。 - 资源泄漏问题:脚本开头提前创建了1个从未使用的
page实例,循环内新建的page实例爬取完成后也没有关闭,会持续占用内存。 - 冗余等待拖慢效率:已经通过
waitForSelector监听目标元素加载,额外固定等待60秒属于无意义阻塞,会把爬取速度拉低数十倍。 - 执行顺序不合理:
setViewport调用放在page.goto之后,不符合正常浏览器的访问逻辑,可能导致页面渲染尺寸异常。 - 存储格式不匹配:爬取结果是结构化JSON数据,保存为
.html后缀的文件不利于后续读取解析。
修正后可运行代码
const puppeteer = require('puppeteer'); const fs = require('fs'); const bluebird = require("bluebird"); (async() => { const browser = await puppeteer.launch({ headless: 'new' }); const urls = ['/news/england/', '/news/wales/']; const results = await bluebird.map(urls, async(url) => { const page = await browser.newPage(); try { await page.setDefaultNavigationTimeout(0); // 跳转页面前先设置视口 await page.setViewport({ width: 1920, height: 1080, deviceScaleFactor: 1, }); await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0'); await page.goto('https://www.bbc.com' + url, { waitUntil: 'domcontentloaded' }); await page.waitForSelector('#topos-component'); const pageData = await page.evaluate(() => { const titleEl = document.querySelector("a.gel-paragon-bold"); const coverEl = document.querySelector(".gs-o-responsive-image--lead > img:nth-child(1)"); return { title: titleEl ? titleEl.innerText.trim() : null, cover: coverEl ? coverEl.src : null }; }); return { targetUrl: 'https://www.bbc.com' + url, ...pageData }; } catch (err) { console.error(`爬取路径${url}失败:`, err.message); return { targetUrl: 'https://www.bbc.com' + url, error: err.message }; } finally { // 用完页面实例及时关闭,释放内存 await page.close(); } }, { concurrency: 3 // 并发配置正确传给bluebird.map }); // 保存为标准JSON格式文件 fs.writeFileSync('crawl_result.json', JSON.stringify(results, null, 2)); await browser.close(); })();
修改说明
- 移除了所有未使用的依赖引入和冗余代码
- 增加了错误捕获逻辑,单个页面爬取失败不会中断整体任务
- 调整了API调用顺序,删除无意义的硬等待
- 增加了DOM元素空值判断,避免空引用报错
- 修正了DOM属性取值逻辑,爬取结果带上对应页面地址方便溯源
- 每个页面实例爬取完成后自动关闭,避免内存泄漏
内容的提问来源于stack exchange,提问作者wifiuk
相关产品推荐
相关产品推荐

