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 文件与目录操作
Dec 24 Python
pygame播放音乐的方法
May 19 Python
Python决策树和随机森林算法实例详解
Jan 30 Python
Python实现时钟显示效果思路详解
Apr 11 Python
python opencv3实现人脸识别(windows)
May 25 Python
python 模拟银行转账功能过程详解
Aug 06 Python
Python代理IP爬虫的新手使用教程
Sep 05 Python
Python 限定函数参数的类型及默认值方式
Dec 24 Python
Python3 main函数使用sys.argv传入多个参数的实现
Dec 25 Python
Python原始套接字编程实例解析
Jan 29 Python
python 实现的车牌识别项目
Jan 25 Python
Django中session进行权限管理的使用
Jul 09 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
php有效防止图片盗用、盗链的两种方法
2016/11/01 PHP
JS支持带x身份证号码验证函数
2008/08/10 Javascript
js AppendChild与insertBefore用法详细对比
2013/12/16 Javascript
JS通过分析userAgent属性来判断浏览器的类型及版本
2014/03/28 Javascript
javascript常用方法总结
2015/05/14 Javascript
JavaScript运动减速效果实例分析
2015/08/04 Javascript
jQuery UI库中dialog对话框功能使用全解析
2016/04/23 Javascript
javascript实现延时显示提示框特效代码
2016/04/27 Javascript
移动端基础事件总结与应用
2017/01/12 Javascript
浅谈js中function的参数默认值
2017/02/20 Javascript
JS实现基于Sketch.js模拟成群游动的蝌蚪运动动画效果【附demo源码下载】
2017/08/18 Javascript
9种改善AngularJS性能的方法
2017/11/28 Javascript
使用Angular CLI从蓝本生成代码详解
2018/03/24 Javascript
vue指令之表单控件绑定v-model v-model与v-bind结合使用
2019/04/17 Javascript
在layui框架中select下拉框监听更改事件的例子
2019/09/20 Javascript
Vue3.0 响应式系统源码逐行分析讲解
2019/10/14 Javascript
vue中h5端打开app(判断是安卓还是苹果)
2021/02/26 Vue.js
[44:15]DOTA2上海特级锦标赛主赛事日 - 5 败者组决赛Liquid VS EG第二局
2016/03/06 DOTA
在Windows服务器下用Apache和mod_wsgi配置Python应用的教程
2015/05/06 Python
Python去除字符串两端空格的方法
2015/05/21 Python
在Python中处理日期和时间的基本知识点整理汇总
2015/05/22 Python
Python的socket模块源码中的一些实现要点分析
2016/06/06 Python
python Tkinter的图片刷新实例
2019/06/14 Python
python字符串的拼接方法总结
2019/11/18 Python
使用pandas 将DataFrame转化成dict
2019/12/10 Python
Python综合应用名片管理系统案例详解
2020/01/03 Python
详解HTML5 canvas绘图基本使用方法
2018/01/29 HTML / CSS
历史专业毕业生的自我鉴定
2013/11/15 职场文书
出纳岗位职责范本
2013/12/01 职场文书
演讲稿格式
2014/04/30 职场文书
全国税务系统先进集体事迹材料
2014/05/19 职场文书
领导欢迎词范文
2015/01/26 职场文书
婚礼伴郎致辞
2015/07/28 职场文书
2016见义勇为事迹材料汇总
2016/03/01 职场文书
怎样评估创业计划书是否有可行性?
2019/08/07 职场文书
python_tkinter事件类型详情
2022/03/20 Python