Puppeteer使用示例详解


Posted in Python onJune 20, 2019

PhantomJS曾经是无头浏览器里的王者,测试、爬虫等都在使用,随着GoogleChrome Headless的出现,PhantomJS的作者已经明确表示不在更新,而GoogleChrome Headless将会是未来爬虫的趋势,而测试将依然会使用Webdriver那套方案,GoogleChrome Headless可以利用WebDriver调用,也可以使用其集成的API——Puppeteer(操纵木偶的人),他的功能和他的名字一样强大,可以随意操控Chrome或Chromeium,缺点就是只有node的API,来看看他的图标:

Puppeteer使用示例详解

Puppeteer是基于DevTools协议来控制headless Chrome的Node库,依赖6.4以上版本的node,本人是在接触这个软件时才开始学习node,依然感觉到它的异步async/await超级强大,在Puppeteer中也大量使用异步来完成任务。

Puppeteer的安装可以使用node的包管理工具npm来安装:

npm i puppeteer

这里安装时会自动安装Chromeium,如果不需要则可以通过配置npm跳过下载,做为一名爬虫工程师我不会去讨论测试相关的使用,接下来看看如何使用,和WebDriver类似,首先需要实例化brower,代码如下:

const puppeteer = require('puppeteer');

(async () => { ​
 const browser = await puppeteer.launch(); 
 const page = await browser.newPage(); 
 await page.goto('http://www.baidu.com'); 
 await browser.close(); 
})();

这段代码执行结束时,你可能什么也没有感觉到,因为它在后台启动了一个Chromeium进程,打开了百度首页,接着就关闭了,当然我们可以在前台打开Chromeium,这里就需要配置一下,所配置参数只需传入launch()即可,常用参数如下:

headless:  是否打开浏览器,默认为true

ignoreHTTPSErrors: 是否忽略https错误,默认为true

executablePath:  配置要调用浏览器的可执行路径,默认是同Puppeteer一起安装的Chromeium

slowMo:指定的毫秒延缓Puppeteer的操作

args:设置浏览器的相关参数,比如是否启动沙箱模式“--no-sandbox”,是否更换代理“--proxy-server”,具体参数请点此查看

使用示例如下:

const browser = await puppeteer.launch({headless:false, args: ["--no-sandbox",]}) //打开浏览器

打开新的窗口:

const page = await browser.newPage();

设置窗口大小

await page.setViewport({
 width: 1920,
 height: 1080
})

过滤不需要的请求:

await page.setRequestInterception(true);
page.on('request', interceptedRequest => {
 if (interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg'))
  interceptedRequest.abort();
 else
  interceptedRequest.continue();
});

为浏览器设置userAgent:

await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299")

设置cookie,

const data = {
 name: "smidB2", 
 domain: ".csdn.net", 
 value: "201806051502283cf43902aa8991a248f9c605204f92530032f23ef22c16270"
}
await page.setCookie(data)

示例中只是演示,真实的cookie是一个列表形式的,需要循环添加才可以

for(let data of cookies){
 await page.setCookie(data)
}

请求url:

const url = "http://www.baidu.com"
await page.goto(url, { waitUntil: "networkidle2" });

设置页面等待时间:

await page.waitFor(1000); // 单位是毫秒

等待页面某个元素加载完成

await page.waitForSelector("input[class='usrname']")

点击某个元素

await page.click("input[class='submit']")

利用page.evaluate()函数拖动鼠标至页面底部,原理就是在页面注入js代码。

let scrollEnable = false;
let scrollStep = 500; //每次滚动的步长
while (scrollEnable) {
	scrollEnable = await page.evaluate((scrollStep) => {
		let scrollTop = document.scrollingElement.scrollTop;
		document.scrollingElement.scrollTop = scrollTop + scrollStep;
		return document.body.clientHeight > scrollTop + 1080 ? true : false
	}, scrollStep);
	await page.waitFor(600)
}

获取html信息

const frame = await page.mainFrame()
const bodyHandle = await frame.$('html');
const html = await frame.evaluate(body => body.innerHTML, bodyHandle);
await bodyHandle.dispose(); //销毁 
console.log(html)

这是爬虫能用到的大致操作,以下是爬取豆瓣热门电影的基本信息和评分的代码,写这个程序时对node也是一知半解,如有不对,欢迎留言

basePupp.js

const puppeteer = require("puppeteer")

class BasePuppeteer{
 puppConfig(){
  const config = {
   headless: false
  }
  return config
 }
 async openBrower(setting){
  const browser = puppeteer.launch(setting)
  return browser
 }
 async openPage(browser){
  const page = await browser.newPage()
  return page
 }
 async closeBrower(browser){
  await browser.close()
 }
 async closePage(page){
  await page.close()
 }
}

const pupp = new BasePuppeteer()
module.exports = pupp

douban.js

const pupp = require("./basePupp.js")
const cheerio = require("cheerio")
const mongo = require("mongodb")
const assert = require("assert")

const MongoClient = mongo.MongoClient
const Urls = "mongodb://10.4.251.129:27017/douban"

MongoClient.connect(Urls, function (err, db) {
 if (err) throw err;
  console.log('数据库已创建');
 var dbase = db.db("runoob");
 dbase.createCollection('detail', function (err, res) {
  if (err) throw err;
  console.log("创建集合!");
  db.close();
 });
});

async function getList(){
 const brower = await pupp.openBrower()
 const page = await pupp.openPage( brower)
 const url = "https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0"
 await page.goto(url);
 while(true){           // 循环点击, 直到获取不到该元素
  try{
   await page.waitFor(1000); 
   await page.waitForSelector('a[class=more]'); // 等待元素加载完成,超时时间30000ms
   await page.click("a[class=more]")
   // break
  }catch(err){
   console.log(err)
   console.log("stop click !!!")
   break
  }
 } 
 await page.waitFor(1000);        // 等待页面一秒
 const links = await page.evaluate(() => {    // 获取电影详情url
  let movies = [...document.querySelectorAll('.list a[class=item]')];
  return movies.map((movie) =>{
   return {
    href: movie.href.trim(),
   }
  });
 });
 console.log(links.length)
 for (var i = 0; i < links.length; i++) {
  const a = links[i];
  await page.waitFor(2000); 
  await getDetail(brower, a.href)
  // break
 }
 await pupp.closePage(page)
 await pupp.closeBrower(brower)
 
}

async function getDetail(brower, url){
 const page = await pupp.openPage(brower)
 await page.goto(url);
 await page.waitFor(1000); 
 try{
  await page.click(".more-actor", {delay: 20})
 }catch(err){
  console.log(err)
 }
 const frame = await page.mainFrame()
 const bodyHandle = await frame.$('html');
 const html = await frame.evaluate(body => body.innerHTML, bodyHandle);
 await bodyHandle.dispose();      // 销毁      
 const $ = cheerio.load(html)
 const title = $("h1 span").text().trim()
 const rating_num = $(".rating_num").text().trim()
 const data = {}
 data["title"] = title
 data["rating_num"] = rating_num
 let info = $("#info").text()
 const keyword = ["director", "screenplay", "lead", "type", "website", "location", "language", "playdate", "playtime", "byname", "imdb"]
 if (info.indexOf("www.") > 0){
  info = info.replace(/https:\/\/|http:\/\//g, "").replace(/\t/g," ").replace(/\r/g, " ").split(":")
  for(var i = 1; i < info.length; i++){
   data[keyword[i-1]] = info[i].split(/\n/g)[0].replace(/ \/ /g, ",").trim()
  }

 }else{
  info = info.replace(/\t/g," ").replace(/\r/g, " ").split(":")
  keyword.splice(4,1)
  for(var i = 1; i < info.length-1; i++){
   data[keyword[i-1]] = info[i].split(/\n/g)[0].replace(/ \/ /g, ",").trim()
  }
  data["website"] = ""
 }
 // console.log(data)
 MongoClient.connect(Urls,function(err,db){       //获取连接
  assert.equal(null,err);           //使用断言模块代替以前的 if判断
  var dbo = db.db("douban");
  dbo.collection("detail").insert(data, function(err,result){  //连接到数据库上面,并使用参数传入集合
   assert.equal(null,err);
   console.log(result);
   db.close();
  });
 });
 await pupp.closePage(page)
}

getList()

以上的代码完成了对豆瓣热门电影的全部抓取,有以下几个步骤:

1, 循环点击加载更多,直到没有此元素可操作而抛出异常

2,加载完全部热门电影列表时解析出每个电影详情页的url并逐一请求

3, 解析详情页的所需数据,

4,对所抓取数据进行入库,这里使用MongoDB

入库后的数据如下所示:

Puppeteer使用示例详解

对以上的浏览器实例化做了优化,写成了单例模式

config.js

module.exports = {
 browserOptions:{
 headless: false,
 // args: ['--no-sandbox', '--proxy-server=http://proxy:abc100@cp.test.com:8995'],
 args: ['--no-sandbox'],
 }
};

brower.js

const puppeteer = require("puppeteer");
const config = require('./config');//
const deasync = require('deasync');
const BROWSER_KEY = Symbol.for('browser');
const BROWSER_STATUS_KEY = Symbol.for('browser_status');

launch(config.browserOptions)
wait4Lunch();

/**
 * 启动并获取浏览器实例
 * @param {*} options
 * param options is puppeteer.launch function's options
 */
function launch(options = {}) {
 if (!global[BROWSER_STATUS_KEY]) {
 global[BROWSER_STATUS_KEY] = 'lunching';
 puppeteer.launch(options)
  .then((browser) => {
  global[BROWSER_KEY] = browser;
  global[BROWSER_STATUS_KEY] = 'lunched';
  })
  .catch((err) => {
  global[BROWSER_STATUS_KEY] = 'error';
  throw err;
  });
 }
}

function wait4Lunch(){
 while (!global[BROWSER_KEY] && global[BROWSER_STATUS_KEY] == 'lunching') {
 // wait for lunch
 deasync.runLoopOnce();
 }
}
module.exports = global[BROWSER_KEY];

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python登录QQ邮箱发信的实现代码
Feb 10 Python
Python中为什么要用self探讨
Apr 14 Python
python写日志封装类实例
Jun 28 Python
使用Python脚本生成随机IP的简单方法
Jul 30 Python
Python实现快速排序算法及去重的快速排序的简单示例
Jun 26 Python
Pycharm远程调试原理及具体配置详解
Aug 08 Python
python 读取.nii格式图像实例
Jul 01 Python
详解pyqt5的UI中嵌入matplotlib图形并实时刷新(挖坑和填坑)
Aug 07 Python
Python下载网易云歌单歌曲的示例代码
Aug 12 Python
python如何设置静态变量
Sep 07 Python
Python 列表推导式需要注意的地方
Oct 23 Python
Python模拟登录和登录跳转的参考示例
Oct 30 Python
获取django框架orm query执行的sql语句实现方法分析
Jun 20 #Python
Python使用LDAP做用户认证的方法
Jun 20 #Python
Python OpenCV中的resize()函数的使用
Jun 20 #Python
python中的句柄操作的方法示例
Jun 20 #Python
使用python获取(宜宾市地震信息)地震信息
Jun 20 #Python
一篇文章了解Python中常见的序列化操作
Jun 20 #Python
python集合是否可变总结
Jun 20 #Python
You might like
CI框架开发新浪微博登录接口源码完整版
2014/05/28 PHP
完美利用Yii2微信后台开发的系列总结
2016/07/18 PHP
PHP获取数组中指定的一列实例
2017/12/27 PHP
PHP正则之正向预查与反向预查讲解与实例
2020/04/06 PHP
javascript 常用关键字列表集合
2007/12/04 Javascript
Checbox的操作含已选、未选及判断代码
2013/11/07 Javascript
JS对象与json字符串格式转换实例
2014/10/28 Javascript
js构造函数、索引数组和属性的实现方式和使用
2014/11/16 Javascript
JS动画效果打开、关闭层的实现方法
2015/05/09 Javascript
javascript中attachEvent用法实例分析
2015/05/14 Javascript
JQuery节点元素属性操作方法
2015/06/11 Javascript
基于Css3和JQuery实现打字机效果
2015/08/11 Javascript
通过点击jqgrid表格弹出需要的表格数据
2015/12/02 Javascript
js仿QQ中对联系人向左滑动、滑出删除按钮的操作
2016/04/07 Javascript
Bootstrap教程JS插件弹出框学习笔记分享
2016/05/17 Javascript
仿百度换肤功能的简单实例代码
2016/07/11 Javascript
详解vue.js的事件处理器v-on:click
2017/06/27 Javascript
浅析JS抽象工厂模式
2017/12/14 Javascript
微信小程序js文件改变参数并在视图上及时更新【推荐】
2018/06/11 Javascript
vue插件mescroll.js实现移动端上拉加载和下拉刷新
2019/03/07 Javascript
Vue源码之关于vm.$delete()/Vue.use()内部原理详解
2019/05/01 Javascript
vue轻量级框架无法获取到vue对象解决方法
2019/05/12 Javascript
详解VSCode配置启动Vue项目
2019/05/14 Javascript
vue二选一tab栏切换新做法实现
2021/01/19 Vue.js
使用python实现baidu hi自动登录的代码
2013/02/10 Python
python轻松实现代码编码格式转换
2015/03/26 Python
python3.5 tkinter实现页面跳转
2018/01/30 Python
Python实现字典排序、按照list中字典的某个key排序的方法示例
2018/12/18 Python
我用Python抓取了7000 多本电子书案例详解
2019/03/25 Python
Python爬虫爬取博客实现可视化过程解析
2020/06/29 Python
英国复古皮包品牌:Beara Beara
2018/07/18 全球购物
介绍一下代理模式(Proxy)
2014/10/17 面试题
六查六看自查材料
2014/02/17 职场文书
学校师德师风自我剖析材料
2014/09/29 职场文书
导游词范文
2015/02/13 职场文书
背起爸爸上学观后感
2015/06/08 职场文书