Files
ai_mip/test_adspower_playwright.py
2026-01-13 18:59:26 +08:00

412 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
AdsPower + Playwright CDP 集成测试
演示如何通过 CDP 连接到 AdsPower 指纹浏览器
"""
from loguru import logger
from adspower_client import AdsPowerClient
from config import Config
import sys
# 配置日志
logger.remove()
logger.add(
sys.stdout,
format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | <level>{message}</level>",
level="INFO"
)
def test_adspower_connection(use_proxy: bool = False, proxy_info: dict = None, use_api_v1: bool = False):
"""测试 AdsPower + Playwright CDP 连接
Args:
use_proxy: 是否使用大麦IP代理
proxy_info: 已获取的代理信息
use_api_v1: 是否使用API v1方式更新代理
"""
# ==================== 配置区 ====================
# 访问的网页地址,在这里修改
TEST_URL = "https://health.baidu.com/m/detail/ar_2366617956693492811" # IP检测网站可查看代理是否生效
# 其他可选项:
# TEST_URL = "https://www.baidu.com" # 百度
# TEST_URL = "https://www.google.com" # Google
# TEST_URL = "https://你的MIP页面地址" # 你的目标网页
# =====================================================
client = AdsPowerClient()
try:
# 0. 先查询 Profile 列表
logger.info("=" * 60)
logger.info("步骤 0: 查询 Profile 列表")
logger.info("=" * 60)
result = client.list_profiles()
if not result:
logger.error("查询 Profile 失败")
return False
profiles = result.get('data', {}).get('list', [])
if not profiles:
logger.error("没有可用的 Profile请先在 AdsPower 中创建 Profile")
return False
# 使用第一个 Profile
first_profile = profiles[0]
profile_id = first_profile.get('profile_id')
profile_name = first_profile.get('name', 'N/A')
logger.info(f"将使用 Profile: {profile_name} (ID: {profile_id})")
logger.info("")
# 1. 启动 AdsPower 浏览器(可选使用代理)
logger.info("=" * 60)
logger.info(f"步骤 1: {'[使用代理] ' if use_proxy else ''}启动 AdsPower 浏览器")
if use_proxy:
logger.info(f"代理更新方式: {'API v1 (直接传入proxy_config)' if use_api_v1 else 'API v2 (使用proxy_id引用)'}")
logger.info("=" * 60)
if use_proxy and proxy_info:
if use_api_v1:
# 使用 API v1 方式:直接传入 proxy_config
logger.info("使用 API v1 方式更新代理...")
proxy_config_v1 = {
"proxy_type": "http",
"proxy_host": proxy_info["host"],
"proxy_port": proxy_info["port"],
"proxy_user": client.DAMAI_USER,
"proxy_password": client.DAMAI_PASSWORD,
"proxy_soft": "other"
}
# 直接更新 Profile
success = client.update_profile_proxy_v1(profile_id, proxy_config_v1)
if not success:
logger.warning("更新代理失败 (API v1),将不使用代理启动")
else:
# 使用 API v2 方式:先创建代理,再引用
logger.info("使用 API v2 方式更新代理...")
proxy_config = {
"type": "http",
"host": proxy_info["host"],
"port": proxy_info["port"],
"user": client.DAMAI_USER,
"password": client.DAMAI_PASSWORD,
"ipchecker": "ip2location",
"remark": "Damai Auto Proxy"
}
# 创建代理
proxy_id = client.create_proxy(proxy_config)
if proxy_id:
# 更新 Profile
client.update_profile_proxy(profile_id, proxy_id)
else:
logger.warning("创建代理失败,将不使用代理启动")
browser_info = client.start_browser(user_id=profile_id)
else:
browser_info = client.start_browser(user_id=profile_id)
if not browser_info:
logger.error("启动 AdsPower 浏览器失败")
return False
logger.info(f"浏览器信息: {browser_info}")
# 2. 通过 CDP 连接到浏览器
logger.info("")
logger.info("=" * 60)
logger.info("步骤 2: 通过 CDP 连接到浏览器")
logger.info("=" * 60)
browser = client.connect_browser(browser_info)
if not browser:
logger.error("CDP 连接失败")
return False
logger.info(f"浏览器版本: {browser.version}")
logger.info(f"上下文数量: {len(browser.contexts)}")
# 3. 获取页面
logger.info("")
logger.info("=" * 60)
logger.info("步骤 3: 获取浏览器页面")
logger.info("=" * 60)
page = client.get_page(browser)
if not page:
logger.error("获取页面失败")
return False
logger.info(f"页面 URL: {page.url}")
# 3.5. 关闭其他标签页只保留AdsPower启动页
logger.info("")
logger.info("=" * 60)
logger.info("步骤 3.5: 清理多余标签页")
logger.info("=" * 60)
context = browser.contexts[0]
all_pages = context.pages
logger.info(f"当前打开的标签页数: {len(all_pages)}")
# 遍历所有页面,关闭非 AdsPower 启动页
closed_count = 0
for p in all_pages:
try:
page_url = p.url
# 保留 AdsPower 启动页
if 'start.adspower.net' in page_url:
logger.info(f"保留启动页: {page_url}")
else:
logger.info(f"关闭标签页: {page_url}")
p.close()
closed_count += 1
except Exception as e:
logger.warning(f"关闭页面失败: {str(e)}")
logger.info(f"已关闭 {closed_count} 个标签页")
# 重新获取当前页面列表
remaining_pages = context.pages
logger.info(f"剩余标签页数: {len(remaining_pages)}")
# 如果所有页面都被关闭了,创建一个新页面
if len(remaining_pages) == 0:
logger.info("所有页面已关闭,创建新标签页")
page = context.new_page()
else:
# 使用第一个剩余页面
page = remaining_pages[0]
logger.info(f"使用剩余页面: {page.url}")
# 4. 测试页面操作
logger.info("")
logger.info("=" * 60)
logger.info("步骤 4: 测试页面操作")
logger.info("=" * 60)
# 访问配置的网页
logger.info(f"访问测试页面: {TEST_URL}")
page.goto(TEST_URL, wait_until='domcontentloaded')
# 等待广告接口响应完成
logger.info("等待广告接口响应...")
try:
# 等待广告接口请求完成
response = page.wait_for_response(
lambda r: 'getRefreshAdAssets' in r.url and r.status == 200,
timeout=10000 # 10秒超时
)
logger.info(f"广告接口已响应: {response.url}")
# 等待DOM更新
import time
time.sleep(2)
except Exception as e:
logger.warning(f"等待广告接口超时或失败: {str(e)}")
logger.info("继续执行...")
import time
time.sleep(2)
# 获取页面标题
title = page.title()
logger.info(f"页面标题: {title}")
# 获取页面 URL
current_url = page.url
logger.info(f"当前 URL: {current_url}")
# 截图测试(点击前)
screenshot_path = "./test_screenshot_before.png"
page.screenshot(path=screenshot_path)
logger.info(f"截图已保存: {screenshot_path}")
# 查找并点击广告
logger.info("")
logger.info("-" * 60)
logger.info("开始查找广告元素...")
try:
# 查找所有广告元素
ad_selector = 'span.ec-tuiguang.ecfc-tuiguang.xz81bbe'
ad_elements = page.locator(ad_selector)
ad_count = ad_elements.count()
logger.info(f"找到 {ad_count} 个广告元素")
if ad_count > 0:
# 点击第一个广告
logger.info("准备点击第一个广告...")
# 滚动到元素可见
first_ad = ad_elements.first
first_ad.scroll_into_view_if_needed()
time.sleep(1)
# 点击广告
first_ad.click()
logger.info("✅ 已点击第一个广告")
# 等待页面跳转
time.sleep(3)
# 获取点击后的页面信息
new_url = page.url
new_title = page.title()
logger.info(f"点击后 URL: {new_url}")
logger.info(f"点击后标题: {new_title}")
# 截图(点击后)
screenshot_path_after = "./test_screenshot_after.png"
page.screenshot(path=screenshot_path_after)
logger.info(f"点击后截图已保存: {screenshot_path_after}")
else:
logger.warning("⚠ 未找到广告元素")
except Exception as e:
logger.error(f"查找/点击广告失败: {str(e)}")
# 保存错误时的截图
page.screenshot(path="./test_screenshot_error.png")
logger.info("错误截图已保存: ./test_screenshot_error.png")
# 5. 清理资源
logger.info("")
logger.info("=" * 60)
logger.info("步骤 5: 测试完成")
logger.info("=" * 60)
# 注意:不停止浏览器,保持运行状态供手动操作
logger.info("浏览器保持运行状态,可继续手动操作")
logger.info("如需停止浏览器请在AdsPower中手动关闭")
logger.info("")
logger.info("=" * 60)
logger.info("测试完成!浏览器未关闭")
logger.info("=" * 60)
return True
except Exception as e:
logger.error(f"测试异常: {str(e)}")
return False
finally:
# 注意:不自动清理资源,保持浏览器运行
pass
def test_multiple_pages():
"""测试多页面操作"""
client = AdsPowerClient()
profile_id = None
try:
logger.info("=" * 60)
logger.info("测试多页面操作")
logger.info("=" * 60)
# 查询 Profile
result = client.list_profiles()
if not result:
return False
profiles = result.get('data', {}).get('list', [])
if not profiles:
logger.error("没有可用的 Profile")
return False
profile_id = profiles[0].get('profile_id')
# 启动并连接
browser_info = client.start_browser(user_id=profile_id)
if not browser_info:
return False
browser = client.connect_browser(browser_info)
if not browser:
return False
# 获取第一个页面
page1 = client.get_page(browser)
logger.info("访问百度...")
page1.goto("https://www.baidu.com")
logger.info(f"页面1标题: {page1.title()}")
# 创建新页面
context = browser.contexts[0]
page2 = context.new_page()
logger.info("访问必应...")
page2.goto("https://www.bing.com")
logger.info(f"页面2标题: {page2.title()}")
logger.info(f"当前打开的页面数: {len(context.pages)}")
# 关闭页面
page2.close()
logger.info("已关闭页面2")
return True
except Exception as e:
logger.error(f"测试异常: {str(e)}")
return False
finally:
try:
if profile_id:
client.stop_browser(user_id=profile_id)
else:
client.stop_browser()
except:
pass
if __name__ == "__main__":
logger.info("开始测试 AdsPower + Playwright CDP 集成")
logger.info("")
logger.info(f"当前环境: {Config.ENV}")
logger.info(f"AdsPower API: {Config.ADSPOWER_API_URL}")
logger.info("")
# 创建客户端
client = AdsPowerClient()
# ==================== 配置区 ====================
# 默认使用代理,如不需要改为 False
use_proxy = True
# 默认使用 API v2 Direct 方式0=v2 proxy_id, 1=v1, 2=v2 direct
use_api_v1 = True # True=API v1, False=API v2
# =====================================================
proxy_info = None
# 如果使用代理,提前获取
if use_proxy:
logger.info("")
logger.info(f"使用代理模式: {'API v1 (直接传入proxy_config)' if use_api_v1 else 'API v2 (使用proxy_id引用)'}")
logger.info("步骤 0: 提前获取大麦IP代理")
proxy_info = client.get_damai_proxy()
if not proxy_info:
logger.error("获取代理失败,终止测试")
sys.exit(1)
logger.info(f"代理地址: {proxy_info['host']}:{proxy_info['port']}")
logger.info("")
# 测试基本连接
if test_adspower_connection(use_proxy=use_proxy, proxy_info=proxy_info, use_api_v1=use_api_v1):
logger.info("\n基本连接测试通过\n")
else:
logger.error("\n基本连接测试失败\n")
sys.exit(1)
# 测试多页面操作
# if test_multiple_pages():
# logger.info("\n多页面操作测试通过\n")
# else:
# logger.error("\n多页面操作测试失败\n")