1引言

曾经使用模拟浏览器操作(selenium + webdriver)来写爬虫,但是稍微有点反爬的网站都会对selenium和webdriver进行识别,网站只需要在前端js添加一下判断脚本,很容易就可以判断出是真人访问还是webdriver。虽然也可以通过中间代理的方式进行js注入屏蔽webdriver检测,但是webdriver对浏览器的模拟操作(输入、点击等等)都会留下webdriver的标记,同样会被识别出来,要绕过这种检测,只有重新编译webdriver,麻烦自不必说,难度不是一般大。

作为selenium+webdriver的优秀替代,pyppeteer就是一个很好的选择。

2 手动安装

通过pip使用豆瓣源加速安装pyppeteer:

pip install -i https://pypi.douban.com/simple pypeteer

按照官方手册,先来感受一下:

importasynciofrom pyppeteer importlaunch

asyncdefmain():

browser= await launch(headless=False)

page=await browser.newPage()

await page.goto('http://www.baidu.com/')

await asyncio.sleep(100)

await browser.close()

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

pyppeteer第一次运行时,会自动下载chromium浏览器,时间可能会有些长。不过,我第一次运行时,直接报错:

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)

尝试多种方法无果,无奈只能手动下载,但手动下载的方法网上资料也几乎没有,让我来做这个先行者吧。

上面代码运行虽然报错,但是控制台前两行却提供了很有用的信息:

[W:pyppeteer.chromium_downloader] start chromium download.

Download may take a few minutes.

可以看到,下载功能是由pyppeteer.chromium_downloader模块完成的,那么我们进入这个模块查看源码。

在这个模块源码中,我们可以看到downloadURLs、chromiumExecutable等变量,很明显指的就是下载链接和chromium的可执行文件路径。我们重点关注一下可执行文件路径

chromiumExecutable:

chromiumExecutable={'linux': DOWNLOADS_FOLDER / REVISION / 'chrome-linux' / 'chrome','mac': (DOWNLOADS_FOLDER / REVISION / 'chrome-mac' / 'Chromium.app' /

'Contents' / 'MacOS' / 'Chromium'),'win32': DOWNLOADS_FOLDER / REVISION / 'chrome-win32' / 'chrome.exe','win64': DOWNLOADS_FOLDER / REVISION / 'chrome-win32' / 'chrome.exe',

}

可见,无论在哪个平台下,chromiumExecutable都是由是4个部分组成,其中 DOWNLOADS_FOLDER 和 REVISION是定义好的变量:

DOWNLOADS_FOLDER = Path(__pyppeteer_home__) / 'local-chromium'

进一步查看可以发现:

__pyppeteer_home__ = os.environ.get('PYPPETEER_HOME', AppDirs('pyppeteer').user_data_dir)

REVISION= os.environ.get('PYPPETEER_CHROMIUM_REVISION', __chromium_revision__)

所以,DOWNLOADS_FOLDER 和 REVISION都是读取对应环境变量设置好的值,如果没有设置,就使用默认值。我们来输出一下,看看默认值:

importpyppeteer.chromium_downloaderprint('默认版本是:{}'.format(pyppeteer.__chromium_revision__))print('可执行文件默认路径:{}'.format(pyppeteer.chromium_downloader.chromiumExecutable.get('win64')))print('win64平台下载链接为:{}'.format(pyppeteer.chromium_downloader.downloadURLs.get('win64')))

输出结果如下:

默认版本是:575458

可执行文件默认路径:C:\Users\Administrator\AppData\Local\pyppeteer\pyppeteer\local-chromium\575458\chrome-win32\chrome.exe

win64平台下载链接为:https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/575458/chrome-win32.zip

在使用上面代码的时候,你可以将win64换成你的平台就好了,有了上面的下载链接,这个时候就可以先开始下载着chromium浏览器(有些慢),然后继续往下看。

对于版本,没什么好说的,是用默认的就好了。但是,对于chromium的可执行文件路径,默认是在C盘,对于有C盘洁癖的我,咋看咋不舒服,那就改了吧。从上面的分析中我们可以知道,C:\Users\Administrator\AppData\Local\pyppeteer\pyppeteer这一部分读取的是环境变量或者默认值,所以,我们可以通过配置环境变量改这一部分(或者也可以直接改源码,读取环境变量那一行,直接设为固定值),通过os.environ添加PYPPETEER_HOME这一变量值,例如我想把我的chromium放在D盘的Program Files文件夹下:

importos

os.environ['PYPPETEER_HOME'] = 'D:\Program Files'

importpyppeteer.chromium_downloaderprint('默认版本是:{}'.format(pyppeteer.__chromium_revision__))print('可执行文件默认路径:{}'.format(pyppeteer.chromium_downloader.chromiumExecutable.get('win64')))print('win64平台下载链接为:{}'.format(pyppeteer.chromium_downloader.downloadURLs.get('win64')))

输出如下:

默认版本是:575458

可执行文件默认路径:D:\Program Files\local-chromium\575458\chrome-win32\chrome.exe

win64平台下载链接为:https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/575458/chrome-win32.zip

特别提醒:上面设置环境变量的那一行,必须在导入pyppeteer这一行上面,否则设置无效。

上面这种方法你需要在每次使用pypeeteer之前通过这行代码设置一下,实在麻烦,所以,我还是更愿意直接在windows系统里面添加这个变量:

虽然我们把环境变量设置为D:\Program Files,但是层层文件夹之后,才到真正的可执行文件chrome.exe,下载好的压缩包解压后,所有文件都在名为chrome-win的文件夹中,所以,我们需要在D:\Program Files创建local-chromium\575458这两个文件夹(575458是上面的版本号,记得修改为你的版本号),然后将解压得到的chrome-win文件夹,重命名为chrome-win32,然后直接拷贝进去就好,整个安装过程就完成了。

再来试试最初(最上面)的代码,你会看到,已经可以成功运行。

我相信,大多数阅读这篇博文的读者都是用pyppeteer来开发爬虫(别说维护世界和平,我不信),那么接下来,重点来说说爬虫中要用到的一些主要操作。

3 主要操作

3.1 打开浏览器

打开浏览器是通过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.

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

3.2 调整窗口大小

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

importasynciofrom pyppeteer importlaunchdefscreen_size():"""使用tkinter获取屏幕大小"""

importtkinter

tk=tkinter.Tk()

width=tk.winfo_screenwidth()

height=tk.winfo_screenheight()

tk.quit()returnwidth, height

asyncdefmain():

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())

3.3 设置userAgent

常规操作,不多说,上代码:

importasynciofrom pyppeteer importlaunch

asyncdefmain():

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())

3.4 执行js脚本

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

importasynciofrom pyppeteer importlaunch

asyncdefmain():

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())

在上面代码中,通过page.evalute方法执行了两段js脚本,第一段脚本将webdriver的属性值设为false,第二段代码在此读取 webdriver属性值,输出为false。

3.5 模拟操作

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

主要来说说输入和点击:

importos

os.environ['PYPPETEER_HOME'] = 'D:\Program Files'

importasynciofrom pyppeteer importlaunch

asyncdefmain():

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())

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

3.6 某电商平台模拟登陆

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

importos

os.environ['PYPPETEER_HOME'] = 'D:\Program Files'

importasynciofrom pyppeteer importlaunchdefscreen_size():"""使用tkinter获取屏幕大小"""

importtkinter

tk=tkinter.Tk()

width=tk.winfo_screenwidth()

height=tk.winfo_screenheight()

tk.quit()returnwidth, height

asyncdefmain():

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())

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

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的检测,登录成功,诸位可以自己尝试一下。

4 总结

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

参考:

python 无头模式 绕过检测_网络爬虫之使用pyppeteer替代selenium完美绕过webdriver检测...相关推荐

  1. chrome webdriver_网络爬虫之使用pyppeteer替代selenium完美绕过webdriver检测 阅读目录

    1 引言 曾经使用模拟浏览器操作(selenium + webdriver)来写爬虫,但是稍微有点反爬的网站都会对selenium和webdriver进行识别,网站只需要在前端js添加一下判断脚本,很 ...

  2. python爬取地图地址_网络爬虫-python爬取高德地图地点

    python爬取你想要的数据,近期由于业务需求,用python爬取了高德地图一些地点的数据,爬出来数据大致情况如下: image 下面是基本流程: 2.安装网络爬取第三方库,主要是下面三个(pip i ...

  3. 【Python网络爬虫实战篇】使用selenium+requests爬取下载高清源视频:关于爬取m3u8文件链接解析为ts视频合并成mp4视频的分析实战

    这两天博主在摸鱼时,偶然间接触到了流媒体的概念,一时间来了兴致.再加上之前博主有着七.八年的视频制作经验,深知视频素材获取的不易.因此,打算利用自己所学的python网络爬虫的知识,通过编写代码实现获 ...

  4. [Python从零到壹] 四.网络爬虫之入门基础及正则表达式抓取博客案例

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  5. c#使用正则表达式获取TR中的多个TD_[Python从零到壹] 四.网络爬虫之入门基础及正则表达式抓取博客案例...

    首先祝大家中秋节和国庆节快乐,欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都 ...

  6. [Python从零到壹] 九.网络爬虫之Selenium基础技术万字详解(定位元素、常用方法、键盘鼠标操作)

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  7. [Python从零到壹] 五.网络爬虫之BeautifulSoup基础语法万字详解

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

  8. 模拟人体质检测_网络通信模块

    模拟人体质检测_网络通信模块 Introduction 模拟人体质测试是研一在实验室参与的项目,为期4+个月, 整个项目的架构(数据传输是双向的): 体质测试传感器-stm32-cc2530通信板-c ...

  9. python 无头模式 绕过检测_python爬虫反反爬虫有绝技,轻松绕开百度人机验证!...

    你可能已经了解到了无头浏览器的作用以及使用的方法,那么本篇文章就让我们一起用无头浏览器做点事情. 是的你没有看错,我们要"搞"的对象就是百度指数这个网站,不知道你平时是否会应用到这 ...

最新文章

  1. 启明云端方案分享| ESP32-S2 摄像头 WIFI方案应用于智能猫眼
  2. 第7章:Kubernetes存储
  3. es6 --- 手写一个promise
  4. Java面试常见算法
  5. 蔡高厅老师 - 高等数学阅读笔记 - 10 - 函数图像的描绘 - 微分三角形 -曲率(44 ~48)
  6. “中华姓氏文化归心工程”在京启动
  7. ECMAScript 和 JavaScript 的关系
  8. 图论知识总结(简易)
  9. mysql的service name_安装MYSQL出错:a windows service with the name MYSQL already...service解决...
  10. kettle使用_ETL工具(kettle)使用系列(二)
  11. html5 游戏 算法,JS/HTML5游戏常用算法之路径搜索算法 A*寻路算法完整实例
  12. 什么是内网、外网?内网、外网有啥区别?
  13. 花呗问答匹配(part3 各种模型运行)
  14. PCB画板与硬件调试+AD快捷键小技巧
  15. 推荐更好用的网络检测命令
  16. ABAP 客户主数据批量导入
  17. Kafka中auto.offset.reset配置项参数为earliest/或者latest的区别
  18. jeeplus框架代码生成器功能的使用
  19. (读后摘抄)《计算机程序设计语言的发展》_王汝传
  20. 协方差,协方差矩阵,矩阵特征值

热门文章

  1. 力扣-二叉树的层序遍历
  2. Flask框架——CSRF保护
  3. 漫谈CMS:ZOOMLA、NETCMS、风讯、动易异同
  4. 【opencv】【第一玩】坤坤的篮球
  5. 漫步最优化二十一——全局收敛
  6. python下载大文件mp4_python下载mp4 同步和异步下载支持断点续下
  7. 数据结构的堆栈与内存中堆栈的区别
  8. leetcode - 264. 丑数 II
  9. 【传统网络】与【SDN】的【DDos攻击与检测】
  10. Incorporating Lexical Priors into Topic Models(即交互式主题模型的应用)论文阅读