# -*- coding: utf-8 -*- """ 本地脚本:打开比特浏览器,并由 DrissionPage (DP) 接管控制。 运行前请确保:1) 比特浏览器客户端已启动(默认 API 端口 54345);2) 已安装 DrissionPage。 """ from __future__ import annotations import random import sys import time from pathlib import Path from worker.bit_browser import BitBrowserAPI # 保证从项目根目录运行时可导入 worker 包 _ROOT = Path(__file__).resolve().parent if str(_ROOT) not in sys.path: sys.path.insert(0, str(_ROOT)) # 比特浏览器 API 地址(与 worker 默认一致) BIT_API_BASE = "http://127.0.0.1:54345" # 要打开的浏览器:None=第一个环境,或指定环境名称/备注 BROWSER_NAME = "测试2" # 例如 "第一个" 或 "主账号" BROWSER_ID = None # 若已知窗口 ID,可直接填 def do_recommend_filter(page): """ 在推荐牛人页面:点开右上角筛选 -> 弹窗内完成筛选操作 -> 点击确认。 筛选 UI 在 iframe recommendFrame 内(推荐牛人 / recommend-v2)。 """ time.sleep(1.5) # 等待 iframe 加载 # 获取推荐牛人 iframe(name=recommendFrame) frame = page.get_frame("@name=recommendFrame") if not frame: frame = page.get_frame(1) # fallback:第一个 iframe if not frame: print("未找到推荐牛人 iframe,跳过筛选") return # 1. 点击右上角「筛选」,打开弹窗 filter_btn = frame.ele("x://span[text()='筛选']", timeout=3) or frame.ele( "x://*[contains(text(),'筛选')]", timeout=2 ) if not filter_btn: print("未找到「筛选」按钮,跳过筛选") return filter_btn.click(by_js=True) time.sleep(0.8) # 2. 弹窗内完成筛选(按需在此增加具体筛选项点击,例如学历、经验、期望职位等) # 若无需改任何条件,可保持默认,直接点确认即可。 # 3. 点击弹窗中的「确认」 confirm = ( frame.ele("x://span[text()='确认']", timeout=3) or frame.ele("x://span[text()='确定']", timeout=2) or frame.ele("x://*[contains(text(),'确认')]", timeout=2) ) if not confirm: print("未找到「确认」按钮") return confirm.click(by_js=True) time.sleep(0.5) print("推荐牛人筛选已确认") def main(): from DrissionPage import ChromiumPage, ChromiumOptions from DrissionPage.errors import NoRectError print("正在连接比特浏览器 API...") bit_api = BitBrowserAPI(BIT_API_BASE) # 打开指定或第一个浏览器窗口 print("正在打开比特浏览器...") cdp_addr, port, browser_id = bit_api.open_browser( browser_id=BROWSER_ID, name=BROWSER_NAME, remark=None, ) print(f"已打开浏览器 ID={browser_id}, CDP 端口={port}") # DrissionPage 接管:先创建选项实例,再设置本地端口 co = ChromiumOptions().set_local_port(port=port) page = ChromiumPage(addr_or_opts=co) page.listen.start('wapi/zprelation/friend/getBossFriendListV2') # 打开推荐牛人/聊天页 page.get("https://www.zhipin.com/web/chat/index") res = page.listen.wait() # 执行推荐牛人筛选:点开右上角筛选 -> 弹窗内完成筛选 -> 点击确认 do_recommend_filter(page) for i in res.response.body["zpData"]["friendList"]: print(i) name = i["name"] job_name = i["jobName"] jobId = i["jobId"] lastTime = i["lastTime"] # 最后一次上线 page.listen.start('wapi/zpchat/boss/historyMsg') # 左侧联系人列表可能是虚拟滚动,直接用 actions.scroll 会 NoRectError;先定位再 JS scrollIntoView,再点击 name_sel = f'x://span[text()="{i["name"]}"]' ele = page.ele(name_sel, timeout=2) if not ele: print(f" 跳过:未找到联系人 {i['name']}") continue try: ele.run_js("this.scrollIntoView({block: 'center', behavior: 'auto'})") except Exception: pass time.sleep(0.8) try: page.ele(name_sel, timeout=2).click(by_js=True) except (NoRectError, Exception) as e: print(f" 跳过:点击 {i['name']} 失败: {e}") continue res = page.listen.wait() for i in res.response.body["zpData"]["messages"]: print(i["body"]) body = i.get("body") or {} try: age = body["resume"]["age"] # 年龄 education = body["resume"]["education"] # 大专 position = body["resume"]["position"] # 期望职位 except: pass text = body.get("text") if body else None if text and "手机号" in text: break if text and "微信号" in text: break else: page.ele('x://*[@id="boss-chat-editor-input"]').input( "后续沟通会更及时,您方便留一下您的微信号吗?我这边加您。") time.sleep(random.randint(1, 3) + random.random()) page.ele('x://div[text()="发送"]').click() time.sleep(random.randint(1, 5) + random.random()) page.ele('x://span[text()="换微信"]').click() time.sleep(random.randint(1, 2) + random.random()) # 只点「交换微信」弹窗里的确定:先定位到“确定与对方交换微信吗?”所在 tooltip,再点其下的确定按钮,避免点到交换简历的确定 page.ele( 'x://span[contains(text(),"确定与对方交换微信吗?")]/../div[@class="btn-box"]/span[contains(@class,"boss-btn-primary")]').click( by_js=True) if __name__ == "__main__": main()