Pyppeteer的使用
在 7.1 节,我们学习了 Selenium 的基本用法,其功能的确非常强大,但很多时候会发现它也有一些不太方便的地方,例如配置环境时,需要先安装好相关测览器,例如 Chrome、Firefox 等,然后到官方网站下载对应的驱动。最重要的是,需要安装对应的 Python Selenium 库,而且得看版本是否对应,这确实不太方便。另外,如果要大规模部署 Selenium,一些环境配置问题也是很头疼的。
本节,我们介绍 Selenium 的另一个替代品: Pyppeteer。
|
是 Pyppeteer,不是 Puppeteer,Puppeteer 是基于 Nodejs 的,Pyppeteer 是基于 Python 的。 |
Pyppeteer 介绍
Puppeteer 是 Google 基于 Node.js 开发的一个工具,有了它,我们可以利用 JavaScript 控制 Chrome 浏览器的一些操作。当然,Puppeteer 也可以应用于网络爬虫上,其 API 极其完善,功能非常强大。
Pyppeteer 又是什么呢?它其实是 Puppeteer 的 Python 版实现,但不是 Google 开发的,是由一位来自日本的工程师依据 Puppeteer 的一些功能开发出来的非官方版本。
Pyppeteer 的背后实际上有一个类似于 Chrome 的浏览器一一Chromium,它执行一些动作,从而进行网页煊染。首先,介绍一下 Chromium 浏览器和 Chrome 浏览器的渊源。
Chromium 是 Google 为了研发 Chrome 启动的项目,是完全开源的。二者基于相同的源代码而构建,Chrome 的所有新功能都会先在 Chromium 上实现,待验证稳定后才移植到 Chrome 上,因此 Chromium 的版本更新频率更高,同时包含很多新功能。但作为一款独立的浏览器,Chromium 的用户群体要小众得多。两款浏览器 “同根同源”,有着同样的 logo,只是配色不同,Chromium logo 的颜色是不同深度的蓝色,Chrome logo 的颜色是蓝色、红色、黄色和绿色这 4 种颜色,如图7-19所示
图7-19 Chromium浏览器和Chrome 浏览器的logo
总的来说,两款浏览器的内核一样,实现方式也一样,可以看作开发版和正式版,功能上没有太大区别。
Pyppeteer 就是依赖 Chromium 浏览器运行的。如果第一次运行 Pyppeteer 的时候,没有安装 Chromium 浏览器,程序就会帮我们自动安装和配置好,免去了烦琐的环境配置等工作。另外,Pyppeteer 是基于 Python 的新特性 async 实现的,所以它的一些操作执行也支持异步方式,和 Selenium 相比效率也提高了。
下面我们就一起了解一下 Pyppeteer 的相关用法。
安装
首先要解决的便是安装问题。由于 Pyppeteer 采用了 Python 的 async 机制,所以要求 Python 版本为 3.5 及以上。
使用 pip3 工具安装 Pyppeteer 即可:
pip3 install pyppeteer
具体的安装过程可以参考 https://setup.scrape.center/pyppetter 。
安装完成之后,便可以开始接下来的学习。
快速上手
我们测试一下基本的页面染操作,这里用网址 https://spa2.scrape.center/ 做测试,如图 7-20 所示。
图7-20 测试网站
这个网站在 7.1 节已经分析过了,整个页面是用 JavaScript 道染出来的,一些 Ajax 接口还带有加密参数,所以没法直接使用 requests 爬取看到的数据,同时也不太好直接模拟 Ajax 来获取数据。
在 7.1 节介绍的使用 Selenium 爬取这个网站中数据的方式,原理就是模拟浏览器的操作,直接用浏览器把页面煊染出来,然后直接获取煊染后的结果。基于同样的原理,Pyppeteer 也可以做到。
下面我们用 Pyppeteer 试试,代码可以写为如下这样:
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('https://spa2.scrape.center/')
await page.waitForSelector('.item .name')
doc = pq(await page.content())
names = [item.text() for item in doc('.item .name').items()]
print('Names:', names)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
运行结果如下:
Names: ['霸王别姬- Farewell My Concubine', '这个杀手不太冷- Léon', '肖申克的救赎- The Shawshank Redemption', '泰坦尼克号- Titanic', '罗马假日- Roman Holiday', '滚烫的爱恋- Flirting Scholar', '乱世佳人- Gone with the Wind', '喜剧之王- The King of Comedy', '楚门的世界- The Truman Show', '狮子王- The Lion King']
先粗略看下一下代码,大体意思是访问了测试网站,然后等待 .item.name 节点加载出来,随后通过 pyquery 从页面源码中提取电影的名称并输出,最后关闭 Pyppeteer。运行结果和之前用 Selenium 实现的结果一样,我们成功模拟了页面的加载行为,然后提取了页面上所有电影的名称。
那么,其中具体发生了什么?我们来逐行解析一下。
-
调用
launch方法新建了一个 Browser 对象,并赋值给browser变量。这一步相当于启动了浏览器。 -
调用
newPage方法,新建了一个 Page 对象,并赋值给page变量。相当于在浏览器中新建了一个选项卡,这时候虽然启动了一个新的选项卡,但是还未访问任何页面,浏览器窗然是空白的。 -
调用
page的goto方法,相当于在浏览器中输入goto方法的参数中的 URL,之后浏览器加载对应的页面。 -
调用
page的waitForSelector方法,传入选择器,页面就会等待选择器对应的节点信息加载出来,加载出来后,就立即返回,否则持续等待直到超时。如果顺利的话,页面会成功加载出来。 -
页面加载完成后,调用
content方法,可以获取当前浏览器页面的源代码,这就是 JavaScript 渲染后的结果。 -
进一步,用
pyquery解析并提取页面上的电影名称,就得到了最终结果了。
另外,其他一些方法(例如调用 asyncio 的 get_event_loop 等方法)的相关操作属于 Python 异步编程 async 相关的内容,大家如果不熟悉,可以查看第 6 章的知识。
通过上面的代码,我们同样可以爬取 JavaScript 染的页面。怎么样?相比 Selenium,这个代码是不是更简洁易读,环境配置也更加方便。在这个过程中,我们没有配置 Chrome 浏览器,没有配置浏览器驱动,免去了一些烦琐的步骤,却达到了和 Selenium 一样的效果,还实现了异步爬取。
接下来,我们看另外一个例子:
import asyncio
from pyppeteer import launch
width, height = 1366, 768
async def main():
browser = await launch()
page = await browser.newPage()
await page.setViewport({'width': width, 'height': height})
await page.goto('https://spa2.scrape.center/')
await page.waitForSelector('.item .name')
await asyncio.sleep(2)
await page.screenshot(path='example.png')
dimensions = await page.evaluate('''() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio,
}
}''')
print(dimensions)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
这里我们用到了几个新的方法,设置了页面窗口的大小、保存了页面截图、执行 JavaScript 语句并返回了对应的数据。其中,在 screenshot 方法里,通过 path. 参数用于传入页面截图的保存路径,另外还可以指定截图的保存格式 type、清晰度 quality、是否全屏 fullPage 和裁切 clip 等参数。页面截图的样例如图 7-21 所示。
图7-21 截图样例
可以看到,返回结果是 JavaScript 煊染后的页面,和我们在浏览器中看到的结果一模一样。
我们还调用 evaluate 方法执行了一些 JavaScript 语句。这里给 JavaScript 传入了一个函数,使用 return 方法返回了页面的宽、高、像素大小比率这三个值,最后得到的是一个 JSON 格式的对象,内容如下:
{'width': 1366, 'height': 768, 'deviceScaleFactor': 1 }
实例就先感受到这里,有太多功能还没提及。
总之,利用 Pyppeteer 可以控制浏览器执行几乎所有想实现的操作和功能,利用它自由地控制爬虫当然也不在话下。
了解了基本的实例后,再来梳理 Pyppeteer 的一些基本和常用操作。Pyppeteer 几乎所有的功能都能在其官方文档的 API Reference 里找到,文档链接是 https://pyppeteer.github.io/pyppeteer/reference.html ,使用哪个方法就来这里查询即可,参数不必死记硬背,即用即查就好。
launch方法
使用 Pyppeteer 的第一步便是启动浏览器。启动浏览器相当于点击桌面上的浏览器图标,用 Pyppeteer 实现时,调用 launch 方法即可。
先来看下 launch 方法的 API,链接为: https://pyppeteer.github.io/pyppeteer/reference.html#launcher ,该方法的定义如下:
pyppeteer.launcher.launch(options:dict=None,**kwargs)→pyppeteer.browser.Browser
可以看到,launch 方法处于 launcher 模块中,在声明中没有特别指定参数,返回值是 browser 模块中的 Browser 对象。另外,观察源码可以发现,这是一个 async 修饰的方法,所以在调用的时候需要使用 await。
接下来,看看 launch 方法的参数。
-
ignoreHTTPSErrors(bool):是否忽略 HTTPS 的错误,默认是 False。
-
headless(bool):是否启用无头模式,即无界面模式。如果 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。下面先尝试一下关闭无头模式:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=False)
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
运行这段代码之后在控制台看不到任何输出,但是会出现一个空白的 Chromium 界面,如图 7-22 所示。
图7-22 空白的 Chromium 界面
这就是一个光秃秃的浏览器而已,看一下相关信息,如图 7-23 所示。
图7-23 相关信息
上面有 Chromium 浏览器的 logo,开发者内部版本号,将其当作开发版的 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,无头模式就会关闭,界面始终会显示出来。这里我们新建了一个页面,打开了百度,界面运行效果如图 7-24 所示。
图7-24 界面运行效果
禁用提示条
可以看到图 7-24 的上面有一条提示 “Chrome 正在受到自动测试软件的控制”,这个提示条有点烦人,怎么关闭呢?这时候就需要用到 args 参数了,禁用操作如下:
browser = await launch(headless=False, args=['--disable-infobars'])
这里不再写完整代码了,就是给 launch 方法中的 args 参数传入 list 形式的数据,这里使用的是 --disable-infobars。
防止检测
有人会说,刚刚只是把提示关闭了,有些网站还是能检测到 Webdriver 属性。不妨拿之前的案例网站 https://antispiderl.scrape.center/ 验证一下:
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://antispiderl.scrape.center/')
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
果然被检测到了,如图 7-25 所示。
图7-25 检测结果
这说明 Pyppeteer 开启 Chromium 后,照样能被检测到 Webdriver 属性的存在。
那么如何规避此问题呢?Pyppeteer 的 Page 对象有一个叫作 evaluateOnNewDocument 的方法,意思是在每次加载网页的时候执行某条语句,这里可以利用它执行隐藏 Webdriver 属性的命令,代码改写如下:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=False, args=['--disable-infobars'])
page = await browser.newPage()
await page.evaluateOnNewDocument('Object.defineProperty(navigator, "webdriver", {get: () => undefined})')
await page.goto('https://antispiderl.scrape.center/')
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
可以看到,整个页面成功加载出来了,绕过了对 Webdriver 属性的检测,如图 7-26 所示。
图7-26 加载成功的页面
页面大小设置
在图 7-28 中,还可以发现页面的显示 bug,整个浏览器的窗口要比显示内容的窗口大,这个情况并非每个页面都会出现。
这时可以设置窗口大小,调用 Page 对象的 setViewport 方法即可,代码如下:
import asyncio
from pyppeteer import launch
width, height = 1366, 768
async def main():
browser = await launch(headless=False, args=['--disable-infobars', f'--window-size={width},{height}'])
page = await browser.newPage()
await page.setViewport({'width': width, 'height': height})
await page.evaluateOnNewDocument('Object.defineProperty(navigator, "webdriver", {get: () => undefined})')
await page.goto('https://antispider1.scrape.center/')
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
这里我们同时设置了浏览器窗口的宽高以及显示区域的宽高,让二者一致,最后发现页面显示正常了,如图 7-27 所示。
图7-27 正常显示的页面
用户数据持久化
刚才我们看到,每次打开 Pyppeteer 的时候,都是一个新的空白的浏览器。如果网页需要登录,那么即使这次登录成功,下一次打开时也还是空白的,又得登录一次,这的确是一个问题。
以淘宝为例,很多时候在关闭浏览器并再次打开时,它依然处于登录状态。这是因为淘宝的一些关键 Cookie 已经保存到本地了,再次登录的时候可以直接读取并保持登录状态。
那么,这些信息保存在哪里呢?答案是用户目录下。其中不仅包含浏览器的基本配置信息,还包含一些 Cache、Cookie 等信息,如果我们能在浏览器启动的时候读取这些信息,就可以恢复一些历史记录甚至登录状态信息了。
这也解决了一个问题:很多朋友每次在启动 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 文件夹,其结构如图 7-28 所示。
图7-28 userdata 文件夹的结构
关于这个文件夹的具体介绍可以看官方的一些说明,例如 https:/chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md
再次运行上面的代码,可以发现淘宝已经处于登录状态,不需要再次登录了,这样就成功跳过了登录的流程。当然,也可能由于时间太久,Cookie 都过期了,还是需要登录。
以上便是 launch 方法及其对应参数的配置。
Browser
我们了解了 launch 方法,它的返回值是一个 Browser 对象,即浏览器对象,我们通常会将其赋值给 browser 变量(其实就是 Browser 类的一个实例)。
下面来看看 Browser 类的定义:
class pyppeteer.browser.Browser(connection: pyppeteer.connection.Connection, contextIds: list[str], ignoreHTTPSErrors: bool, setDefaultViewport: bool, process: Optional[subprocess.Popen] = None, closeCallback: Callable[[None], Awaitable[None]] = None, **kwargs)
从这里可以看到,Browser 类的构造方法有很多参数,大多数情况下直接使用 launch 方法或 connect 方法构建浏览器对象即可。
browser 作为 Browser 类的实例,自然有很多用于操作浏览器的方法,下面我们选取一些比较有用的方法介绍一下。
开启无痕模式
我们知道 Chrome 浏览器有无痕模式,其好处就是环境比较干净,不与其他浏览器示例共享 Cache、Cookie 等内容,可以通过 createIncognitoBrowserContext 方法开启无痕模式。示例如下:
import asyncio
from pyppeteer import launch
width, height = 1200, 768
async def main():
browser = await launch(headless=False,
args=['--disable-infobars', f'--window-size={width},{height}'])
context = await browser.createIncognitoBrowserContext()
page = await context.newPage()
await page.setViewport({'width': width, 'height': height})
await page.goto('https://www.baidu.com')
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
这里调用的是 browser 的 createIncognitoBrowserContext 方法,返回值是一个 context 对象。利用 context 对象可以新建选项卡。
运行这段代码后,我们发现浏览器进入了无痕模式,界面如图 7-29 所示。
图7-29 开启浏览器的无痕模式
关闭
怎样关闭浏览器就不多说了,使用的是 close 方法。很多时候会因为忘记关闭浏览器而产生额外开销,因此一定要记得在浏览器使用完毕之后调用 close 方法。示例如下:
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('https://spa2.scrape.center/')
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
Page
Page 即页面,对应一个网页、一个选项卡。
在前面的很多示例中,其实已经出现了 Page 对象的身影,这里再详细看一下它的一些常见用法。
选择器
Page 对象内置了很多用于选取节点的选择器方法,例如 J 方法,给它传入一个选择器,就能返回匹配到的第一个节点,等价于 querySelector 方法;又如 JJ 方法,给它传入选择器,会返回符合选择器的所有节点组成的列表,等价于 querySelectorAll 方法。
下面我们分别调用了 J 方法、querySelector 方法、JJ 方法和 querySelectorAll 方法,代码如下:
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('https://spa2.scrape.center/')
await page.waitForSelector('.item .name')
j_result1 = await page.J('.item .name')
j_result2 = await page.querySelector('.item .name')
jj_result1 = await page.JJ('.item .name')
jj_result2 = await page.querySelectorAll('.item .name')
print('J Result1:', j_result1)
print('J Result2:', j_result2)
print('JJ Result1:', jj_result1)
print('JJ Result2:', jj_result2)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
运行结果如下:
可以看到,J 方法和 querySelector 方法的返回结果都是和传入的选择器相匹配的单个节点,返回值为 ElementHandle 对象。JJ 方法和 querySelectorAll 方法则都是返回了和选择器相匹配的节点组成的列表,列表中的内容是 ElementHandle 对象。
选项卡操作
前面我们已经多次演练了新建选项卡的操作,使用的是 newPage 方法。那么新建选项卡之后,怎样获取和切换呢?先调用 pages 方法获取所有打开的页面,然后选择一个页面调用其 bringToFront 方法即可。下面来看一个例子:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=False)
page = await browser.newPage()
await page.goto('https://www.baidu.com')
page = await browser.newPage()
await page.goto('https://www.bing.com')
pages = await browser.pages()
print('Pages:', pages)
page1 = pages[1]
await page1.bringToFront()
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
这里启动了 Pyppeteer,然后调用 newPage 方法新建了两个选项卡,并访问了两个网站。
页面操作
一定要有对应的方法来控制一个页面的加载、前进、后退、关闭和保存等行为,示例如下:
import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq
async def main():
browser = await launch(headless=False)
page = await browser.newPage()
await page.goto('https://dynamic1.scrape.cuiqingcai.com/')
await page.goto('https://spa2.scrape.center/')
# 后退
await page.goBack()
# 前进
await page.goForward()
# 刷新
await page.reload()
# 保存 PDF
await page.pdf()
# 截图
await page.screenshot()
# 设置页面 HTML
await page.setContent('<h2>Hello World</h2>')
# 设置 User-Agent
await page.setUserAgent('Python')
# 设置 Headers
await page.setExtraHTTPHeaders(headers={})
# 关闭
await page.close()
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
这里我们介绍了一些常用的控制页面的方法,除此以外,还设置了 User-Agent、Headers。
点击
Pyppeteer 同样可以模拟点击,调用其 click 方法即可。以 https://spa2.scrape.center/ 为例,等其所有节点都加载出来后,模拟点击:
import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq
async def main():
browser = await launch(headless=False)
page = await browser.newPage()
await page.goto('https://spa2.scrape.center/')
await page.waitForSelector('.item .name')
await page.click('.item .name', options={
'button': 'right',
'clickCount': 1, # 1 或 2
'delay': 3000, # 毫秒
})
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
这里 click 方法中的第一个参数就是选择器,即在哪里操作。第二个参数是几项配置,具体有如下内容。
-
button: 鼠标按钮,取值有 left、middle、right。
-
clickCount: 点击次数,取值有 1 和 2,表示单击和双击。
-
delay: 延迟点击。
输入文本
Pyppeteer 也可以输入文本,使用 type 方法即可,示例如下:
import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq
async def main():
browser = await launch(headless=False)
page = await browser.newPage()
await page.goto('https://www.taobao.com')
# 后退
await page.type('#q', 'iPad')
# 关闭
await asyncio.sleep(10)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
这里我们打开淘宝,给 type 方法的第一个参数传入选择器,第二个参数传入要输入的文本内容,Pyppeteer 就可以帮我们完成输入了。
获取信息
Page 对象需要调用 content 方法获取源码,Cookies 对象调用 cookies 方法获取,示例如下:
import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq
async def main():
browser = await launch(headless=False)
page = await browser.newPage()
await page.goto('https://spa2.scrape.center/')
print('HTML:', await page.content())
print('Cookies:', await page.cookies())
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
执行
Pyppeteer 可以支持执行 JavaScript 语句,使用 evaluate 方法即可。看之前的例子:
import asyncio
from pyppeteer import launch
width, height = 1366, 768
async def main():
browser = await launch()
page = await browser.newPage()
await page.setViewport({'width': width, 'height': height})
await page.goto('https://spa2.scrape.center/')
await page.waitForSelector('.item .name')
await asyncio.sleep(2)
await page.screenshot(path='example.png')
dimensions = await page.evaluate('''() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio,
}
}''')
print(dimensions)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
这里我们调用 evaluate 方法执行了 JavaScript 语句,并获取了对应的结果。另外,Pyppeteer 还有 exposeFunction、evaluateOnNewDocument、evaluateHandle 方法,可以了解一下。
延时等待
在本节最开头的地方,我们演示了 waitForSelector 的用法,它可以让页面等待某些符合条件的节点加载出来再返回结果。这里我们给 waitForSelector 传入一个 CSS 选择器,如果找到符合条件的节点,就立马返回结果,否则等待直到超时。
除了 waitForSelector 外,还有很多其他的等待方法,具体如下。
-
waitForFunction:等待某个 JavaScript 方法执行完毕或返回结果。
-
waitForNavigation:等待页面跳转,如果没加载出来,就报错。
-
waitForRequest:等待某个特定的请求发出。
-
waitForResponse:等待某个特定请求对应的响应。
-
waitFor:通用的等待方法。
-
waitForXPath:等待符合 XPath 的节点加载出来。
通过各种等待方法,就可以控制页面的加载情况了。
总结
Pyppeteer 还有其他很多功能,例如键盘事件、鼠标事件、对话框事件等,这里就不再一一赘述了。
更多内容可以参考官方文档 https://miyakogi.github.io/pyppeteer/reference.html 的案例说明。
本节我们凭借一些小案例介绍了 Pyppeteer 的基本用法,7.6 节将使用 Pyppeteer 完成一个爬取实例。