control_opened_browser.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. """
  2. 连接已打开的 AdsPower 浏览器,不重新启动/关闭
  3. 用于用户已手动开启浏览器并希望直接控制当前窗口的场景
  4. """
  5. import time
  6. import random
  7. import argparse
  8. from pathlib import Path
  9. from urllib.parse import urlencode
  10. from playwright.sync_api import sync_playwright, Page
  11. try:
  12. from .ads_power_client import AdsPowerClient
  13. except ImportError:
  14. import sys
  15. sys.path.insert(0, str(Path(__file__).parent))
  16. from ads_power_client import AdsPowerClient
  17. def get_active_ws_endpoint(base_url: str, profile_id: str) -> str:
  18. """通过 AdsPower API 获取已打开浏览器的 ws endpoint"""
  19. import requests
  20. url = f"{base_url}/api/v1/browser/active"
  21. resp = requests.get(url, params={"user_id": profile_id}, timeout=10)
  22. resp.raise_for_status()
  23. data = resp.json()
  24. if data.get("code") != 0:
  25. raise RuntimeError(f"获取活动浏览器失败: {data}")
  26. ws = data["data"]["ws"]["puppeteer"]
  27. return ws
  28. def control_opened_browser(
  29. profile_id: str,
  30. query: str = "concessionnaire voiture Casablanca",
  31. ads_power_url: str = "http://127.0.0.1:50325",
  32. screenshot_path: str = "facebook_dealer_controlled.png",
  33. stay_seconds: int = 60
  34. ):
  35. """连接已打开的浏览器并控制它访问摩洛哥汽车经销商主页"""
  36. ws_endpoint = get_active_ws_endpoint(ads_power_url, profile_id)
  37. print(f"连接到已打开的浏览器: {ws_endpoint}")
  38. playwright = sync_playwright().start()
  39. browser = playwright.chromium.connect_over_cdp(ws_endpoint)
  40. # 获取第一个页面
  41. context = browser.contexts[0] if browser.contexts else browser.new_context()
  42. page = context.pages[0] if context.pages else context.new_page()
  43. page.set_viewport_size({"width": 1280, "height": 800})
  44. try:
  45. # 构建 Facebook 搜索 URL
  46. encoded_query = urlencode({"q": query})
  47. search_url = f"https://www.facebook.com/search/pages/?{encoded_query}"
  48. print(f"在当前浏览器窗口访问搜索页: {search_url}")
  49. page.goto(search_url, wait_until="domcontentloaded", timeout=60000)
  50. time.sleep(random.uniform(4, 6))
  51. # 等待页面稳定后截图
  52. time.sleep(random.uniform(3, 5))
  53. try:
  54. page.screenshot(path=f"{screenshot_path}_search.png", full_page=False, timeout=60000)
  55. print(f"搜索结果已截图: {screenshot_path}_search.png")
  56. except Exception as e:
  57. print(f"搜索结果截图超时或失败: {e}")
  58. # 收集页面链接
  59. links = set()
  60. elements = page.query_selector_all('a[href*="/"]')
  61. for el in elements:
  62. href = el.get_attribute("href")
  63. if href and _is_valid_page_link(href):
  64. links.add(_normalize_url(href))
  65. links = list(links)
  66. print(f"找到 {len(links)} 个可能的主页链接")
  67. if not links:
  68. print("未找到任何主页链接,请检查搜索词或页面是否已加载")
  69. return
  70. # 访问第一个主页
  71. first_link = links[0]
  72. print(f"准备进入第一个主页: {first_link}")
  73. page.goto(first_link, wait_until="domcontentloaded", timeout=60000)
  74. time.sleep(random.uniform(3, 5))
  75. # 截图主页
  76. time.sleep(random.uniform(2, 4))
  77. try:
  78. page.screenshot(path=screenshot_path, full_page=False, timeout=60000)
  79. print(f"主页已截图: {screenshot_path}")
  80. except Exception as e:
  81. print(f"主页截图超时或失败: {e}")
  82. print(f"当前页面标题: {page.title()}")
  83. print(f"当前 URL: {page.url}")
  84. # 停留,不发送任何信息,不断开 Playwright 连接,浏览器保持打开
  85. print(f"浏览器将停留 {stay_seconds} 秒,不执行任何发送操作,也不断开 Playwright 连接,浏览器保持打开...")
  86. print("你可以在自己的电脑屏幕上看到这个窗口")
  87. time.sleep(stay_seconds)
  88. finally:
  89. # 不断开 Playwright 连接,浏览器保持打开,只停止 Playwright(保持浏览器窗口打开)
  90. print("停止 Playwright,浏览器窗口保持打开")
  91. playwright.stop()
  92. def _is_valid_page_link(href: str) -> bool:
  93. """判断是否是有效的 Facebook 主页链接"""
  94. from urllib.parse import urlparse
  95. parsed = urlparse(href)
  96. if parsed.netloc not in ["www.facebook.com", "facebook.com", "m.facebook.com"]:
  97. return False
  98. excluded = ["/search/", "/watch", "/groups", "/marketplace",
  99. "/messages", "/notifications", "/settings",
  100. "/photo.php", "/videos", "/posts", "/reel"]
  101. for pattern in excluded:
  102. if pattern in parsed.path:
  103. return False
  104. path = parsed.path.strip("/")
  105. if not path or "/" in path:
  106. return False
  107. return True
  108. def _normalize_url(url: str) -> str:
  109. """标准化 Facebook URL"""
  110. from urllib.parse import urlparse
  111. parsed = urlparse(url)
  112. path = parsed.path.strip("/").split("/")[0]
  113. return f"https://www.facebook.com/{path}/"
  114. def main():
  115. parser = argparse.ArgumentParser(description="控制已打开的 AdsPower 浏览器访问 Facebook 经销商主页")
  116. parser.add_argument("--profile-id", required=True, help="AdsPower profile ID")
  117. parser.add_argument("--query", default="concessionnaire voiture Casablanca", help="搜索关键词")
  118. parser.add_argument("--ads-power-url", default="http://127.0.0.1:50325", help="AdsPower API URL")
  119. parser.add_argument("--screenshot", default="D:/WEFANBOT/facebook_dealer_controlled.png", help="截图保存路径")
  120. parser.add_argument("--stay", type=int, default=60, help="停留秒数")
  121. args = parser.parse_args()
  122. control_opened_browser(
  123. profile_id=args.profile_id,
  124. query=args.query,
  125. ads_power_url=args.ads_power_url,
  126. screenshot_path=args.screenshot,
  127. stay_seconds=args.stay
  128. )
  129. if __name__ == "__main__":
  130. main()