This commit is contained in:
sjk
2026-01-13 18:59:26 +08:00
commit 7feccf246d
56 changed files with 11596 additions and 0 deletions

411
test_adspower_playwright.py Normal file
View File

@@ -0,0 +1,411 @@
"""
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")