共计 5836 个字符,预计需要花费 15 分钟才能阅读完成。
Playwright 是由微软公司 2020 年初公布的新一代自动化测试工具,相较于目前最罕用的 Selenium,它仅用一个 API 即可主动执行 Chromium、Firefox、WebKit 等支流浏览器自动化操作。
对各种开发语言也有十分好的反对。罕用的 NodeJs、Java、python 都有反对,且有丰盛的文档参考。
Python 环境下的装置应用
1、装置依赖库
pip install playwright
2、装置浏览器驱动文件
装置好依赖库之后,会主动注册全局命令。上面 2 种形式都能够疾速装置驱动文件(驱动就是内置的浏览器)
python -m playwright install
或者:
playwright install
如果命令是 python3,替换为 pip3 install 和 python3 -m 即可。
网上有十分多的教程。装置并非本文的重点。
多环境隔离的利用场景
常见的如爬虫,可能须要应用代理 IP 隔离开不同的浏览器进行数据抓取。
像另一些须要多号操作的营销内容,也须要多个浏览器相互隔离开。更高要求的才会应用代理 + 隔离的形式。
产生齐全不一样的浏览器环境。比方大量的号去做不同的事。
还有很多罕用的场景。独立洁净的浏览器环境 +Playwright 的自动化。能够实现十分多的乏味的利用。
Playwright 启动浏览器有几种模式。咱们须要先进行理解。
1、一般的无痕模式,用完即销毁。这种形式下,浏览器的历史记录之类的不会保留。适宜用于爬虫性的工作。
代码大抵是这样的。
browser = pw.chromium.launch(headless=headless, proxy=my_proxy, | |
ignore_default_args=ignore_args, | |
args=default_args) | |
browserContext = browser.new_context(user_agent=userAgent, storage_state=storage_state) |
能够指定 UserAgent,这是咱们模仿不同操作系统和浏览器数据的必填项。
也能够指定 headless 无头模式,这样浏览器不会有界面呈现。背地去工作。
2、一般的长久模式,须要指定用户的数据目录。实现数据的隔离。
比方 1 号浏览器存到 data1,2 号存到 data2,数据不会抵触,各干各的事,能够同时登陆一个网站的多个账号,互不影响。
不不便的中央在于,每次执行完工作,浏览器会随着程序敞开而敞开。
copy 一段网上的示例
# 获取 google chrome 的本地缓存文件 | |
USER_DIR_PATH = f"C:\\Users\\{getpass.getuser()}\\AppData\Local\Google\Chrome\\User Data" | |
with sync_playwright() as p: | |
browser = p.chromium.launch_persistent_context( | |
# 指定本机用户缓存地址,这是隔离环境的次要点,指定不同的目录寄存用户的数据。user_data_dir=USER_DIR_PATH, | |
# 接管下载事件,容许下载须要 | |
accept_downloads=True, | |
# 设置 GUI 模式,能够看到浏览器界面 | |
headless=False, | |
bypass_csp=True, | |
slow_mo=1000, | |
channel="chrome", | |
) | |
page = browser.new_page() | |
page.goto("https://www.cnblogs.com/yoyoketang") | |
page.pause() |
3、直连零碎的 Chrome。如果零碎有 Chrome 浏览器,playwright 能够间接连贯,并进行操作。然而须要做一些配置。
这也是我目前用得最多的模式。
外围的原理就是应用 CDP 连贯上 Chrome。须要开启 Chrome 时,指定一个调试端口,供咱们近程连贯下来应用。
官网提供的具体函数是
pw.chromium.connect_over_cdp(cdp_url, timeout=0)
长处在于:
脚本和浏览器拆散。脚本开不开,浏览器都不影响。只是须要自动化的时候,脚本才去工作。
毛病:
就是配置略麻烦。好在封装好之后,就是一次的麻烦,前面也会比拟顺畅。
如何封装属于本人的疾速启动类,python 和 java 都能够,下次再聊。
上面以 Chrome 浏览器 + 动静代理为例构建多个不同的环境
因为 Chrome 自带的 proxy 代理性能并不反对带账号密码的代理形式。
而咱们洽购的代理,必定都是有账号密码的。
所以外围点是增加一个插件,配置上代理,能反对 http 和 socks5 的代理,并反对账号密码进行连贯。
而后再通过 python,调用零碎的浏览器,产生不同的环境,应用不同的代理 IP。就能达到目标。
间接上图
没有好用的免费代理,本地模仿了一个 HK 节点的代理。
能够看到 4 个浏览器的指纹曾经不一样了。配合上代理,就是洁净的环境了。
外围的逻辑在于启用不同的 DataDir 用户数据目录,加个独立的代理插件来反对 http 和 socks5 的代理,
1、外围 1,应用 python 来疾速启动 Chrome
if sys.platform.startswith('linux'): # Linux | |
exe_name = 'chrome' | |
extParam.append('--no-sandbox') | |
elif sys.platform.startswith('win'): # Windows | |
win_path = 'C:\Program Files\Google\Chrome\Application\chrome.exe' | |
exe_name = win_path if os.path.exists(win_path) else 'chrome.exe' | |
elif sys.platform.startswith('darwin'): # Mac | |
exe_name = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' | |
extParam.append('--no-sandbox') | |
# 启用 UA | |
if config.get('user_agent'): | |
extParam.append(fr'--user-agent="{config.get("user_agent")}"') | |
# 启用无痕 | |
if config.get('incognito'): | |
extParam.append('--incognito') | |
# 无开屏 | |
if config.get('no_window'): | |
extParam.append('--no-startup-window') | |
command = fr'"{exe_name}" --remote-debugging-port={port} --user-data-dir="{data_dir}" --no-sandbox --disable-gpu --disable-software-rasterize --disable-background-networking --disable-background-mode --disable-sync --disable-blink-features=AutomationControlled --disable-client-side-phishing-detection --disable-default-apps --disable-desktop-notifications --disable-hang-monitor --disable-infobars --disable-notifications --disable-plugins-discovery --no-first-run --dns-prefetch-disable --ignore-certificate-errors --allow-running-insecure-content --test-type --origin-trial-disabled-features=WebGPU --no-default-browser-check --no-service-autorun --disable-popup-blocking --password-store=basic --disable-web-security --disable-dev-shm-usage --disable-component-update --disable-features=RendererCodeIntegrity --disable-features=FlashDeprecationWarning,EnablePasswordsAccountStorage {"".join(extParam)}' | |
os.popen(command) |
还有不少代码,就不往上面贴了。
2、外围点 2,动静加载插件进不同的 Chrome 环境,各用各的代理。
def create_proxyauth_extension(proxy_host, proxy_port, | |
proxy_username, proxy_password, | |
scheme='http', plugin_dir=None): | |
""" | |
代理认证插件,返回代理插件的地址 | |
Chrome 应用带账号密码的代理 IP | |
插件起源:https://github.com/henices/Chrome-proxy-helper | |
参考:https://ask.hellobi.com/blog/cuiqingcai/10307#articleHeader5 | |
https://my.oschina.net/sunboy2050/blog/1858508 | |
https://github.com/aneasystone/selenium-test/blob/master/08-proxy-with-password.py | |
https://developer.chrome.com/extensions/proxy | |
args: | |
proxy_host (str): 你的代理地址或者域名(str 类型)proxy_port (int): 代理端口号(int 类型)proxy_username (str): 用户名(字符串)proxy_password (str): 明码(字符串)kwargs: | |
scheme (str): 代理形式 默认 http | |
plugin_dir (str): 扩大的目录门路 | |
return str -> plugin_path | |
""" | |
# 插件目录 | |
if not plugin_dir: | |
plugin_dir = os.path.join(get_data_dir('chrome_plugin'), f'custom_proxyauth_plugin') | |
if not os.path.exists(plugin_dir): | |
os.makedirs(plugin_dir) | |
# 生成的 Zip 文件地址 | |
plugin_file = os.path.join(plugin_dir, f"proxy_plugin_{proxy_host}_{proxy_port}.zip") | |
# 旧文件清理掉 | |
if os.path.exists(plugin_file): | |
os.remove(plugin_file) | |
manifest_json = """{"version":"1.0.0","manifest_version": 2,"name":"Chrome Proxy","permissions": ["proxy","tabs","unlimitedStorage","storage","<all_urls>","webRequest","webRequestBlocking"],"background": {"scripts": ["background.js"] | |
}, | |
"minimum_chrome_version":"22.0.0" | |
} | |
"""background_js = string.Template(""" | |
var config = { | |
mode: "fixed_servers", | |
pacScript: {}, | |
rules: { | |
singleProxy: {scheme: "${scheme}", | |
host: "${host}", | |
port: ${port} | |
}, | |
bypassList: ["foobar.com"] | |
} | |
}; | |
chrome.proxy.settings.set({value: config, scope: "regular"}, function() {}); | |
function callbackFn(details) { | |
return { | |
authCredentials: {username: "${username}", | |
password: "${password}" | |
} | |
}; | |
} | |
chrome.webRequest.onAuthRequired.addListener( | |
callbackFn, | |
{urls: ["<all_urls>"]}, | |
['blocking'] | |
); | |
""" | |
).substitute( | |
host=proxy_host, | |
port=proxy_port, | |
username=proxy_username, | |
password=proxy_password, | |
scheme=scheme, | |
) | |
# 先写 ZIP | |
with zipfile.ZipFile(plugin_file, 'w') as zp: | |
zp.writestr("manifest.json", manifest_json) | |
zp.writestr("background.js", background_js) | |
# 再手写文件过来 | |
with open(os.path.join(plugin_dir, 'manifest.json'), 'w+') as fi: | |
fi.write(manifest_json) | |
with open(os.path.join(plugin_dir, 'background.js'), 'w+') as fi: | |
fi.write(background_js) | |
return plugin_file |
Java 也能够用同样的形式实现。后续配上 Java 的多线程。置信开 100 个窗口干活,不是什么难事。
Playwright 在下载上传方面,比以前的 Selenium 要强很多。还有很多性能,下次再分享。
关注我的公众号:青塬科技,定期分享教训文章。