Puppeteer 入门
阅读原文时间:2021年04月26日阅读:1

Puppeteer 

1.介绍
Puppeteer  翻译是操纵木偶的人,利用这个工具,我们能做一个操纵页面的人。通俗点儿说,你可以通过代码的方式模拟人在 Chrome 中的各种操作,打开网址、开启多个 Tab、填写输入框,模拟鼠标轨迹、滚动滚动条,甚至截屏某个元素都可以。Puppeteer是一个Nodejs的库,支持调用Chrome的API来操纵Web,相比较Selenium或是PhantomJs,它最大的特点就是它的操作Dom可以完全在内存中进行模拟既在V8引擎中处理而不打开浏览器,而且关键是这个是Chrome团队在维护,会拥有更好的兼容性和前景。
官方:https://github.com/GoogleChrome/puppeteer
API文档: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md

2.用处
1.利用网页生成PDF、图片
2.高级爬虫,可以爬取大量异步渲染内容的网页
3.自动化表单提交、UI测试、键盘输入等
4.帮你创建一个最新的自动化测试环境(chrome),可以直接在此运行测试用例
5.捕获站点的时间线,以便追踪你的网站,帮助分析网站性能问题

3.安装nodejs
英文网址:https://nodejs.org/en/download/
中文网址:http://nodejs.cn/download/
通过  uname -a  命令查看到我的Linux系统位数是64位(备注:x86_64表示64位系统, i686 i386表示32位系统)
假定下载的文件放在/home/software/中
cd /home/software/
解压:tar -xvf   node-v6.10.0-linux-x64.tar.xz   
重命名 mv node-v6.10.0-linux-x64  nodejs 
建立软连接,变为全局
ln -s /home/software/nodejs/bin/npm /usr/local/bin/ 
ln -s /home/software/nodejs/bin/node /usr/local/bin/

npm在中国的下载速度并不是很理想,我们可以安装淘宝提供的cnpm来代替其功能。
官方网址 : https://npm.taobao.org/
全局安装cnpm。之后可以直接使用cnpm代替npm,命令格式是一样的。
npm install -g cnpm --registry=https://registry.npm.taobao.org

如果全局安装之后无法直接使用全局安装命令,一般是npm的路径没有在系统变量中,可以通过以下方法解决
用 bash echo -e "export PATH=$(npm prefix -g)/bin:$PATH" >> ~/.bashrc && source ~/.bashrc
bash echo -e "export NODE_PATH=/home/software/nodejs/node_modules" >> ~/.bashrc && source ~/.bashrc

使用node -v 和 npm -v 如有版本号输出,代表安装成功

4.安装Puppeteer

cnpm i puppeteer --save

5.例子

1).截图

puppeteer.launch 启动浏览器实例

browser.newPage() 创建一个新页面

page.goto 进入指定网页

page.screenshot 截图

page.setViewport 设置视图大小

page.setUserAget 设置UserAgent

const puppeteer = require('puppeteer');
async function run () {
    const browser = await puppeteer.launch({
    //设置超时时间
    timeout: 15000,
    //如果是访问https页面 此属性会忽略https错误
    ignoreHTTPSErrors: true,
    // 打开开发者工具, 当此值为true时, headless总为false
    devtools: false,
    // false:关闭headless模式, 会打开浏览器,true:不打开浏览器
    headless: false,
    });
    const page = await browser.newPage();
    //设置视窗大小为 1920x1080
    page.setViewport({width:1920, height:1080});
    //设置UserAgent
    page.setUserAgent('Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/48.0.2564.82 Chrome/48.0.2564.82 Safari/537.36');
    //waitUntil:等待页面完全加载完,包含异步渲染
    await page.goto('https://tianfang.cnblogs.com', {waitUntil: 'networkidle2'});
    await page.screenshot({ path: 'screen.png',fullPage: true,});
    await browser.close();
};
run();

2).采集数据

const puppeteer = require('puppeteer');
async function run () {
    const browser = await puppeteer.launch({
        headless: true,
        args: ['--no-sandbox']
    });
    const page = await browser.newPage();
    page.setUserAgent('Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/48.0.2564.82 Chrome/48.0.2564.82 Safari/537.36');

    //进入页面
    await page.goto('https://www.guazi.com/hz/buy/', {waitUntil: 'networkidle2'});

    //获取页面标题
    let title = await page.title();
    console.log(title);

    //注入jquery
    await page.addScriptTag({
        url: "https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"
    });

    //向页面注入我们的函数,在浏览器中执行函数,相当于在控制台中执行函数,返回一个 Promise
    const result = await page.evaluate(() => {
        //var $ = window.$;  如果页面已加载jquery,可以不用注入jquery,用此方法可以直接获取jquery
        var arr = [];
        let brand = $(".js-brand").find("ul li p a");
        brand.each(function(){
            let brandText = $(this).html();
            arr.push({brandText});
        });

        return arr ;
    });

    console.log(result);

    const car_list_select = ".carlist"

    const carList =  await page.evaluate((sel) => {
        var arr1 = [];
        let carA = $(sel).find("li a");
        carA.each(function(){
            let title = $(this).find("h2").html();
            let price = $(this).find(".t-price p").text();
            arr1.push({title:title,price,price});
        });

        return arr1 ;
    },car_list_select);

   console.log(carList);

   //退出
   process.exit(1);
};
run();

3).模拟输入

page本身提供原始的mouse和keyboard的模拟输入类。

page.mouse

page.keyboard

page.click(selector[, options]) 在被选择元素上模拟点击

page.type(selector, text[, options]) 在被选择的输入框中输入

page.hover(selector) 模拟鼠标移动到被选择元素上

page.select(selector, …values) 在被选择元素上模拟选择select选项

page.tap(selector) 在被选择元素上模拟触摸

const puppeteer = require('puppeteer');
let timeout = function (delay) {
     return new Promise((resolve, reject) => {
           setTimeout(() => {
                  try {
                      resolve(1)
                  } catch (e) {
                      reject(0)
                   }
           }, delay);
     })
 }
async function run () {
    const browser = await puppeteer.launch({
        headless: true,
        args: ['--no-sandbox']
    });
    const page = await browser.newPage();
    page.setUserAgent('User-Agent:Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/48.0.2564.82 Chrome/48.0.2564.82 Safari/537.36');
    await page.goto('https://passport.jd.com/new/login.aspx', {waitUntil: 'networkidle2'});

    const loginTab = await page.$('.login-tab-r');
    await loginTab.click();
    const name = await page.$eval('#loginname',el=>el.placeholder);
    console.log(name);
    //const loginFor = page.$('.login-form');
    //await page.screenshot({path:"jd.png", fullPage:true});
    const image = await page.waitForSelector('.login-form');
    await image.screenshot({path:"jd_form.png"});

    //await page.click("#loginname");
    //await page.keyboard.type("wuxing164");
    //await page.click("#nloginpwd");
    //await page.keyboard.type("wuxing165");

    await page.type('#loginname', "wuxing164");
    await page.type('#nloginpwd', "wuxing165");

    await page.click("#loginsubmit");

    await timeout(3000);

    await page.screenshot({
        path: 'jd-index.png'
    });
    process.exit(1);
};
run();

---------------------------------------------------------------------------------------------------------------------------------------------------------

// 页面加载更多按钮出现(查找元素)
await page.waitForSelector('.more');

// 拿到页面上的jQuery
var $ = window.$;

面模拟设置相关函数有如下几个,
page.setViewport: 设置视图大小
page.setUserAget: 设置UserAgent
page.SetCookie: 设置Cookie

页面跳转相关函数有如下几个,
page.goto(url, options)
page.goBack(options)
page.goForward(options)
page.reload(options)

常用的元素函数选择有:

page.$(selector)  选一个元素

page.$$(selector) 选多个元素

模拟输入
page本身提供原始的mouse和keyboard的模拟输入类。
page.mouse
page.keyboard
但同时也提供更方便快捷的模拟输入函数

page.click(selector[, options])        在被选择元素上模拟点击
page.type(selector, text[, options])    在被选择的输入框中输入
page.hover(selector)                模拟鼠标移动到被选择元素上
page.select(selector, …values)        在被选择元素上模拟选择select选项
page.tap(selector)                    在被选择元素上模拟触摸

当我们使用page.goto等跳转函数主动跳转页面时,本身该函数就是可以异步等待的,可以直接使用await等待跳转完成。

除此之外,系统也提供了如下等待函数

page.waitForNavigation(options)
page.waitFor(selectorOrFunctionOrTimeout[, options[, …args]])
page.waitForSelector(selector[, options])
page.waitForXPath(xpath[, options])
page.waitForFunction(pageFunction[, options[, …args]])
其中最常用的是page.waitForNavigation,常用于等待跳转结束,例如点击搜索按钮后,等待跳转至搜索结果页面。

const navigationPromise = page.waitForNavigation();
await page.click('a.my-link'); 
await navigationPromise; 

另外,我们如果需要更细粒度的等待,可以使用其它几个wait函数,如如果我们要等待某图片的第一次加载。

执行脚本

执行脚本最常用的函数是page.evaluate,它类似于在控制台中执行指令。
console.log(await page.evaluate('1 + 2'));
var title = await page.evaluate('document.title')

它也可以用来执行写好的node函数,实际上该函数是在浏览器中执行的,但可以像本地函数一样编写,还支持参数传值。

var title = await page.evaluate(async (i) => {
    return document.title + ' ' + i;
}, 'hello');

console.log(title);

另外,还有几个其它的执行脚本的函数,应用于不同的场合,也是非常有用的。
page.evaluateHandle(pageFunction, …args)
page.evaluateOnNewDocument(pageFunction, …args)
page.$$eval(selector, pageFunction[, …args])
page.$eval(selector, pageFunction[, …args])
例如:
const searchValue = await page.$eval('#search', el => el.value);
const preloadHref = await page.$eval('link[rel=preload]', el => el.href);
const html = await page.$eval('.main-container', e => e.outerHTML);

信息查看
puppeteer提供了一些查看页面信息的函数,
page.url()
page.content()
page.frames()
page.mainFrame()
page.metrics()
page.target()
page.title()
page.viewport()

page.$(selector)  选一个元素
page.$$(selector) 选多个元素



page.$(selector)  选一个元素
page.$$(selector) 选多个元素



page.$(selector)  选一个元素  
page.$$(selector) 选多个元素



page.$(selector)  选一个元素  
page.$$(selector) 选多个元素

一些参考文章

https://blog.csdn.net/xiatiancc/article/details/78872157
https://cloud.tencent.com/developer/article/1006000
https://www.jianshu.com/p/4441d0241b6e
https://cnodejs.org/topic/5a238b818eab6ee92a694622
https://blog.csdn.net/u010142437/article/details/79136182
https://www.cnblogs.com/TianFang/p/9059978.html
http://csbun.github.io/blog/2017/09/puppeteer/
https://blog.csdn.net/u010142437/article/details/79136182
https://gitbook.cn/books/5aa4835a987ed32df6a2123c/index.html
https://www.cnblogs.com/imgss/p/headless.html