From ce0b61fc6d6d8cb7638f03a770706d9f63c9eeaa Mon Sep 17 00:00:00 2001 From: ddrwode <34234@3来 34> Date: Fri, 6 Mar 2026 14:00:21 +0800 Subject: [PATCH] haha --- worker/tasks/boss_recruit.py | 76 ++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/worker/tasks/boss_recruit.py b/worker/tasks/boss_recruit.py index 5708a45..b53e89c 100644 --- a/worker/tasks/boss_recruit.py +++ b/worker/tasks/boss_recruit.py @@ -467,15 +467,17 @@ class BossRecruitHandler(BaseTaskHandler): def _greet_one_geek(self, page, container, item: dict) -> bool: """ - 对单个牛人打招呼,对齐 1.py _greet_one_geek 逻辑。 - 关键改进:使用 finally 确保失败时关闭所有面板/弹窗, - 防止 UI 残留导致下一个人卡死在循环中。 + 对单个牛人打招呼。 + 关键:点击姓名后,BOSS 会动态加载一个新 iframe(简历详情面板), + "打招呼"按钮在这个面板 frame 中。 + 不能搜索所有 frame(因为 recommendFrame 中也有打招呼按钮), + 必须精确定位到详情面板 frame。 """ geek_name = str((item.get("geekCard") or {}).get("geekName", "")).strip() if not geek_name: return False - panel_opened = False # 标记是否打开了右侧面板 + panel_opened = False try: # 查找姓名元素 @@ -491,18 +493,20 @@ class BossRecruitHandler(BaseTaskHandler): time.sleep(3) panel_opened = True - # 获取右侧面板 - panel = page.get_frame(1) + # 获取简历详情面板 frame(非 recommendFrame 的那个 iframe) + panel = self._get_detail_panel_frame(page) + if not panel: + return False time.sleep(random.uniform(0.5, 1.2)) # 打招呼 - greet_btn = panel.ele('x://*[contains(text(),"打招呼")]', timeout=2) + greet_btn = panel.ele('x://*[contains(text(),"打招呼")]', timeout=3) if not greet_btn: return False greet_btn.click(by_js=True) time.sleep(random.uniform(0.5, 1.2)) - # 收藏(可选,失败不影响) + # 收藏(可选) try: collect_btn = panel.ele('x://*[contains(text(),"收藏")]', timeout=2) if collect_btn: @@ -511,17 +515,19 @@ class BossRecruitHandler(BaseTaskHandler): except Exception: pass - # 点击 btn-outline 按钮(可选,失败不影响) + # 点击 btn-outline 按钮(可选) for _ in range(3): try: extra_btn = panel.ele('x://*[@class="btn-v2 btn-outline-v2"]', timeout=1) if extra_btn: extra_btn.click(by_js=True) time.sleep(random.uniform(0.5, 1.2)) + else: + break except Exception: break - # 快速回复(可选,失败不影响) + # 快速回复 try: input_box = page.ele('x://*[@data-placeholder="快速回复"]', timeout=2) if input_box: @@ -541,11 +547,59 @@ class BossRecruitHandler(BaseTaskHandler): except Exception: return False finally: - # 无论成功还是失败,都确保关闭所有面板 if panel_opened: self._close_all_panels(page) time.sleep(0.5) + @staticmethod + def _get_detail_panel_frame(page): + """ + 获取简历详情面板 frame。 + + 页面结构: + - frame 0: recommendFrame(推荐牛人列表,name="recommendFrame") + - frame 1: 简历详情面板(点击牛人后动态加载的 iframe,无固定 name) + + 由于多次交互后 frame 索引可能变化,不能用固定索引。 + 策略:遍历所有 frame,排除 name="recommendFrame" 的,返回另一个。 + """ + try: + frames = page.get_frames() + except Exception: + return None + + if not frames: + return None + + # 只有一个 frame 时,它就是 recommendFrame,没有详情面板 + if len(frames) <= 1: + # 仍然尝试 get_frame(1)(可能 DrissionPage 有隐式 frame) + try: + return page.get_frame(1) + except Exception: + return None + + # 多个 frame 时,找到不是 recommendFrame 的那个 + for i in range(len(frames) - 1, -1, -1): # 倒序,最新加载的通常在后面 + try: + frame = page.get_frame(i) + # 检查 frame 的 name 属性 + frame_ele = getattr(frame, 'frame_ele', None) + if frame_ele: + name = frame_ele.attr('name') or '' + if name == 'recommendFrame': + continue + # 不是 recommendFrame 的 frame 就是详情面板 + return frame + except Exception: + continue + + # 兜底:如果上面的方法都失败,尝试直接 get_frame(1) + try: + return page.get_frame(1) + except Exception: + return None + # ────────────────────── 联系记录入库 ────────────────────── def _save_new_greet_contacts(self, *, records: List[dict], worker_id: str, account_name: str) -> int: