Pyppeteer⼊门(转载)
⼀.简介
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 由不同深度的蓝⾊构成。
Pyppeteer 就是依赖于 Chromium 这个浏览器来运⾏的。那么有了 Pyppeteer 之后,我们就可以免去那
些繁琐的环境配置等问题。如果第⼀次运⾏的时候,Chromium 浏览器没有安装,那么程序会帮我们⾃动安装和配置,就免去了繁琐的环境配置等⼯作。另外 Pyppeteer 是基于 Python 的新特性 async 实现的,所以它的⼀些执⾏也⽀持异步操作,效率相对于 Selenium 来说也提⾼了。
安装:
pip3 install pyppeteer
⼆.抓取js
⽽Pyppeteer 模拟jsvascript渲染,抓取信息。
import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq
async def main():
browser = await launch()
page = wPage()
('scrape/js/')
doc = pq(t())
print('Quotes:', doc('.quote').length)
await browser.close()
<_event_loop().run_until_complete(main())
运⾏结果:
10
那么这⾥⾯的过程发⽣了什么?
实际上,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 获得特定的内容,代码如下
mport asyncio
from pyppeteer import launchchrome浏览器是什么浏览器图标
async def main():
browser = await launch()
page = wPage()
('scrape/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()
<_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 我们可以控制浏览器执⾏⼏乎所有动作,想要的操作和功能基本都可以实现,⽤它来⾃由地控制爬⾍当然就不在话下了。
⼆.详细⽤法
开启浏览器
使⽤ Pyppeteer 的第⼀步便是启动浏览器,⾸先我们看下怎样启动⼀个浏览器,其实就相当于我们点击桌⾯上的浏览器图标⼀样,把它开起来。⽤ Pyppeteer 完成同样的操作,只需要调⽤ 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)
<_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 = wPage()
('www.baidu')
await asyncio.sleep(100)
<_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 = wPage()
('www.taobao')
await asyncio.sleep(100)
<_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 = wPage()
await page.setViewport({'width': width, 'height': height})
('www.taobao')
await asyncio.sleep(100)
<_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 = wPage()
('login.taobao/member/login.jhtml?redirectURL=www.taobao/')
await page.evaluate(
'''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
await asyncio.sleep(100)
<_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 = wPage()
('www.taobao')
await asyncio.sleep(100)
<_event_loop().run_until_complete(main())
好,这⾥就是加了⼀个 userDataDir 的属性,值为 userdata,即当前⽬录的 userdata ⽂件夹。我们可以⾸先运⾏⼀下,然后登录⼀次淘宝,这时候我们同时可以观察到在当前运⾏⽬录下⼜多了⼀个 userdata 的⽂件夹,⾥⾯的结构是这样⼦的:
再次运⾏上⾯的代码,这时候可以发现现在就已经是登录状态了,不需要再次登录了,这样就成功跳过
了登录的流程。当然可能时间太久了,Cookies 都过期了,那还是需要登录的。
好了,本想把 Pyppeteer 的⽤法详细介绍完的,结果只 launch 的⽅法就介绍这么多了,后⾯的内容放到其他⽂章来介绍了,其他的内容后续⽂章会陆续放出,谢谢。
⼩彩蛋:以上⽂章摘⾃即将完稿的《Python3⽹络爬⾍开发实战(第⼆版)》,敬请期待,谢谢。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论