Playwright是由微軟公司2020年初發佈的新一代自動化測試工具,相較於目前最常用的Selenium,它僅用一個API即可自動執行Chromium、Firefox、WebKit等主流瀏覽器自動化操作。 對各種開發語言也有非常好的支持。常用的NodeJs、Java、python都有支持,且有豐富 ...
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要強很多。還有很多功能,下次再分享。
關註我的公眾號:青塬科技,定期分享經驗文章。