翼度科技»论坛 编程开发 python 查看内容

【自动化】使用PlayWright+代理IP实现多环境隔离

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
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、普通的无痕模式,用完即销毁。这种方式下,浏览器的历史记录之类的不会保存。适合用于爬虫性的工作。
代码大致是这样的。
  1. browser = pw.chromium.launch(headless=headless, proxy=my_proxy,
  2.                                          ignore_default_args=ignore_args,
  3.                                  args=default_args)
  4. browserContext = browser.new_context(user_agent=userAgent, storage_state=storage_state)
复制代码
可以指定UserAgent,这是我们模拟不同操作系统和浏览器数据的必填项。
也可以指定headless无头模式,这样浏览器不会有界面出现。背后去工作。
2、普通的持久模式,需要指定用户的数据目录。实现数据的隔离。
比如1号浏览器存到data1,2号存到data2,数据不会冲突,各干各的事,可以同时登陆一个网站的多个账号,互不影响。
不方便的地方在于,每次执行完任务,浏览器会随着程序关闭而关闭。
copy一段网上的示例
  1. # 获取 google chrome 的本地缓存文件
  2. USER_DIR_PATH = f"C:\\Users\\{getpass.getuser()}\\AppData\Local\Google\Chrome\\User Data"
  3. with sync_playwright() as p:
  4.     browser = p.chromium.launch_persistent_context(
  5.                         # 指定本机用户缓存地址,这是隔离环境的主要点,指定不同的目录存放用户的数据。
  6.                         user_data_dir=USER_DIR_PATH,
  7.                         # 接收下载事件,允许下载需要
  8.                         accept_downloads=True,
  9.                         # 设置 GUI 模式,可以看到浏览器界面
  10.                         headless=False,
  11.                         bypass_csp=True,
  12.                         slow_mo=1000,
  13.                         channel="chrome",
  14.                     )
  15.     page = browser.new_page()
  16.     page.goto("https://www.cnblogs.com/yoyoketang")
  17.     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
  1. if sys.platform.startswith('linux'):  # Linux
  2.     exe_name = 'chrome'
  3.     extParam.append('--no-sandbox')
  4. elif sys.platform.startswith('win'):  # Windows
  5.     win_path = 'C:\Program Files\Google\Chrome\Application\chrome.exe'
  6.     exe_name = win_path if os.path.exists(win_path) else 'chrome.exe'
  7. elif sys.platform.startswith('darwin'):  # Mac
  8.     exe_name = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
  9.     extParam.append('--no-sandbox')
  10. # 启用UA
  11. if config.get('user_agent'):
  12.     extParam.append(fr'--user-agent="{config.get("user_agent")}"')
  13. # 启用无痕
  14. if config.get('incognito'):
  15.     extParam.append('--incognito')
  16. # 无开屏
  17. if config.get('no_window'):
  18.     extParam.append('--no-startup-window')
  19.         
  20. 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)}'
  21. os.popen(command)
复制代码
还有不少代码,就不往上面贴了。
2、核心点2,动态加载插件进不同的Chrome环境,各用各的代理。
  1. def create_proxyauth_extension(proxy_host, proxy_port,
  2.                                proxy_username, proxy_password,
  3.                                scheme='http', plugin_dir=None):
  4.     """
  5.     代理认证插件,返回代理插件的地址
  6.     Chrome使用带账号密码的代理IP
  7.     插件来源:https://github.com/henices/Chrome-proxy-helper
  8.     参考:https://ask.hellobi.com/blog/cuiqingcai/10307#articleHeader5
  9.     https://my.oschina.net/sunboy2050/blog/1858508
  10.     https://github.com/aneasystone/selenium-test/blob/master/08-proxy-with-password.py
  11.     https://developer.chrome.com/extensions/proxy
  12.     args:
  13.         proxy_host (str): 你的代理地址或者域名(str类型)
  14.         proxy_port (int): 代理端口号(int类型)
  15.         proxy_username (str):用户名(字符串)
  16.         proxy_password (str): 密码 (字符串)
  17.     kwargs:
  18.         scheme (str): 代理方式 默认http
  19.         plugin_dir (str): 扩展的目录路径
  20.     return str -> plugin_path
  21.     """
  22.     # 插件目录
  23.     if not plugin_dir:
  24.         plugin_dir = os.path.join(get_data_dir('chrome_plugin'), f'custom_proxyauth_plugin')
  25.     if not os.path.exists(plugin_dir):
  26.         os.makedirs(plugin_dir)
  27.     # 生成的Zip文件地址
  28.     plugin_file = os.path.join(plugin_dir, f"proxy_plugin_{proxy_host}_{proxy_port}.zip")
  29.     # 旧文件清理掉
  30.     if os.path.exists(plugin_file):
  31.         os.remove(plugin_file)
  32.     manifest_json = """
  33.     {
  34.         "version": "1.0.0",
  35.         "manifest_version": 2,
  36.         "name": "Chrome Proxy",
  37.         "permissions": [
  38.             "proxy",
  39.             "tabs",
  40.             "unlimitedStorage",
  41.             "storage",
  42.             "<all_urls>",
  43.             "webRequest",
  44.             "webRequestBlocking"
  45.         ],
  46.         "background": {
  47.             "scripts": ["background.js"]
  48.         },
  49.         "minimum_chrome_version":"22.0.0"
  50.     }
  51.     """
  52.     background_js = string.Template(
  53.         """
  54.         var config = {
  55.                 mode: "fixed_servers",
  56.                 pacScript: {},
  57.                 rules: {
  58.                   singleProxy: {
  59.                     scheme: "${scheme}",
  60.                     host: "${host}",
  61.                     port: ${port}
  62.                   },
  63.                   bypassList: ["foobar.com"]
  64.                 }
  65.               };
  66.         chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});
  67.         function callbackFn(details) {
  68.             return {
  69.                 authCredentials: {
  70.                     username: "${username}",
  71.                     password: "${password}"
  72.                 }
  73.             };
  74.         }
  75.         chrome.webRequest.onAuthRequired.addListener(
  76.                     callbackFn,
  77.                     {urls: ["<all_urls>"]},
  78.                     ['blocking']
  79.         );
  80.         """
  81.     ).substitute(
  82.         host=proxy_host,
  83.         port=proxy_port,
  84.         username=proxy_username,
  85.         password=proxy_password,
  86.         scheme=scheme,
  87.     )
  88.     # 先写ZIP
  89.     with zipfile.ZipFile(plugin_file, 'w') as zp:
  90.         zp.writestr("manifest.json", manifest_json)
  91.         zp.writestr("background.js", background_js)
  92.     # 再手写文件过去
  93.     with open(os.path.join(plugin_dir, 'manifest.json'), 'w+') as fi:
  94.         fi.write(manifest_json)
  95.     with open(os.path.join(plugin_dir, 'background.js'), 'w+') as fi:
  96.         fi.write(background_js)
  97.     return plugin_file
复制代码
Java也可以用同样的方式实现。后续配上Java的多线程。相信开100个窗口干活,不是什么难事。
Playwright在下载上传方面,比以前的Selenium要强很多。还有很多功能,下次再分享。
关注我的公众号:青塬科技,定期分享经验文章。

来源:https://www.cnblogs.com/kylexy/p/18051812
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具