爬虫利器Puppeteer实战


Posted in Javascript onJanuary 09, 2019

Puppeteer 介绍

Puppeteer 翻译是操纵木偶的人,利用这个工具,我们能做一个操纵页面的人。 Puppeteer 是一个 Nodejs 的库,支持调用 Chrome的API来操纵Web ,相比较 Selenium 或是 PhantomJs ,它最大的特点就是它的操作 Dom 可以完全在内存中进行模拟既在 V8 引擎中处理而不打开浏览器,而且关键是这个是Chrome团队在维护,会拥有更好的兼容性和前景。

Puppeteer 用处

  • 利用网页生成PDF、图片
  • 爬取SPA应用,并生成预渲染内容(即“SSR” 服务端渲染)
  • 可以从网站抓取内容
  • 自动化表单提交、UI测试、键盘输入等
  • 帮你创建一个最新的自动化测试环境(chrome),可以直接在此运行测试用例6.捕获站点的时间线,以便追踪你的网站,帮助分析网站性能问题

Puppeteer 使用

安装 Puppeteer

由于封网,直接下载 Chromium 会失败,可以先阻止下载 Chromium 然后再手动下载它

# 安装命令
npm i puppeteer --save

# 错误信息
ERROR: Failed to download Chromium r515411! Set "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" env variable to skip download.

# 设置环境变量跳过下载 Chromium
set PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 

# 或者可以这样干,只下载模块而不build
npm i --save puppeteer --ignore-scripts

# 成功安装模块
+ puppeteer@0.13.0
added 1 package in 1.77s

手动下载 Chromium,下载完后将压缩包解压,会有个 Chromium.app,将其放在你喜欢的目录下,例如 /Users/huqiyang/Documents/project/z/chromium/Chromium.app。正常安装包后 Chromium.app 会在 .local-chromium

Tip:下载 Chromium 失败解决办法

更换国内Chromium源

PUPPETEER_DOWNLOAD_HOST=https://storage.googleapis.com.cnpmjs.org
npm i puppeteer

或者用 cnpm 安装

npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm i puppeteer

点击查阅 Puppeteer  API

初试 Puppeteer,截个图吧

知识点

  • puppeteer.launch 启动浏览器实例
  • browser.newPage()  创建一个新页面
  • page.goto 进入指定网页
  • page.screenshot 截图
const puppeteer = require('puppeteer');

(async () => {
 const browser = await (puppeteer.launch({
  // 若是手动下载的chromium需要指定chromium地址, 默认引用地址为 /项目目录/node_modules/puppeteer/.local-chromium/
  executablePath: '/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium',
  //设置超时时间
  timeout: 15000,
  //如果是访问https页面 此属性会忽略https错误
  ignoreHTTPSErrors: true,
  // 打开开发者工具, 当此值为true时, headless总为false
  devtools: false,
  // 关闭headless模式, 不会打开浏览器
  headless: false
 }));
 const page = await browser.newPage();
 await page.goto('https://www.jianshu.com/u/40909ea33e50');
 await page.screenshot({
  path: 'jianshu.png',
  type: 'png',
  // quality: 100, 只对jpg有效
  fullPage: true,
  // 指定区域截图,clip和fullPage两者只能设置一个
  // clip: {
  //  x: 0,
  //  y: 0,
  //  width: 1000,
  //  height: 40
  // }
 });
 browser.close();
})();

运行结果 

爬虫利器Puppeteer实战

进阶,获取网易云音乐的歌词和评论

网易云音乐的API经过AES和RSA算法加密,需要携带加密的信息通过POST方式请求才能获取到数据。但 Puppeteer 出现后,这些都不重要了,只要它页面上显示了,通过 Puppeteer 都能获取到该元素。

知识点

  • page.type 获取输入框焦点并输入文字
  • page.keyboard.press 模拟键盘按下某个按键,目前mac上组合键无效为已知bug
  • page.waitFor 页面等待,可以是时间、某个元素、某个函数
  • page.frames() 获取当前页面所有的 iframe,然后根据 iframe 的名字精确获取某个想要的 iframe
  • iframe.$('.srchsongst') 获取 iframe 中的某个元素
  • iframe.evaluate() 在浏览器中执行函数,相当于在控制台中执行函数,返回一个 Promise
  • Array.from 将类数组对象转化为对象
  • page.click() 点击一个元素
  • iframe.$eval() 相当于在 iframe 中运行 document.queryselector 获取指定元素,并将其作为第一个参数传递
  • iframe.$$eval 相当于在 iframe 中运行 document.querySelectorAll 获取指定元素数组,并将其作为第一个参数传递
const fs = require('fs');
const puppeteer = require('puppeteer');

(async () => {
 const browser = await (puppeteer.launch({ executablePath: '/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium', headless: false }));
 const page = await browser.newPage();
 // 进入页面
 await page.goto('https://music.163.com/#');

 // 点击搜索框拟人输入 鬼才会想起
 const musicName = '鬼才会想';
 await page.type('.txt.j-flag', musicName, {delay: 0});

 // 回车
 await page.keyboard.press('Enter');

 // 获取歌曲列表的 iframe
 await page.waitFor(2000);
 let iframe = await page.frames().find(f => f.name() === 'contentFrame');
 const SONG_LS_SELECTOR = await iframe.$('.srchsongst');

 // 获取歌曲 鬼才会想起 的地址
 const selectedSongHref = await iframe.evaluate(e => {
  const songList = Array.from(e.childNodes);
  const idx = songList.findIndex(v => v.childNodes[1].innerText.replace(/\s/g, '') === '鬼才会想起');
  return songList[idx].childNodes[1].firstChild.firstChild.firstChild.href;
 }, SONG_LS_SELECTOR);

 // 进入歌曲页面
 await page.goto(selectedSongHref);

 // 获取歌曲页面嵌套的 iframe
 await page.waitFor(2000);
 iframe = await page.frames().find(f => f.name() === 'contentFrame');

 // 点击 展开按钮
 const unfoldButton = await iframe.$('#flag_ctrl');
 await unfoldButton.click();

 // 获取歌词
 const LYRIC_SELECTOR = await iframe.$('#lyric-content');
 const lyricCtn = await iframe.evaluate(e => {
  return e.innerText;
 }, LYRIC_SELECTOR);

 console.log(lyricCtn);

 // 截图
 await page.screenshot({
  path: '歌曲.png',
  fullPage: true,
 });

 // 写入文件
 let writerStream = fs.createWriteStream('歌词.txt');
 writerStream.write(lyricCtn, 'UTF8');
 writerStream.end();

 // 获取评论数量
 const commentCount = await iframe.$eval('.sub.s-fc3', e => e.innerText);
 console.log(commentCount);

 // 获取评论
 const commentList = await iframe.$$eval('.itm', elements => {
  const ctn = elements.map(v => {
   return v.innerText.replace(/\s/g, '');
  });
  return ctn;
 });
 console.log(commentList);
})();

运行结果

爬虫利器Puppeteer实战

爬虫利器Puppeteer实战

爬虫利器Puppeteer实战

高级爬虫

爬取SPA应用,并生成预渲染内容(即“SSR” 服务端渲染),通俗讲就是在页面上显示的内容我们都能获取到。下面我们就通过爬取 瓜子二手车直卖网 的车辆信息来认识它。

首先通过 axios 来试试

const axios = require('axios');
const useAxios = () => {
 axios.get('https://www.guazi.com/hz/buy/')
  .then(((result) => {
   console.log(result.data);
  }))
  .catch((err) => {
   console.log(err);
  });
};

结果它返回给我这个玩意,这显然不是我要的内容

爬虫利器Puppeteer实战

通过 Puppeteer 爬取

const fs = require('fs');
const puppeteer = require('puppeteer');

(async () => {
 const browser = await (puppeteer.launch({ executablePath: '/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium', headless: true }));
 const page = await browser.newPage();

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

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

 // 获取汽车品牌
 const BRANDS_INFO_SELECTOR = '.dd-all.clearfix.js-brand.js-option-hid-info';
 const brands = await page.evaluate(sel => {
  const ulList = Array.from($(sel).find('ul li p a'));
  const ctn = ulList.map(v => {
   return v.innerText.replace(/\s/g, '');
  });
  return ctn;
 }, BRANDS_INFO_SELECTOR);
 console.log('汽车品牌: ', JSON.stringify(brands));
 let writerStream = fs.createWriteStream('car_brands.json');
 writerStream.write(JSON.stringify(brands, undefined, 2), 'UTF8');
 writerStream.end();
 // await bodyHandle.dispose();

 // 获取车源列表
 const CAR_LIST_SELECTOR = 'ul.carlist';
 const carList = await page.evaluate((sel) => {
  const catBoxs = Array.from($(sel).find('li a'));
  const ctn = catBoxs.map(v => {
   const title = $(v).find('h2.t').text();
   const subTitle = $(v).find('div.t-i').text().split('|');
   return {
    title: title,
    year: subTitle[0],
    milemeter: subTitle[1]
   };
  });
  return ctn;
 }, CAR_LIST_SELECTOR);

 console.log(`总共${carList.length}辆汽车数据: `, JSON.stringify(carList, undefined, 2));

 // 将车辆信息写入文件
 writerStream = fs.createWriteStream('car_info_list.json');
 writerStream.write(JSON.stringify(carList, undefined, 2), 'UTF8');
 writerStream.end();

 browser.close();
})();

运行结果

爬虫利器Puppeteer实战

爬虫利器Puppeteer实战

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

Javascript 相关文章推荐
DEFER怎么用?
Jul 01 Javascript
extjs 学习笔记(二) Ext.Element类
Oct 13 Javascript
js中精确计算加法和减法示例
Mar 28 Javascript
jQuery使用height()获取高度需要注意的地方
Dec 13 Javascript
JavaScript版的TwoQueues缓存模型
Dec 29 Javascript
JS实现控制表格内指定单元格内容对齐的方法
Mar 30 Javascript
写给小白的JavaScript引擎指南
Dec 04 Javascript
JavaScript+html5 canvas实现本地截图教程
Apr 16 Javascript
JavaScript用构造函数如何获取变量的类型名
Dec 23 Javascript
简单的Vue异步组件实例Demo
Dec 27 Javascript
JS实现字符串翻转的方法分析
Aug 31 Javascript
js实现动态增加文件域表单功能
Oct 22 Javascript
puppeteer库入门初探
Jan 09 #Javascript
node.js的Express服务器基本使用教程
Jan 09 #Javascript
JavaScript学习笔记之基于定时器实现图片无缝滚动功能详解
Jan 09 #Javascript
element-ui 时间选择器限制范围的实现(随动)
Jan 09 #Javascript
JavaScript学习笔记之DOM基础操作实例小结
Jan 09 #Javascript
如何解决webpack-dev-server代理常切换问题
Jan 09 #Javascript
JavaScript学习笔记之数组基本操作示例
Jan 09 #Javascript
You might like
php中记录用户访问过的产品,在cookie记录产品id,id取得产品信息
2011/05/04 PHP
PHP中的socket_read和socket_recv区别详解
2015/02/09 PHP
php pdo oracle中文乱码的快速解决方法
2016/05/16 PHP
PHP使用PDO、mysqli扩展实现与数据库交互操作详解
2019/07/20 PHP
PHP迭代器和生成器用法实例分析
2019/09/28 PHP
关于laravel5.5的定时任务详解(demo)
2019/10/23 PHP
Prototype使用指南之hash.js
2007/01/10 Javascript
关于window.pageYOffset和document.documentElement.scrollTop
2011/04/05 Javascript
jquery submit ie6下失效的原因分析及解决方法
2013/11/15 Javascript
浅谈javascript中createElement事件
2014/12/05 Javascript
vue限制输入框只能输入8位整数和2位小数的代码
2019/11/06 Javascript
vue-iview动态新增和删除的方法
2020/06/17 Javascript
[02:05:03]完美世界DOTA2联赛循环赛 LBZS VS Matador BO2 10.28
2020/10/28 DOTA
Python中optparse模块使用浅析
2015/01/01 Python
Python 提取dict转换为xml/json/table并输出的实现代码
2016/08/28 Python
CentOS 6.X系统下升级Python2.6到Python2.7 的方法
2016/10/12 Python
Python3.5以上版本lxml导入etree报错的解决方案
2019/06/26 Python
详解Django 时间与时区设置问题
2019/07/23 Python
Python操作远程服务器 paramiko模块详细介绍
2019/08/07 Python
Python 简单计算要求形状面积的实例
2020/01/18 Python
Python控制鼠标键盘代码实例
2020/12/08 Python
CSS3中animation实现流光按钮效果
2020/12/21 HTML / CSS
浅谈基于Canvas的手绘风格图形库Rough.js
2018/03/19 HTML / CSS
美国生日蛋糕店:Bake Me A Wish!
2017/02/08 全球购物
巴西图书和电子产品购物网站:Saraiva
2017/06/07 全球购物
美国网上鞋子零售商:Dr. Scholl’s Shoes
2017/11/17 全球购物
英国最受欢迎的在线隐形眼镜商店:VisionDirect.co.uk
2018/12/06 全球购物
希腊品牌鞋类销售网站:epapoutsia.gr
2020/03/18 全球购物
用JAVA SOCKET编程,读服务器几个字符,再写入本地显示
2012/11/25 面试题
介绍一下#error预处理
2015/09/25 面试题
服务员岗位职责
2015/02/03 职场文书
房地产销售主管岗位职责
2015/02/13 职场文书
2015年保育员个人工作总结
2015/05/13 职场文书
小学生暑假生活总结
2015/07/13 职场文书
怎样评估创业计划书是否有可行性?
2019/08/07 职场文书
Django REST framework 限流功能的使用
2021/06/24 Python