Puppeteer天气爬虫性能优化咨询:Mars数据抓取速度过慢
Mars天气爬虫性能优化方案
核心性能瓶颈及优化点
- 复用浏览器实例(最关键优化):原代码每次请求都启动/销毁浏览器,这是性能极差的核心原因。浏览器启动是高资源开销操作,应全局复用单个浏览器实例,仅在服务启动时初始化、退出时销毁。
- 启用无头模式:
headless: false会启动带界面的浏览器,性能开销远高于无头模式,改为headless: "new"(新版Puppeteer的无头模式更高效)。 - 优化页面加载策略:
page.goto默认等待所有资源加载完成,可设置waitUntil: "domcontentloaded",仅等待DOM加载完成就开始爬取,跳过图片、广告等非必要资源。 - 重构数据提取逻辑:
- 用
querySelector替代childNodes:后者依赖DOM节点顺序,易因空白文本节点或页面小改动失效,直接通过元素类名定位更稳定。 - 避免重复读取
innerText:温度文本一次性读取后拆分,减少DOM访问次数。 - 替换
Map为普通JS对象:对象的创建和序列化效率更高,无需额外使用Map结构。
- 用
- 可选:拦截无关资源:屏蔽图片、样式、字体等非必要请求,进一步缩短页面加载时间。
优化后的完整代码
const puppeteer = require("puppeteer"); const express = require("express"); const app = express(); // 全局复用浏览器实例,仅初始化一次 let browser; // 服务启动前初始化浏览器 async function initBrowser() { browser = await puppeteer.launch({ headless: "new", // 启用高效无头模式 args: [ "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", // 解决容器环境内存限制问题 "--disable-gpu" ] }); } app.get("/weather", async (req, res) => { try { const page = await browser.newPage(); // 拦截非必要资源,加速页面加载 await page.setRequestInterception(true); page.on("request", (req) => { const blockedTypes = ["image", "stylesheet", "font", "script"]; if (blockedTypes.includes(req.resourceType())) { req.abort(); } else { req.continue(); } }); // 仅等待DOM加载完成,跳过非必要资源 await page.goto("https://mars.nasa.gov/msl/weather/", { waitUntil: "domcontentloaded", timeout: 30000 // 设置超时时间,避免无限等待 }); const nasaWeatherDataScrape = await page.evaluate(() => { return [...document.querySelectorAll(".item")].map(item => { // 通过类名精准定位元素,避免依赖节点顺序 const solElement = item.querySelector(".sol"); const dateElement = item.querySelector(".earth_date"); const tempElement = item.querySelector(".temp"); // 一次性处理温度文本,减少DOM访问 const tempText = tempElement?.innerText || ""; const [highPart, lowPart] = tempText.split("C"); return { Sol: solElement?.innerText.split(" ").pop() || "N/A", Date: dateElement?.innerText || "N/A", High: highPart?.split(" ").pop() || "N/A", Low: lowPart?.split(" ").pop() || "N/A" }; }); }); res.send(nasaWeatherDataScrape); await page.close(); // 仅关闭当前页面,保留浏览器实例复用 } catch (error) { console.error("爬取失败:", error); res.status(500).send({ error: "爬取失败" }); } }); // 启动服务流程 initBrowser().then(() => { app.listen(5000, () => { console.log("服务运行在 http://localhost:5000"); }); }); // 进程退出时关闭浏览器 process.on("exit", async () => { if (browser) { await browser.close(); } });
内容的提问来源于stack exchange,提问作者Seamus O'Sullivan
相关产品推荐
相关产品推荐

