412 lines
14 KiB
Python
412 lines
14 KiB
Python
"""
|
||
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")
|