如何实现多网页爬取与存储?基于Node.js+Vue.js的技术咨询
多网页爬虫实现方案
核心结论
不用给每个网页写全量重复代码,但必须针对每个目标站点定义专属的爬取规则——毕竟不同网站的DOM结构、CSS选择器肯定不一样。但你可以把启动浏览器、滚动加载、页面解析这类通用逻辑抽成工具函数,只给每个站点单独配置「URL、滚动规则、数据提取选择器」即可。
具体实现步骤
1. 抽象通用爬虫工具函数
把你现有代码里的重复逻辑(比如Puppeteer启动/关闭、滚动加载、页面内容获取)抽成可复用的函数,接受站点配置作为参数:
// 通用爬虫工具函数 async function crawlSite(config) { const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); // 跳转到目标站点 await page.goto(config.url, { waitUntil: 'networkidle2' }); // 执行滚动加载(仅当站点需要时) if (config.needScroll) { let previousHeight; while (true) { const currentHeight = await page.evaluate('document.body.scrollHeight'); if (currentHeight === previousHeight) break; previousHeight = currentHeight; await page.evaluate('window.scrollTo(0, document.body.scrollHeight)'); // 加延迟避免滚动过快导致内容未加载 await page.waitForTimeout(1000); } } // 解析页面内容 const content = await page.content(); const $ = cheerio.load(content); // 调用站点专属的数据提取逻辑 const data = config.extractData($); await browser.close(); return data; }
2. 为每个站点定义爬取规则
每个站点单独写一个配置对象,只包含该站点独有的规则,不用重复写爬虫核心逻辑:
// 你的原有新闻站点配置 const newsSiteAConfig = { url: process.env.VUE_APP_PROD_NEWS_DOMAIN, needScroll: true, extractData: ($) => { const newsList = []; $(".list-bigger-wrap").each((index, item) => { const imgsrc = $(item).find("img").attr('src'); const title = $(item).find(".list-bigger-txt p:first-child").text().trim(); const period = $(item).find(".list-bigger-txt p:nth-child(2)").text().trim(); const place = $(item).find(".list-bigger-txt p:nth-child(3)").text().trim(); const exclusive = $(item).find(".list-bigger-txt p:nth-child(4)").text().trim(); newsList.push({ imgsrc, title, period, place, exclusive }); }); return newsList; } }; // 示例:第二个新闻站点的配置 const newsSiteBConfig = { url: 'https://example-news-site.com/latest', needScroll: false, extractData: ($) => { const newsList = []; $(".news-card").each((index, item) => { const title = $(item).find(".card-title").text().trim(); const content = $(item).find(".card-summary").text().trim(); const publishTime = $(item).find(".card-time").text().trim(); newsList.push({ title, content, publishTime }); }); return newsList; } };
3. 批量执行爬取并整合数据
在路由里调用通用函数,并行或串行处理多个站点的爬取请求,最后整合结果:
router.get('/multi-news', async function(req, res, next) { try { // 并行爬取多个站点(效率更高,也可根据需求改成串行) const [siteANews, siteBNews] = await Promise.all([ crawlSite(newsSiteAConfig), crawlSite(newsSiteBConfig) ]); // 整合所有数据(可按站点分类,或合并成统一格式) const allNews = [...siteANews, ...siteBNews]; // 执行数据入库逻辑(复用你之前的代码) // await saveNewsToDB(allNews); res.status(200).json({ total: allNews.length, data: allNews }); } catch (error) { console.error('爬取失败:', error); res.status(500).json({ message: '爬取任务执行失败' }); } });
实用优化建议
- 规则可配置化:把站点规则存在JSON文件或数据库里,新增站点时直接加配置,不用修改核心代码。
- 错误隔离:给每个站点的爬取任务单独加try-catch,避免某个站点爬取失败导致整个批量任务中断。
- 反爬适配:添加随机User-Agent、请求延迟,必要时使用代理IP,降低被目标站点封禁的风险。
- 数据去重:爬取后根据标题、链接等唯一标识去重,避免重复数据入库。
内容的提问来源于stack exchange,提问作者dongdong
相关产品推荐
相关产品推荐

