pyppeteer的使用(笔记,未完待续)


1、基本使用

--------------------------------------------

import asyncio
from pyppeteer import launch


async def main():
    browser = await launch(
        {'headless': False, 'userDataDir': r'C:\temp', 'autoClose': False,
         'args': ['--disable-infobars', '--no-sandbox']})
    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 page.screenshot(path='example.png')
    await page.pdf(path='example.pdf')
    await asyncio.sleep(100)

asyncio.get_event_loop().run_until_complete(main())

 2、主要操作

browser = await launch();//启动浏览器实例
page = await browser.newPage();//打开一个空白页
await page.goto('https://example.com');//在地址栏输入网址并等待加载
await page.screenshot({path: 'example.png'});//截个图
await page.setUserAgent(
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299')
await page.goto('https://www.baidu.com')
await page.type('#kw', keyword, {'delay': 100}) 获取输入框焦点并输入文字{'delay': input_time_random() - 50}
await page.click('#su')
await page.waitForNavigation({
      waitUntil: 'load'});//等待页面加载出来,等同于window.onload
await browser.close();//关掉浏览器
content = await page.content()
cookies = await page.cookies()
await page.evaluate(js1)   //在网页中执行js代码
await page.keyboard.press 模拟键盘按下某个按键,目前mac上组合键无效为已知bug
page.waitFor 页面等待,可以是时间、某个元素、某个函数
page.url 获得当前访问的url
await get_cookie(page)
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 获取指定元素数组,并将其作为第一个参数传递
#await page.waitFor(10000)
# print(await (await item.getProperty('textContent')).jsonValue()) 获取元素文本
# print(await (await item.getProperty('href')).jsonValue()) 获取元素链接
View Code
View Code

以下是具体说明,更新中......

(1)打开浏览器

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

打开浏览器是通过pyppeteer.launcher.launch(options: dict = None, **kwargs) 方法,运行该函数后,会得到一个pyppeteer.browser.Browser实例,也就是说浏览器对象实例。launch方法是必须使用的方法,所以,详细学学它的参数,你也直接阅读官方文档,因为我也是直接翻译的:

  • ignoreHTTPSErrors (bool): 是否HTTPS错误,某人情况下为False.
  • headless (bool): 是否以无头模式(无界面模式)执行,默认为True,为True时是不会弹出可视界面的,所以,上面代码运行时设置headless=False。注意,下面还有个devtools参数,表示是否出现打开调试窗口,如果devtools设置为True,headless就算设置为False也会弹出可视界面。
  • executablePath (str): Chromium或Chrome浏览器的可执行文件路径,如果设置,则使用设置的这个路径,不使用默认设置.
  • slowMo (int|float): 设置这个参数可以延迟pyppeteer的操作,单位是毫秒.
  • args (List[str]): 要传递给浏览器进程的一些其他参数.
  • ignoreDefaultArgs (bool): 如果有些参数你不想使用默认值,那么,通过这个参数设置,不过,孩子,最好别用,有危险(电脑会爆炸).
  • handleSIGINT (bool): 是否响应 SIGINT 信号,是否允许通过快捷键Ctrl+C来终止浏览器进程,默认值为True,也就是允许.
  • handleSIGTERM (bool): 是否响应 SIGTERM 信号,也就是说kill命令关闭浏览器,,默认值为True,也就是允许.
  • handleSIGHUP (bool): 是否响应 SIGHUP 信号,即挂起信号,默认值为True,也就是允许.
  • dumpio (bool): 是要将浏览器进程的输出传递给process.stdout 和 process.stderr 对象,默认为False不传递。
  • userDataDir (str): 用户数据文件目录.
  • env (dict): 以字典的形式传递给浏览器环境变量.
  • devtools (bool): 是否打开调试窗口,上面介绍headless参数是说过,默认值为False不打开.
  • logLevel (int|str): 日志级别,默认和 root logger 对象的级别相同.
  • autoClose (bool): 当所有操作都执行完后,是否自动关闭浏览器,默认True,自动关闭.
  • loop (asyncio.AbstractEventLoop): 时间循环。
  • appMode (bool): Deprecated.

打开浏览器操作简单,看参数就行,不多介绍。

(2)调整窗口大小

如果你运行了上面的代码,你会发现,打开的页面只在窗口左上角一小块显示,看着很别扭,这是因为pyppeteer默认窗口大小是800*600,所以,调整一下吧。调整窗口大小通过方法实现,看下面代码,最大化窗口:

import asyncio
from pyppeteer import launch
 
def screen_size():
    """使用tkinter获取屏幕大小"""
    import tkinter
    tk = tkinter.Tk()
    width = tk.winfo_screenwidth()
    height = tk.winfo_screenheight()
    tk.quit()
    return width, height
 
async def main():
    browser = await launch(headless=False)
    page = await browser.newPage()
    width, height = screen_size()
    await page.setViewport({ # 最大化窗口
        "width": width,
        "height": height
    })
await page.goto('http://www.baidu.com/')
await asyncio.sleep(100)
await browser.close()
 
asyncio.get_event_loop().run_until_complete(main())
View Code

(3)设置userAgent,设置cookie,保存cookie

import asyncio
from pyppeteer import launch
 
async def main():
browser = await launch(headless=False)
page = await browser.newPage()
# 设置请求头userAgent
await page.setUserAgent('Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Mobile Safari/537.36')
await page.goto('http://www.baidu.com/')
await asyncio.sleep(100)
await browser.close()
 
asyncio.get_event_loop().run_until_complete(main())
View Code
cookies = open(r'C:\Users\ho\Downloads\EditThisCookie\cookies.txt').read()
for item in json.loads(cookies):
    await page.setCookie(item)
await page.goto('http://www.xxx.com/')
View Code
# 保存cookie
async def save_cookie(cookie):
    with open("cookie.json", 'w+', encoding="utf-8") as file:
        json.dump(cookie, file, ensure_ascii=False)
View Code

(4)执行js脚本

有时候,为了达成某些目的(例如屏蔽网站原有js),我们不可避免得需要执行一些js脚本。执行js脚本通过evaluate方法。如下所示,我们通过js来修改window.navigator.webdriver属性的值,由此绕过网站对webdriver的检测:

import asyncio
from pyppeteer import launch
 
async def main():
js1 = '''() =>{
 
    Object.defineProperties(navigator,{
    webdriver:{
        get: () => false
        }
    })
}'''
 
js2 = '''() => {
    alert (
        window.navigator.webdriver
    )
}'''
browser = await launch({'headless':False, 'args':['--no-sandbox'],})
 
page = await browser.newPage()
await page.goto('https://h5.ele.me/login/')
await page.evaluate(js1)
await page.evaluate(js2)
 
asyncio.get_event_loop().run_until_complete(main())
View Code

在上面代码中,通过page.evalute方法执行了两段js脚本,第一段脚本将webdriver的属性值设为false,第二段代码在此读取 webdriver属性值,输出为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())
View Code

 (5)模拟操作

pyppeteer提供了Keyboard和Mouse两个类来实现模拟操作,前者是用来实现键盘模拟,后者实现鼠标模拟(还有其他触屏之类的就不说了)。

主要来说说输入和点击:

import os
os.environ['PYPPETEER_HOME'] = 'D:\Program Files'
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://h5.ele.me/login/')
await page.type('form section input', '12345678999') # 模拟键盘输入手机号
await page.click('form section button') # 模拟鼠标点击获取验证码
await asyncio.sleep(200)
await browser.close()
 
asyncio.get_event_loop().run_until_complete(main())
View Code

上面的模拟操作中,无论是模拟键盘输入还是鼠标点击定位都是通过css选择器,似乎pyppeteer的type和click直接模拟操作定位都只能通过css选择器(或者是我在官方文档中没找到方法),当然,要间接通过xpath先定位,然后再模拟操作也是可以的。下一小节中模拟登陆外卖平台就是用这种方法,不过,这种方法要麻烦一些,不推荐。

await page.hover('#alreday-login > a') #移动到元素上

              (参考https://www.jianshu.com/p/ccd4ae892440,模拟人工滑动操作)

(6)dialog点击弹窗

from pyppeteer.dialog import Dialog

async def handle_dialog(page,dialog: Dialog):
    print(dialog.message)#打印出弹框的信息
    print(dialog.type)#打印出弹框的类型,是alert、confirm、prompt哪种
    # print(dialog.defaultValue())#打印出默认的值只有prompt弹框才有
    await page.waitFor(2000)#特意加两秒等可以看到弹框出现后取消
    await dialog.dismiss()
   # await dialog.accept(‘000’) #可以给弹窗设置默认值

async def main():
    browser = await launch({'headless': False, 'userDataDir': r'C:\temp', 'autoClose': False, 'args': ['--disable-infobars', '--no-sandbox']})
    page = await browser.newPage()

    page.on('dialog', lambda dialog: asyncio.ensure_future(handle_dialog(page, dialog)))
    await page.goto('https://XXX.com/')
View Code

(7)某电商平台模拟登录

用selenium + chrome 实现了模拟登陆这个电商平台,但是实在是有些麻烦,绕过对webdriver的检测不难,但是,通过webdriver对浏览器的每一步操作都会留下特殊的痕迹,会被平台识别,这个必须通过重新编译chrome的webdriver才能实现,麻烦得让人想哭。不说了,都是泪,下面直接上用pyppeteer实现的代码:

import os
os.environ['PYPPETEER_HOME'] = 'D:\Program Files'
import asyncio
from pyppeteer import launch
 
def screen_size():
    """使用tkinter获取屏幕大小"""
    import tkinter
    tk = tkinter.Tk()
    width = tk.winfo_screenwidth()
    height = tk.winfo_screenheight()
    tk.quit()
    return width, height
 
 
async def main():
    js1 = '''() =>{
 
        Object.defineProperties(navigator,{
        webdriver:{
            get: () => false
            }
        })
    }'''
 
    js2 = '''() => {
        alert (
            window.navigator.webdriver
        )
    }'''
    browser = await launch({'headless':False, 'args':['--no-sandbox'],})
 
    page = await browser.newPage()
    width, height = screen_size()
    await page.setViewport({ # 最大化窗口
        "width": width,
        "height": height
    })
    await page.goto('https://h5.ele.me/login/')
    await page.evaluate(js1)
    await page.evaluate(js2)
    input_sjh = await page.xpath('//form/section[1]/input[1]')
    click_yzm = await page.xpath('//form/section[1]/button[1]')
    input_yzm = await page.xpath('//form/section[2]/input[1]')
    but = await page.xpath('//form/section[2]/input[1]')
    print(input_sjh)
    await input_sjh[0].type('*****手机号********')
    await click_yzm[0].click()
    ya = input('请输入验证码:')
    await input_yzm[0].type(str(ya))
    await but[0].click()
    await asyncio.sleep(3)
    await page.goto('https://www.ele.me/home/')
    await asyncio.sleep(100)
    await browser.close()
 
asyncio.get_event_loop().run_until_complete(main())
View Code

登录时,由于等待时间过长(我猜的)导致出现以下错误:

pyppeteer.errors.NetworkError: Protocol Error (Runtime.callFunctionOn): Session closed. Most likely the page has been closed.

在github上找到了解决方法,似乎只能改源码,找到pyppeteer包下的connection.py模块,在其43行和44行改为下面这样:

self._ws = websockets.client.connect(
# self._url, max_size=None, loop=self._loop)
self._url, max_size=None, loop=self._loop, ping_interval=None, ping_timeout=None)

再次运行就没问题了。可以成功绕过官方对webdriver的检测,登录成功,诸位可以自己尝试一下。

3、总结

当使用selenium+webdriver写爬虫被检测到时,pyppeteer是你得不二选择,几乎所有能在人工操作浏览器进行的操作通过pyppeteer都能实现,且能完美避开官方对webdriver的检测。pyppeteer涉及的使用方法还很多,本文只介绍了常用方法的很小很小一部分,需要一说的是,pyppeteer的中文资料真的很少,多看看官方文档吧。

待续:https://blog.csdn.net/u012206617/article/details/108323037


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM