将浏览器代理标准化为 Playwright/Chromium 可接受的格式。 Chromium 不支持带账号密码的 socks5/socks5h 代理认证。 对于 `socks5(h)://user:pass@host:port`,自动降级为 `http://user:pass@host:port`, 方便兼容同时提供 HTTP/SOCKS5 双入口的代理服务商。 Returns: (normalized_proxy_url, warning_message)
(proxy_url: str)
| 258 | return None |
| 259 | |
| 260 | def normalize_browser_proxy_url(proxy_url: str) -> tuple[Optional[str], Optional[str]]: |
| 261 | """将浏览器代理标准化为 Playwright/Chromium 可接受的格式。 |
| 262 | |
| 263 | Chromium 不支持带账号密码的 socks5/socks5h 代理认证。 |
| 264 | 对于 `socks5(h)://user:pass@host:port`,自动降级为 `http://user:pass@host:port`, |
| 265 | 方便兼容同时提供 HTTP/SOCKS5 双入口的代理服务商。 |
| 266 | |
| 267 | Returns: |
| 268 | (normalized_proxy_url, warning_message) |
| 269 | """ |
| 270 | if not proxy_url: |
| 271 | return None, None |
| 272 | |
| 273 | proxy_url = proxy_url.strip() |
| 274 | match = re.match(r'^(socks5h?|socks5|http|https)://(?:([^:]+):([^@]+)@)?([^:]+):(\d+)$', proxy_url) |
| 275 | if not match: |
| 276 | if not re.match(r'^(http|https|socks5h?|socks5)://', proxy_url): |
| 277 | proxy_url = f"http://{proxy_url}" |
| 278 | return proxy_url, None |
| 279 | |
| 280 | protocol, username, password, host, port = match.groups() |
| 281 | if protocol.startswith("socks5") and username and password: |
| 282 | normalized = f"http://{username}:{password}@{host}:{port}" |
| 283 | warning = ( |
| 284 | f"检测到带认证的 {protocol.upper()} 代理。" |
| 285 | "Chromium 不支持 socks5 用户名密码认证," |
| 286 | f"已自动改用 HTTP 代理启动浏览器: http://{host}:{port}" |
| 287 | ) |
| 288 | return normalized, warning |
| 289 | |
| 290 | if protocol == "socks5h": |
| 291 | proxy_url = f"socks5://{host}:{port}" |
| 292 | |
| 293 | return proxy_url, None |
| 294 | |
| 295 | def validate_browser_proxy_url(proxy_url: str) -> tuple[bool, str]: |
| 296 | if not proxy_url: return True, None |
no outgoing calls
no test coverage detected