Puppeteer 是 Google 基于 Node.js 开发的一个工具,有了它咱们能够经过 JavaScript 来控制 Chrome 浏览器的一些操做,固然也能够用做网络爬虫上,其 API 极其完善,功能很是强大。 而 Pyppeteer 又是什么呢?它其实是 Puppeteer 的 Python 版本的实现,但他不是 Google 开发的,是一位来自于日本的工程师依据 Puppeteer 的一些功能开发出来的非官方版本。html
在 Pyppetter 中,实际上它背后也是有一个相似 Chrome 浏览器的 Chromium 浏览器在执行一些动做进行网页渲染,首先说下 Chrome 浏览器和 Chromium 浏览器的渊源。git
Chromium 是谷歌为了研发 Chrome 而启动的项目,是彻底开源的。两者基于相同的源代码构建,Chrome 全部的新功能都会先在 Chromium 上实现,待验证稳定后才会移植,
所以 Chromium 的版本更新频率更高,也会包含不少新的功能,但做为一款独立的浏览器,Chromium 的用户群体要小众得多。两款浏览器“同根同源”,它们有着一样的 Logo,
但配色不一样,Chrome 由蓝红绿黄四种颜色组成,而 Chromium 由不一样深度的蓝色构成。
Pyppeteer 就是依赖于 Chromium 这个浏览器来运行的。那么有了 Pyppeteer 以后,咱们就能够免去那些繁琐的环境配置等问题。若是第一次运行的时候,Chromium 浏览器没有安装,那么程序会帮咱们自动安装和配置,就免去了繁琐的环境配置等工做。另外 Pyppeteer 是基于 Python 的新特性 async 实现的,因此它的一些执行也支持异步操做,效率相对于 Selenium 来讲也提升了。github
安装:web
pip3 install pyppeteer
而Pyppeteer 模拟jsvascript渲染,抓取信息。浏览器
import asyncio from pyppeteer import launch from pyquery import PyQuery as pq async 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())
运行结果:网络
10
那么这里面的过程发生了什么?less
实际上,Pyppeteer 整个流程就完成了浏览器的开启、新建页面、页面加载等操做。另外 Pyppeteer 里面进行了异步操做,因此须要配合 async/await 关键词来实现。异步
首先, launch 方法会新建一个 Browser 对象,而后赋值给 browser,而后调用 newPage 方法至关于浏览器中新建了一个选项卡,同时新建了一个 Page 对象。然 后 Page 对象调用了 goto 方法就至关于在浏览器中输入了这个 URL,浏览器跳转到了对应的页面进行加载,加载完成以后再调用 content 方法,返回当前浏览器页面的源代码。而后进一步地,咱们用 pyquery 进行一样地解析,就能够获得 JavaScript 渲染的结果了。async
另外其余的一些方法如调用 asyncio 的 get_event_loop 等方法的相关操做则属于 Python 异步 async 相关的内容了,你们若是不熟悉能够了解下 Python 的 async/await 的相关知识。函数
好,经过上面的代码,咱们就能够完成 JavaScript 渲染页面的爬取了。
在这个过程当中,咱们没有配置 Chrome 浏览器,没有配置浏览器驱动,免去了一些繁琐的步骤,一样达到了 Selenium 的效果,还实现了异步抓取,爽歪歪!
接下来咱们再看看另一个例子,这个例子能够模拟网页截图,保存 PDF,另外还能够执行自定义的 JavaScript 得到特定的内容,代码以下
mport asyncio from pyppeteer import launch async 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 等各个参数实现截图。
可见其内容也是 JavaScript 渲染后的内容,另外这个方法还能够指定放缩大小 scale、页码范围 pageRanges、宽高 width 和 height、方向 landscape 等等参数,导出定制化的 pdf 用这个方法就十分方便。
最后咱们又调用了 evaluate 方法执行了一些 JavaScript,JavaScript 传入的是一个函数,使用 return 方法返回了网页的宽高、像素大小比率三个值,最后获得的是一个 JSON 格式的对象,内容以下:
{'width': 800, 'height': 600, 'deviceScaleFactor': 1}
OK,实例就先感觉到这里,还有太多太多的功能还没说起。
总之利用 Pyppeteer 咱们能够控制浏览器执行几乎全部动做,想要的操做和功能基本均可以实现,用它来自由地控制爬虫固然就不在话下了。
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 launch async def main(): await launch(headless=False) await asyncio.sleep(100) asyncio.get_event_loop().run_until_complete(main())
运行以后看不到任何控制台输出,可是这时候就会出现一个空白的 Chromium 界面了:
可是能够看到这就是一个光秃秃的浏览器而已,看一下相关信息
看到了,这就是 Chromium,上面还写了开发者内部版本,能够认为是开发版的 Chrome 浏览器就好。
另外咱们还能够开启调试模式,好比在写爬虫的时候会常常须要分析网页结构还有网络请求,因此开启调试工具仍是颇有必要的,咱们能够将 devtools 参数设置为 True, 这样每开启一个界面就会弹出一个调试窗口,很是方便,示例以下:
import asyncio from pyppeteer import launch async 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 就会被关闭了,界面始终会显现出来。在这里咱们新建了一个页面,打开了百度,界面运行效果以下:
这时候咱们能够看到上面的一条提示:"Chrome 正受到自动测试软件的控制",这个提示条有点烦,那咋关闭呢?这时候就须要用到 args 参数了,禁用操做以下:
browser = await launch(headless=False, args=['--disable-infobars'])
这里就再也不写完整代码了,就是在 launch 方法中,args 参数经过 list 形式传入便可,这里使用的是 --disable-infobars 的参数。
另外有人就说了,这里你只是把提示关闭了,有些网站仍是会检测到是 webdriver 吧,好比淘宝检测到是 webdriver 就会禁止登陆了,咱们能够试试:
import asyncio from pyppeteer import launch async 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())
运行时候进行一下登陆,而后就会弹出滑块,本身手动拖动一下,而后就报错了,界面以下:
爬虫的时候看到这界面是很让人崩溃的吧,并且这时候咱们还发现了页面的 bug,整个浏览器窗口比显示的内容窗口要大,这个是某些页面会出现的状况,让人看起来很不爽。
咱们能够先解决一下这个显示的 bug,须要设置下 window-size 还有 viewport,代码以下:
import asyncio from pyppeteer import launch width, height = 1366, 768 async 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())
这样整个界面就正常了:
OK,那刚才所说的 webdriver 检测问题怎样来解决呢?其实淘宝主要经过 window.navigator.webdriver 来对 webdriver 进行检测,因此咱们只须要使用 JavaScript 将它设置为 false 便可,代码以下:
import asyncio from pyppeteer import launch async 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())
这里没加输入用户名密码的代码,固然后面能够自行添加,下面打开以后,咱们点击输入用户名密码,而后这时候会出现一个滑动条,这里滑动的话,就能够经过了,如图所示:
OK,这样的话咱们就成功规避了 webdriver 的检测,使用鼠标拖动模拟就能够完成淘宝的登陆了。
还有另外一种方法能够进一步免去淘宝登陆的烦恼,那就是设置用户目录。平时咱们已经注意到,当咱们登陆淘宝以后,若是下次再次打开浏览器发现仍是登陆的状态。这是由于淘宝的一些关键 Cookies 已经保存到本地了,下次登陆的时候能够直接读取并保持登陆状态。
那么这些信息保存在哪里了呢?其实就是保存在用户目录下了,里面不只包含了浏览器的基本配置信息,还有一些 Cache、Cookies 等各类信息都在里面,若是咱们能在浏览器启动的时候读取这些信息,那么启动的时候就能够恢复一些历史记录甚至一些登陆状态信息了。
这也就解决了一个问题:不少朋友在每次启动 Selenium 或 Pyppeteer 的时候老是是一个全新的浏览器,那就是没有设置用户目录,若是设置了它,每次打开就再也不是一个全新的浏览器了,它能够恢复以前的历史记录,也能够恢复不少网站的登陆信息。
那么这个怎么来作呢?很简单,在启动的时候设置 userDataDir 就行了,示例以下:
import asyncio from pyppeteer import launch async 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 的文件夹,里面的结构是这样子的:
具体的介绍能够看官方的一些说明,如:https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md,这里面介绍了 userdatadir 的相关内容。
再次运行上面的代码,这时候能够发现如今就已是登陆状态了,不须要再次登陆了,这样就成功跳过了登陆的流程。固然可能时间过久了,Cookies 都过时了,那仍是须要登陆的。
好了,本想把 Pyppeteer 的用法详细介绍完的,结果只 launch 的方法就介绍这么多了,后面的内容放到其余文章来介绍了,其余的内容后续文章会陆续放出,谢谢。
小彩蛋:以上文章摘自即将完稿的《Python3网络爬虫开发实战(第二版)》,敬请期待,谢谢。