超越Selenium的存在---Pyppeteer

2024-04-17 07:58

本文主要是介绍超越Selenium的存在---Pyppeteer,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

阅读本文大概需要 10 分钟。

 

如果大家对 Python 爬虫有所了解的话,想必你应该听说过 Selenium 这个库,这实际上是一个自动化测试工具,现在已经被广泛用于网络爬虫中来应对 JavaScript 渲染的页面的抓取。

但 Selenium 用的时候有个麻烦事,就是环境的相关配置,得安装好相关浏览器,比如 Chrome、Firefox 等等,然后还要到官方网站去下载对应的驱动,最重要的还需要安装对应的 Python Selenium 库,确实是不是很方便,另外如果要做大规模部署的话,环境配置的一些问题也是个头疼的事情。

那么本节就介绍另一个类似的替代品,叫做 Pyppeteer。注意,是叫做 Pyppeteer,不是 Puppeteer。Puppeteer 是 Google 基于 Node.js 开发的一个工具,有了它我们可以通过 JavaScript 来控制 Chrome 浏览器的一些操作,当然也可以用作网络爬虫上,其 API 极其完善,功能非常强大。而 Pyppeteer 又是什么呢?它实际上是 Puppeteer 的 Python 版本的实现,但他不是 Google 开发的,是一位来自于日本的工程师依据 Puppeteer 的一些功能开发出来的非官方版本。

在 Pyppetter 中,实际上它背后也是有一个类似 Chrome 浏览器的 Chromium 浏览器在执行一些动作进行网页渲染,首先说下 Chrome 浏览器和 Chromium 浏览器的渊源。

Chromium 是谷歌为了研发 Chrome 而启动的项目,是完全开源的。二者基于相同的源代码构建,Chrome 所有的新功能都会先在 Chromium 上实现,待验证稳定后才会移植,因此 Chromium 的版本更新频率更高,也会包含很多新的功能,但作为一款独立的浏览器,Chromium 的用户群体要小众得多。两款浏览器“同根同源”,它们有着同样的 Logo,但配色不同,Chrome 由蓝红绿黄四种颜色组成,而 Chromium 由不同深度的蓝色构成。

640?wx_fmt=jpeg
Chromium vs Chrome

总的来说,两款浏览器的内核是一样的,实现方式也是一样的,可以认为是开发版和正式版的区别,功能上基本是没有太大区别的。

Pyppeteer 就是依赖于 Chromium 这个浏览器来运行的。那么有了 Pyppeteer 之后,我们就可以免去那些繁琐的环境配置等问题。如果第一次运行的时候,Chromium 浏览器没有安全,那么程序会帮我们自动安装和配置,就免去了繁琐的环境配置等工作。另外 Pyppeteer 是基于 Python 的新特性 async 实现的,所以它的一些执行也支持异步操作,效率相对于 Selenium 来说也提高了。

那么下面就让我们来一起了解下 Pyppeteer 的相关用法吧。

安装

首先就是安装问题了,由于 Pyppeteer 采用了 Python 的 async 机制,所以其运行要求的 Python 版本为 3.5 及以上。

安装方式非常简单:

pip3 install pyppeteer

好了,安装完成之后我们命令行下测试下:

>>> import pyppeteer

如果没有报错,那么就证明安装成功了。

快速上手

接下来我们测试下基本的页面渲染操作,这里我们选用的网址为:http://quotes.toscrape.com/js/,这个页面是 JavaScript 渲染而成的,用基本的 requests 库请求得到的 HTML 结果里面是不包含页面中所见的条目内容的。

为了证明 requests 无法完成正常的抓取,我们可以先用如下代码来测试一下:

import requests
from pyquery import PyQuery as pqurl = 'http://quotes.toscrape.com/js/'
response = requests.get(url)
doc = pq(response.text)
print('Quotes:', doc('.quote').length)

这里首先使用 requests 来请求网页内容,然后使用 pyquery 来解析页面中的每一个条目。观察源码之后我们发现每个条目的 class 名为 quote,所以这里选用了 .quote 这个 CSS 选择器来选择,最后输出条目数量。

运行结果:

Quotes: 0

结果是 0,这就证明使用 requests 是无法正常抓取到相关数据的。因为什么?因为这个页面是 JavaScript 渲染而成的,我们所看到的内容都是网页加载后又执行了 JavaScript 之后才呈现出来的,因此这些条目数据并不存在于原始 HTML 代码中,而 requests 仅仅抓取的是原始 HTML 代码。

好的,所以遇到这种类型的网站我们应该怎么办呢?

其实答案有很多:

  • 分析网页源代码数据,如果数据是隐藏在 HTML 中的其他地方,以 JavaScript 变量的形式存在,直接提取就好了。

  • 分析 Ajax,很多数据可能是经过 Ajax 请求时候获取的,所以可以分析其接口。

  • 模拟 JavaScript 渲染过程,直接抓取渲染后的结果。

而 Pyppeteer 和 Selenium 就是用的第三种方法,下面我们再用 Pyppeteer 来试试,如果用 Pyppeteer 实现如上页面的抓取的话,代码就可以写为如下形式:

import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pqasync def main():browser = await launch()page = await browser.newPage()await page.goto('http://quotes.toscrape.com/js/')doc = pq(await page.content())print('Quotes:', doc('.quote').length)await browser.close()asyncio.get_event_loop().run_until_complete(main())

运行结果:

Quotes: 10

看运行结果,这说明我们就成功匹配出来了 class 为 quote 的条目,总数为 10 条,具体的内容可以进一步使用 pyquery 解析查看。

那么这里面的过程发生了什么?

实际上,Pyppeteer 整个流程就完成了浏览器的开启、新建页面、页面加载等操作。另外 Pyppeteer 里面进行了异步操作,所以需要配合 async/await 关键词来实现。

首先, launch 方法会新建一个 Browser 对象,然后赋值给 browser,然后调用 newPage  方法相当于浏览器中新建了一个选项卡,同时新建了一个 Page 对象。然后 Page 对象调用了 goto 方法就相当于在浏览器中输入了这个 URL,浏览器跳转到了对应的页面进行加载,加载完成之后再调用 content 方法,返回当前浏览器页面的源代码。然后进一步地,我们用 pyquery 进行同样地解析,就可以得到 JavaScript 渲染的结果了。

另外其他的一些方法如调用 asyncio 的 get_event_loop 等方法的相关操作则属于 Python 异步 async 相关的内容了,大家如果不熟悉可以了解下 Python 的 async/await 的相关知识。

好,通过上面的代码,我们就可以完成 JavaScript 渲染页面的爬取了。

在这个过程中,我们没有配置 Chrome 浏览器,没有配置浏览器驱动,免去了一些繁琐的步骤,同样达到了 Selenium 的效果,还实现了异步抓取,爽歪歪!

接下来我们再看看另外一个例子,这个例子可以模拟网页截图,保存 PDF,另外还可以执行自定义的 JavaScript 获得特定的内容,代码如下:

import asyncio
from pyppeteer import launchasync def main():browser = await launch()page = await browser.newPage()await page.goto('http://quotes.toscrape.com/js/')await page.screenshot(path='example.png')await page.pdf(path='example.pdf')dimensions = await page.evaluate('''() => {return {width: document.documentElement.clientWidth,height: document.documentElement.clientHeight,deviceScaleFactor: window.devicePixelRatio,}}''')print(dimensions)# >>> {'width': 800, 'height': 600, 'deviceScaleFactor': 1}await browser.close()asyncio.get_event_loop().run_until_complete(main())

这里我们又用到了几个新的 API,完成了网页截图保存、网页导出 PDF 保存、执行 JavaScript 并返回对应数据。

首先 screenshot 方法可以传入保存的图片路径,另外还可以指定保存格式 type、清晰度 quality、是否全屏 fullPage、裁切 clip 等各个参数实现截图。

截图的样例如下:

640?wx_fmt=jpeg
效果页面

可以看到它返回的就是 JavaScript 渲染后的页面。

pdf 方法也是类似的,只不过页面保存格式不一样,最后得到一个多页的 pdf 文件,样例如下:

640?wx_fmt=jpeg
保存的PDF

可见其内容也是 JavaScript 渲染后的内容,另外这个方法还可以指定放缩大小 scale、页码范围 pageRanges、宽高 width 和 height、方向 landscape 等等参数,导出定制化的 pdf 用这个方法就十分方便。

最后我们又调用了 evaluate 方法执行了一些 JavaScript,JavaScript 传入的是一个函数,使用 return 方法返回了网页的宽高、像素大小比率三个值,最后得到的是一个 JSON 格式的对象,内容如下:

{'width': 800, 'height': 600, 'deviceScaleFactor': 1}

OK,实例就先感受到这里,还有太多太多的功能还没提及。

总之利用 Pyppeteer 我们可以控制浏览器执行几乎所有动作,想要的操作和功能基本都可以实现,用它来自由地控制爬虫当然就不在话下了。

详细用法

了解了基本的实例之后,我们再来梳理一下 Pyppeteer 的一些基本和常用操作。Pyppeteer 的几乎所有功能都能在其官方文档的 API Reference 里面找到,链接为:https://miyakogi.github.io/pyppeteer/reference.html,用到哪个方法就来这里查询就好了,参数不必死记硬背,即用即查就好。

开启浏览器

使用 Pyppeteer 的第一步便是启动浏览器,首先我们看下怎样启动一个浏览器,其实就相当于我们点击桌面上的浏览器图标一样,把它开起来。用 Pyppeteer 完成同样的操作,只需要调用 launch 方法即可。

我们先看下 launch 方法的 API,链接为:https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.launcher.launch,其方法定义如下:

pyppeteer.launcher.launch(options: dict = None, **kwargs) → pyppeteer.browser.Browser

可以看到它处于 launcher 模块中,参数没有在声明中特别指定,返回类型是 browser 模块中的 Browser 对象,另外观察源码发现这是一个 async 修饰的方法,所以调用它的时候需要使用 await。

接下来看看它的参数:

  • ignoreHTTPSErrors (bool): 是否要忽略 HTTPS 的错误,默认是 False。

  • headless (bool): 是否启用 Headless 模式,即无界面模式,如果 devtools 这个参数是 True 的话,那么该参数就会被设置为 False,否则为 True,即默认是开启无界面模式的。

  • executablePath (str): 可执行文件的路径,如果指定之后就不需要使用默认的 Chromium 了,可以指定为已有的 Chrome 或 Chromium。

  • slowMo (int|float): 通过传入指定的时间,可以减缓 Pyppeteer 的一些模拟操作。

  • args (List[str]): 在执行过程中可以传入的额外参数。

  • ignoreDefaultArgs (bool): 不使用 Pyppeteer 的默认参数,如果使用了这个参数,那么最好通过 args 参数来设定一些参数,否则可能会出现一些意想不到的问题。这个参数相对比较危险,慎用。

  • handleSIGINT (bool): 是否响应 SIGINT 信号,也就是可以使用 Ctrl + C 来终止浏览器程序,默认是 True。

  • handleSIGTERM (bool): 是否响应 SIGTERM 信号,一般是 kill 命令,默认是 True。

  • handleSIGHUP (bool): 是否响应 SIGHUP 信号,即挂起信号,比如终端退出操作,默认是 True。

  • dumpio (bool): 是否将 Pyppeteer 的输出内容传给 process.stdout 和 process.stderr 对象,默认是 False。

  • userDataDir (str): 即用户数据文件夹,即可以保留一些个性化配置和操作记录。

  • env (dict): 环境变量,可以通过字典形式传入。

  • devtools (bool): 是否为每一个页面自动开启调试工具,默认是 False。如果这个参数设置为 True,那么 headless 参数就会无效,会被强制设置为 False。

  • logLevel  (int|str): 日志级别,默认和 root logger 对象的级别相同。

  • autoClose (bool): 当一些命令执行完之后,是否自动关闭浏览器,默认是 True。

  • loop (asyncio.AbstractEventLoop): 时间循环对象。

好了,知道这些参数之后,我们可以先试试看。

首先可以试用下最常用的参数 headless,如果我们将它设置为 True 或者默认不设置它,在启动的时候我们是看不到任何界面的,如果把它设置为 False,那么在启动的时候就可以看到界面了,一般我们在调试的时候会把它设置为 False,在生产环境上就可以设置为 True,我们先尝试一下关闭 headless 模式:

import asyncio
from pyppeteer import launchasync def main():await launch(headless=False)await asyncio.sleep(100)asyncio.get_event_loop().run_until_complete(main())

运行之后看不到任何控制台输出,但是这时候就会出现一个空白的 Chromium 界面了:

640?wx_fmt=jpeg
关闭 Headless 模式之后的界面

但是可以看到这就是一个光秃秃的浏览器而已,看一下相关信息:

640?wx_fmt=jpeg
浏览器相关信息

看到了,这就是 Chromium,上面还写了开发者内部版本,可以认为是开发版的 Chrome 浏览器就好。

另外我们还可以开启调试模式,比如在写爬虫的时候会经常需要分析网页结构还有网络请求,所以开启调试工具还是很有必要的,我们可以将 devtools 参数设置为 True,这样每开启一个界面就会弹出一个调试窗口,非常方便,示例如下:

import asyncio
from pyppeteer import launchasync def main():browser = await launch(devtools=True)page = await browser.newPage()await page.goto('https://www.baidu.com')await asyncio.sleep(100)asyncio.get_event_loop().run_until_complete(main())

刚才说过 devtools 这个参数如果设置为了 True,那么 headless 就会被关闭了,界面始终会显现出来。在这里我们新建了一个页面,打开了百度,界面运行效果如下:

640?wx_fmt=jpeg
调试模式

这时候我们可以看到上面的一条提示:"Chrome 正受到自动测试软件的控制",这个提示条有点烦,那咋关闭呢?这时候就需要用到 args 参数了,禁用操作如下:

browser = await launch(headless=False, args=['--disable-infobars'])

这里就不再写完整代码了,就是在 launch 方法中,args 参数通过 list 形式传入即可,这里使用的是 --disable-infobars 的参数。

另外有人就说了,这里你只是把提示关闭了,有些网站还是会检测到是 webdriver 吧,比如淘宝检测到是 webdriver 就会禁止登录了,我们可以试试:

import asyncio
from pyppeteer import launchasync def main():browser = await launch(headless=False)page = await browser.newPage()await page.goto('https://www.taobao.com')await asyncio.sleep(100)asyncio.get_event_loop().run_until_complete(main())

运行时候进行一下登录,然后就会弹出滑块,自己手动拖动一下,然后就报错了,界面如下:

640?wx_fmt=jpeg
淘宝登录失败

爬虫的时候看到这界面是很让人崩溃的吧,而且这时候我们还发现了页面的 bug,整个浏览器窗口比显示的内容窗口要大,这个是某些页面会出现的情况,让人看起来很不爽。

我们可以先解决一下这个显示的 bug,需要设置下 window-size 还有 viewport,代码如下:

import asyncio
from pyppeteer import launchwidth, height = 1366, 768async def main():browser = await launch(headless=False,args=[f'--window-size={width},{height}'])page = await browser.newPage()await page.setViewport({'width': width, 'height': height})await page.goto('https://www.taobao.com')await asyncio.sleep(100)asyncio.get_event_loop().run_until_complete(main())

这样整个界面就正常了:

640?wx_fmt=jpeg
正常的界面

OK,那刚才所说的 webdriver 检测问题怎样来解决呢?其实淘宝主要通过 window.navigator.webdriver 来对 webdriver 进行检测,所以我们只需要使用 JavaScript 将它设置为 false 即可,代码如下:

import asyncio
from pyppeteer import launchasync def main():browser = await launch(headless=False, args=['--disable-infobars'])page = await browser.newPage()await page.goto('https://login.taobao.com/member/login.jhtml?redirectURL=https://www.taobao.com/')await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')await asyncio.sleep(100)asyncio.get_event_loop().run_until_complete(main())

这里没加输入用户名密码的代码,当然后面可以自行添加,下面打开之后,我们点击输入用户名密码,然后这时候会出现一个滑动条,这里滑动的话,就可以通过了,如图所示:

640?wx_fmt=jpeg
淘宝滑动条验证通过

OK,这样的话我们就成功规避了 webdriver 的检测,使用鼠标拖动模拟就可以完成淘宝的登录了。

还有另一种方法可以进一步免去淘宝登录的烦恼,那就是设置用户目录。平时我们已经注意到,当我们登录淘宝之后,如果下次再次打开浏览器发现还是登录的状态。这是因为淘宝的一些关键 Cookies 已经保存到本地了,下次登录的时候可以直接读取并保持登录状态。

那么这些信息保存在哪里了呢?其实就是保存在用户目录下了,里面不仅包含了浏览器的基本配置信息,还有一些 Cache、Cookies 等各种信息都在里面,如果我们能在浏览器启动的时候读取这些信息,那么启动的时候就可以恢复一些历史记录甚至一些登录状态信息了。

这也就解决了一个问题:很多朋友在每次启动 Selenium 或 Pyppeteer 的时候总是是一个全新的浏览器,那就是没有设置用户目录,如果设置了它,每次打开就不再是一个全新的浏览器了,它可以恢复之前的历史记录,也可以恢复很多网站的登录信息。

那么这个怎么来做呢?很简单,在启动的时候设置 userDataDir 就好了,示例如下:

import asyncio
from pyppeteer import launchasync def main():browser = await launch(headless=False, userDataDir='./userdata', args=['--disable-infobars'])page = await browser.newPage()await page.goto('https://www.taobao.com')await asyncio.sleep(100)asyncio.get_event_loop().run_until_complete(main())

好,这里就是加了一个 userDataDir 的属性,值为 userdata,即当前目录的 userdata 文件夹。我们可以首先运行一下,然后登录一次淘宝,这时候我们同时可以观察到在当前运行目录下又多了一个 userdata 的文件夹,里面的结构是这样子的:

640?wx_fmt=jpeg
用户文件夹

具体的介绍可以看官方的一些说明,如:https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md,这里面介绍了 userdatadir 的相关内容。

再次运行上面的代码,这时候可以发现现在就已经是登录状态了,不需要再次登录了,这样就成功跳过了登录的流程。当然可能时间太久了,Cookies 都过期了,那还是需要登录的。

好了,本想把 Pyppeteer 的用法详细介绍完的,结果只 launch 的方法就介绍这么多了,后面的内容放到其他文章来介绍了,其他的内容后续文章会陆续放出,谢谢。

小彩蛋:以上文章摘自即将完稿的《Python3网络爬虫开发实战(第二版)》,敬请期待,谢谢。

本节代码获取

https://github.com/Python3WebSpider/PyppeteerTest

点击阅读原文也可以直达哦!

640?wx_fmt=gif

“每天学习一点点“

这篇关于超越Selenium的存在---Pyppeteer的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/911228

相关文章

easyui同时验证账户格式和ajax是否存在

accountName: {validator: function (value, param) {if (!/^[a-zA-Z][a-zA-Z0-9_]{3,15}$/i.test(value)) {$.fn.validatebox.defaults.rules.accountName.message = '账户名称不合法(字母开头,允许4-16字节,允许字母数字下划线)';return fal

【408DS算法题】039进阶-判断图中路径是否存在

Index 题目分析实现总结 题目 对于给定的图G,设计函数实现判断G中是否含有从start结点到stop结点的路径。 分析实现 对于图的路径的存在性判断,有两种做法:(本文的实现均基于邻接矩阵存储方式的图) 1.图的BFS BFS的思路相对比较直观——从起始结点出发进行层次遍历,遍历过程中遇到结点i就表示存在路径start->i,故只需判断每个结点i是否就是stop

《战锤40K:星际战士2》超越《黑神话》 登Steam热销榜首

《使命召唤:黑色行动6》将登陆 PC Game Pass看来确实影响了销量,因为这次在 Steam 上它的预购并没有占领 Steam 热销榜单之首。这次霸榜的则是即将推出的《战锤40K:星际战士2》。 根据 SteamDB 显示,这部将于9 月 10 日发售的游戏已经面向预购了高级版本的玩家开放了抢先体验,也因此推动了大量的预购销量。该游戏不仅是目前 Steam 上预购最多的游戏,而且还是总体

超越IP-Adapter!阿里提出UniPortrait,可通过文本定制生成高保真的单人或多人图像。

阿里提出UniPortrait,能根据用户提供的文本描述,快速生成既忠实于原图又能灵活调整的个性化人像,用户甚至可以通过简单的句子来描述多个不同的人物,而不需要一一指定每个人的位置。这种设计大大简化了用户的操作,提升了个性化生成的效率和效果。 UniPortrait以统一的方式定制单 ID 和多 ID 图像,提供高保真身份保存、广泛的面部可编辑性、自由格式的文本描述,并且无需预先确定的布局。

如何使用Selenium捕获控制台日志

Selenium是一个流行的开源工具,用于自动化Web浏览器。其中一个关键功能是能够与浏览器的开发者控制台交互。本文将向您展示如何在Selenium中使用Java获取控制台日志。这些日志对于调试和解决Selenium脚本的问题非常有用。 如何查看任何网页的控制台日志 首先,打开浏览器的开发者控制台。在大多数浏览器中,您可以通过右键点击页面并选择“检查”来做到这一点。我们将在我们的测试网站——h

selenium的webdriver三种等待方式(显式等待WebDriverWait+implicitly_wait隐式等待+sleep强制等待)

隐式等待是等页面加载,不是等元素!!! 1、显式等待  一个显式等待是你定义的一段代码,用于等待某个条件发生然后再继续执行后续代码。显式等待是等元素加载!!! from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import

selenium webdriver实现的原理及流程

用Selenium实现自动化,主要需要三个东西: 1.自动化测试代码:自动化测试代码发送请求给浏览器的驱动(比如火狐驱动、谷歌驱动) 2.浏览器驱动:它来解析这些自动化测试的代码,解析后把它们发送给浏览器 3.浏览器:执行浏览器驱动发来的指令,并最终完成工程师想要的操作。 测试代码中包含了各种期望的对浏览器界面的操作,例如点击。测试代码通过给Webdriver发送指令,让Webdriv

LeetCode题练习与总结:存在重复元素Ⅱ--219

一、题目描述 给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false 。 示例 1: 输入:nums = [1,2,3,1], k = 3输出:true 示例 2: 输入:nums = [1,0,1,1], k

南卡科技“满分之选”全新开放式耳机发布,打造超越Pro的极致体验!

在音频技术的不断革新中,南卡品牌以其深厚的声学底蕴和对创新的不懈追求,再次为市场带来惊喜。今天,我们自豪地宣布,南卡OE Pro2开放式蓝牙耳机正式亮相,它不仅代表了南卡在开放式耳机领域的技术巅峰,更是对音质和佩戴舒适度的双重革新。 31°悬浮倾斜设计,无感佩戴的新高度 南卡OE Pro2将对耳机舒适性的诠释拉升到一个新境界,采用了开放式佩戴设计,彻底告别了传统耳机的堵塞感。基于上万耳

Selenium 实现图片验证码识别

前言 在测试过程中,有的时候登录需要输入图片验证码。这时候使用Selenium进行自动化测试,怎么做图片验证码识别?本篇内容主要介绍使用Selenium、BufferedImage、Tesseract进行图片 验证码识别。 环境准备 jdk:1.8 tessdata:文章末尾附下载地址 安装Tesseract 我本地是ubuntu系统 sudo apt install tesserac