验证码问题
该网站对频率检测十分敏感,很快就会出现"按住"验证码,模拟按住倒不难,但是按完以后有时并不能解决验证…
但是意外发现,在“无痕模式”下进行重新登录后,就可以解决验证…但是通过requests进行模拟登录后cookies还是无效。
解决方案:
既然模拟不行,那么就手动,因此想用selenium来模拟点击登陆。但是因为俄罗斯的问题,无法登陆。
于是只好更换selenium为pyppeteer。之前就听说了pyppeteer是可以代替selenium的模拟神器,相比Selenium 不太方便的地方:比如速度太慢、对版本配置要求严苛,最麻烦是经常要更新对应的驱动,还有些网页是可以检测到是否使用了Selenium ,pyppeteer都有提升,这次也借机会学习一下。
安装:
pip install pyppeteer
, 需要Python3.6以上(因为用了async)
pyppeteer
最简单demo:打开网页,并截图
1 | import asyncio |
执行JS,相当于requests-html中的render
1 | dimensions = await page.evaluate('''() => { |
元素选择器:
Page.querySelector()
/Page.querySelectorAll()
/Page.xpath()
缩写为Page.J()
, Page.JJ()
, and Page.Jx()
.
xpath进阶:
- starts-with 顾名思义,匹配一个属性开始位置的关键字
- contains 匹配一个属性值中包含的字符串
text()
匹配的是显示文本信息://a[contains(text(), "百度搜索")]
元素操作:点击、输入
pyppeteer.input.Mouse
click(x: float*, y: float, options: dict = None*, ***kwargs)
button
(str):left
,right
, ormiddle
, defaults toleft
:- 右键点击:
await page.click('a#cccccc',{"button": "right"})
- 右键点击:
clickCount
(int): defaults to 1.delay
(int|float): Time to wait betweenmousedown
andmouseup
in milliseconds. Defaults to 0.
down
(options: dict = None, *kwargs) `button
(str):left
,right
, ormiddle
, defaults toleft
.clickCount
(int): defaults to 1.
move(x: float, y: float, options: dict = None, **kwargs*)
p(options: dict = None, **kwargs*)
1 | # 使用xpath |
launch
-
headless
:无界面模式 -
devtools
:启用开发者助手 -
userDataDir='./userdata'
: 记录些用户信息,比如cookies -
dumpio=True
防止浏览器开多个页面而卡死 -
executablePath
: 指定自定义的chrome路径 -
参数args设置,可以类比selenium
-
--start-maximized
: 页面全屏 -
--window-size={width},{height}
:设置窗口大小,区别于页面的page.setViewport({'width': 1366, 'height': 768})
-
--disable-infobars
:不显示信息栏 比如 chrome正在受到自动测试软件的控制 -
--incognito
:百度搜到的chrome.exe无痕命令(selenium是如此,pyppeteer不推荐) -
--user-agent
: 比如'--user-agent=Mozilla/5.0'
-
--proxy-server={proxy}
: 浏览器代理 配合某些中间人代理使用 -
--no-sandbox
, # 取消沙盒模式 沙盒模式下权限太小 -
'--log-level=3'
-
'--load-extension={}'.format(chrome_extension)
、'--disable-extensions-except={}'.format(chrome_extension)
# 加载插件1
2
3
4
5chrome_extension_path = "插件所在目录"
args = [
"--load-extension={}".format(chrome_extension_path),
"--disable-extensions-except={}".format(chrome_extension_path),
]
-
-
more: launch参数说明
执行JS:
-
page.evaluate('window.scrollBy(100, document.body.scrollHeight)')
-
await page.evaluate('alert("在浏览器执行js脚本!")')
1
2
3# 相当于拿到了dom对象
element = await page.J('#ul>a[name="tj_trtieba"]')
print(await page.evaluate('el => el.innerHTML', element)) -
page.evaluateOnNewDocument(pageFunction[, …args]), 指定的函数在所属的页面被创建并且所属页面的任意script执行之前被调用。常用于修改页面JS环境。
1
2
3
4page = await browser.newPage()
await page.setViewPort({'width': 1366, 'height': 768})
await page.evaluateOnNewDocument('''() => {
Object.defineProperty(navigator, 'webdriver', {get: () => false });附—ElementHandle元素操作:
- 获取元素边界框坐标:boundingBox(),返回元素的边界框(相对于主框架)=> x 坐标、 y 坐标、width、height
- 元素是否可见:isIntersectingViewport()
- 上传文件:uploadFile(*filpaths)
- ElementHandle 类 转 Frame类:contentFrame(),如果句柄未引用iframe,则返回None。
- 聚焦该元素:focus()
- 与鼠标相关:hover () ,将鼠标悬停到元素上面
- 与键盘相关:press (key[, options]),按键,key 表示按键的名称,option可配置:
- text (string) - 如果指定,则使用此文本生成输入事件
- delay (number) - keydown 和 keyup 之间等待的时间。默认是 0
page.keyboard.down('Shift')
:按下shift
more: 页面元素操作
安全性:
- 在使用 Pyppeteer 仍然会遇到无头浏览器检测,这里安利一个第三方库「pyppeteer-stealth」,「puppeteer-extra-plugin-stealth」引用Github上的说明「Applies various evasion techniques to make detection of headless puppeteer harder.」「A plugin for puppeteer-extra to prevent detection.」(Github地址:https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth )。可见,「pyppeteer-stealth」也是用于防止机器人检测的。用法也很简单,给page加上stealth
1 | import asyncio |
这样就可以省去隐藏WebDriver等操作,可谓省时省力省心。
await page.evaluateOnNewDocument('Object.defineProperty(navigator,"webdriver",{get:()=>undefined})')
stealth.min.js
最完美方案!模拟浏览器如何正确隐藏特征
无痕隐身模式访问
1 | async def start_browser(): |
较全的API说明:pyppeteer的基本使用、页面信息、设置
问题:按钮不生效
网页输入账号和密码之前,按钮状态为disable=true
,两个都输入后才能按下,而使用pyppeteer时,发现无论怎么尝试都不能使得其disable变化(用开发者助手修改后,无法生成正确的提交请求)。突然巧合之中,想到了是不是元素没加载成功,于是确实在开发者助手的console
中看到有很多不支持的,比如angular、bootstrap……
于是百度问题,看看是不是什么设置没开,但都没找到。后来想到升级下pyppeteer版本会不会好一点,发现自己的版本为1.0.2,已经是最新的了(https://github.com/miyakogi/pyppeteer/issues/306)。由于不通过程序直接通过浏览器进入程序也出现了无法点击的问题。再加上下面有人提出chromium有点难下载,并通过源码找道了默认的配置和设置,于是经过一阵的折腾过后,我发现**“按钮无法点击”的问题就在于chromium版本过低**。
1 | import pyppeteer.chromium_downloader |
登录这个storage的网站可以看到,上面所记录的chromium全都是2013年的版本,必然版本落后了。于是下载最新的chromium覆盖C:\Users\mrli\AppData\Local\pyppeteer\pyppeteer\local-chromium\588429\chrome-win32
目录后再运行,按钮不能点击的问题就消失了!~😄
启发于:使用pyppeteer 下载chromium 报错 或速度慢、pyppeteer使用时常见的bug及解决办法【转载】
- 📖pyppeteer实战案例:Pyppeteer的使用——爬取京东——消除指纹、页面滑动事件、获取数据,获取到的是Json数据:
(await a[0].getProperty("textContent")).jsonValue()
- 📖官方API: https://pyppeteer.github.io/pyppeteer/reference.html#pyppeteer.page.Page.xpath、puppeteer中文手册
- setViewport全屏
Author: Mrli
Link: https://nymrli.top/2022/05/05/selenium不行的工作-pyppeteer上/
Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.
toy reimplementation of an event loop in Python[翻译]NextPost >
mitmproxy拦截代理——PC版HttpCanary