Puppeteer的基本使用及多目标同时访问

文章目录

一、安装 puppeteer 并更改默认缓存路径

1、更改 Puppeteer 用于安装浏览器的默认缓存目录

在项目文件夹根目录中创建 .puppeteerrc.cjs 文件,文件内容如下:

js 复制代码
const {join} = require('path');

/**
 * @type {import("puppeteer").Configuration}
 */

module.exports = {
  // Changes the cache location for Puppeteer.
  cacheDirectory: join(__dirname, '.cache', 'puppeteer'),
};

注:安装 Puppeteer 时,它会自动下载最新版本的用于测试的 Chrome,该方法适用于不希望将安装的浏览器(Chrome)安装在默认路径下。浏览器默认下载到 $HOME/.cache/puppeteer 文件夹(从 Puppeteer v19.0.0 开始)。根据自身实际可跳过该步骤。

2、安装 puppeteer

yarn add puppeteer

3、项目结构目录

更多安装详情及环境配置参见官网:https://pptr.nodejs.cn/

二、基本使用

1、启动浏览器并访问目标网站
js 复制代码
const puppeteer = require('puppeteer');

// 启动浏览器
const brower = await puppeteer.launch({
    // 无头模式(默认值 true,设置为false后启动程序将弹出浏览器)
    headless: false,
    args: ['--no-sandbox', '--disable-setuid-sandbox', '--enable-accelerated-2d-canvas', '--enable-aggressive-domstorage-flushing'],
    // 忽略 https 报错
    ignoreHTTPSErrors: true,
    timeout: 120000,
});
// 创建标签页
const page = await brower.newPage();
// 设置可视区域的大小
await page.setViewport({ width: 1920, height: 800 })
// 访问目标(此处以访问百度为例)
await page.goto('https://www.baidu.com/', {
    // waitUntil: 'networkidle0',
    timeout: 120000
});

// 1、生成截图
// 2、生成PDF文件
// 3、其他...

// 关闭浏览器
await brower.close();

参数说明:

  • waitUntil:给定事件字符串,直到事件被触发,表示等待成功。
    • load:默认值,等待 load 事件触发。
    • domcontentloaded:等待 domcontentloaded 事件触发。
    • networkidle0:直到目标页面的请求不超过0个,且这一状态持续 500ms
    • networkidle2:直到目标页面的请求不超过2个,且这一状态持续 500ms

参考链接:https://pptr.nodejs.cn/api/puppeteer.puppeteerlifecycleevent

2、生成截图
js 复制代码
...
await page.goto(...);

let imgBuffer = null;
imgBuffer = await page.screenshot({
    // 保存路径
    path: './screenshot/img.png',
    // 全屏截取
    fullPage: true,
    // 截取范围
    // clip: {x: 0, y: 0, width: 1920, height: 800}
});

await brower.close();

// 后续可封装成函数
// return imgBuffer;

imgBuffer:截图获取成功后,生成 buffer 数据,便于后续将该图片传递至前端展示。

关于 screenshot 方法的更多参数见:https://pptr.nodejs.cn/api/puppeteer.screenshotoptions/

3、生成 PDF 文件
js 复制代码
...
await page.goto(...);

await page.pdf({
    path: `./public/pdf/article.pdf`,
    // 隐藏页眉和页脚
    displayHeaderFooter: true,
    // 页面范围(全部)
    pageRanges: '',
    // 格式
    format: 'A3',
    // scale: 1.2,
    // 生成带标签的(可访问的)PDF。
    // tagged: false,
    // 背景
    printBackground: true,
    margin: { top: '15px' }
    // outline: true,
})

await brower.close();

更多详情参见:https://pptr.nodejs.cn/api/puppeteer.pdfoptions

4、获取目标网站 html 结构并解析

此处解析使用 cheerio,安装 cheerio

yarn add cheerio

获取 html 结构:

js 复制代码
...
await page.goto(...);

// 获取页面 html 结构
const html = await page.content();
// 有点类似于 JQuery 
const $ = cheerio.load(html);
// 获取目标元素中的属性
const link = $('#xxx .xxx').find('a').attr('href');
// 获取文本内容
const title = $('#xxx .xxx').find('a span').text();

await brower.close();
5、拦截请求
js 复制代码
// 创建标签页
let page = await brower.newPage();
// 启用请求拦截
await page.setRequestInterception(true);

// 设置请求处理函数
page.on('request', (request) => {
    // 只允许加载HTML、CSS、JS文件,其他资源(如图片)则阻止加载,提高加载速度
    if (request.resourceType() === 'image' || request.url().endsWith('.mp4')) {
        // 终止请求
        request.abort();
    } else {
        request.continue();
    }
});

await page.goto(...);
6、执行 JavaScript
js 复制代码
...
await page.goto(...);

await page.evaluate(async () => {
    // 获取目标元素
    const element = document.querySelector('#xxx');
    // 隐藏目标元素
    element ? element.style.display = 'none' : '';
})
7、同时访问多个目标
js 复制代码
// 目标网站信息,可以把过滤规则写在里面
const targetUrl = {
    "url1": { 
        "url": "http://xxx.com",
        "filter": async (html) => {
            const $ = cheerio.load(html);
            // 获取文本内容
            const title = $('#xxx .xxx').find('a span').text();
            return title;
        }
    },
    "url2": { "url": "xxx",... },
}

// 启动浏览器 ... 
const brower = await puppeteer.launch({...})

// 针对不同网站获取信息
const promises = Object.keys(targetUrl).map(async (target) => {
    let page = target;
    // 创建标签页
    page = await brower.newPage();
    // 访问目标网址
    await page.goto(targetUrl[target]['url'], {
        waitUntil: 'domcontentloaded'
    });
    // 获取页面 html 结构
    const html = await page.content();
    // 针对不同的网站过滤提取不同的信息
    const titleList = await targetUrl[target]['filter'](html);
    // 关闭标签页
    await page.close();
    // 以对象的键命名
    return { [target]: titleList }
})

// 处理返回的信息,插入数据库
const data = {};
await Promise.all(promises).then((result) => {
    // 获取集合,此处使用的mongodb数据库,集合名称为 eng_news
    const engNewsTable = mongoDB.collection('eng_news');
    result.forEach((el, index) => {
        // 获取键名
        const target = Object.keys(targetUrl)[index];
        // 获取键名对应的值
        data[target] = el[target]
    })
    // 插入数据库
    engNewsTable.insertOne(data);
}).catch((error) => {
    console.log(error);
});

// 关闭浏览器
await brower.close();
8、补充说明图片获取后传递至前端展示

后端使用 express 框架:

js 复制代码
router.post('/getTargetImg', async function (req, res, next) {
    const pdfResult = await getTargetPDF(req.body.url);

    if (pdfResult) {
        const imgBuffer = pdfResult.imgBuffer;
        if (imgBuffer) {
            // 设置 Content-Type 为 image/png
            res.setHeader('Content-Type', 'image/png');
            res.send(imgBuffer);
        } else {
            res.send(null);
        }
    } else {
        res.send(null);
    }
})

前端使用 vue3 接收:

js 复制代码
await getTargetImgAPI({ url: url }).then(
    (response) => {
        if (response.data) {
            // 图片获取成功 ...
            const blob = new Blob([response.data], { type: "image/png" });
            const url = URL.createObjectURL(blob);
            
        } else {
            // 图片获取失败
            ...
        }
    },
    (error) => {
        console.log(error);
    }
);

前端获取到 buffer 数据并处理完成后,生成的 url 可直接放在 img 标签的 :src 中使用。例:<img :src="xx">

三、效果展示

四、参考资料

相关推荐
大今野2 小时前
node.js和js
开发语言·javascript·node.js
LLLuckyGirl~2 小时前
node.js之---单线程异步非阻塞 I/O
node.js
前端小小王5 小时前
pnpm、Yarn 和 npm 的区别?
前端·npm·node.js
牛奔6 小时前
windows nvm 切换node版本后,npm找不到
前端·windows·npm·node.js
鸭梨山大。6 小时前
NPM组件包 vant部分版本内嵌挖矿代码
前端·安全·npm·node.js·vue
秋雨凉人心11 小时前
简单发布一个npm包
前端·javascript·webpack·npm·node.js
哥谭居民000113 小时前
将一个组件的propName属性与父组件中的variable变量进行双向绑定的vue3(组件传值)
javascript·vue.js·typescript·npm·node.js·css3
土豆炒马铃薯。14 小时前
【Vue】前端使用node.js对数据库直接进行CRUD操作
前端·javascript·vue.js·node.js·html5
CC__xy14 小时前
Node二、Node.js 模块化、es6 软件包、Express - 框架
前端·node.js
bin915316 小时前
npm报错
前端·npm·node.js