This commit is contained in:
ddrwode
2026-03-06 14:00:21 +08:00
parent 1f935096c2
commit ce0b61fc6d

View File

@@ -467,15 +467,17 @@ class BossRecruitHandler(BaseTaskHandler):
def _greet_one_geek(self, page, container, item: dict) -> bool: def _greet_one_geek(self, page, container, item: dict) -> bool:
""" """
对单个牛人打招呼,对齐 1.py _greet_one_geek 逻辑 对单个牛人打招呼。
关键改进:使用 finally 确保失败时关闭所有面板/弹窗 关键点击姓名后BOSS 会动态加载一个新 iframe简历详情面板
防止 UI 残留导致下一个人卡死在循环中。 "打招呼"按钮在这个面板 frame 中。
不能搜索所有 frame因为 recommendFrame 中也有打招呼按钮),
必须精确定位到详情面板 frame。
""" """
geek_name = str((item.get("geekCard") or {}).get("geekName", "")).strip() geek_name = str((item.get("geekCard") or {}).get("geekName", "")).strip()
if not geek_name: if not geek_name:
return False return False
panel_opened = False # 标记是否打开了右侧面板 panel_opened = False
try: try:
# 查找姓名元素 # 查找姓名元素
@@ -491,18 +493,20 @@ class BossRecruitHandler(BaseTaskHandler):
time.sleep(3) time.sleep(3)
panel_opened = True panel_opened = True
# 获取右侧面板 # 获取简历详情面板 frame非 recommendFrame 的那个 iframe
panel = page.get_frame(1) panel = self._get_detail_panel_frame(page)
if not panel:
return False
time.sleep(random.uniform(0.5, 1.2)) 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: if not greet_btn:
return False return False
greet_btn.click(by_js=True) greet_btn.click(by_js=True)
time.sleep(random.uniform(0.5, 1.2)) time.sleep(random.uniform(0.5, 1.2))
# 收藏(可选,失败不影响 # 收藏(可选)
try: try:
collect_btn = panel.ele('x://*[contains(text(),"收藏")]', timeout=2) collect_btn = panel.ele('x://*[contains(text(),"收藏")]', timeout=2)
if collect_btn: if collect_btn:
@@ -511,17 +515,19 @@ class BossRecruitHandler(BaseTaskHandler):
except Exception: except Exception:
pass pass
# 点击 btn-outline 按钮(可选,失败不影响 # 点击 btn-outline 按钮(可选)
for _ in range(3): for _ in range(3):
try: try:
extra_btn = panel.ele('x://*[@class="btn-v2 btn-outline-v2"]', timeout=1) extra_btn = panel.ele('x://*[@class="btn-v2 btn-outline-v2"]', timeout=1)
if extra_btn: if extra_btn:
extra_btn.click(by_js=True) extra_btn.click(by_js=True)
time.sleep(random.uniform(0.5, 1.2)) time.sleep(random.uniform(0.5, 1.2))
else:
break
except Exception: except Exception:
break break
# 快速回复(可选,失败不影响) # 快速回复
try: try:
input_box = page.ele('x://*[@data-placeholder="快速回复"]', timeout=2) input_box = page.ele('x://*[@data-placeholder="快速回复"]', timeout=2)
if input_box: if input_box:
@@ -541,11 +547,59 @@ class BossRecruitHandler(BaseTaskHandler):
except Exception: except Exception:
return False return False
finally: finally:
# 无论成功还是失败,都确保关闭所有面板
if panel_opened: if panel_opened:
self._close_all_panels(page) self._close_all_panels(page)
time.sleep(0.5) 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: def _save_new_greet_contacts(self, *, records: List[dict], worker_id: str, account_name: str) -> int: