#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 遍历全部顶导航模块, 映射每个模块的侧边栏功能结构。 含随机 5-15s 点击间隔(反爬)。结果写入 db/menu_map.json, 进度写 db/map_progress.log """ import json, time, random, os, urllib.request DAEMON = "http://127.0.0.1:10086/command" SESSION = "2haohr" BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) OUT = os.path.join(BASE, "db", "menu_map.json") LOG = os.path.join(BASE, "db", "map_progress.log") # (模块名, 是否在"更多"下拉里, 备注) MODULES = [ ("工作台", 0), ("人事", 0), ("组织", 0), ("招聘", 0), ("考勤假期", 0), ("薪酬", 0), ("审批", 1), ("培训学习", 1), ("绩效", 1), ("社保公积金", 1), ("行政管理", 1), ("电子合同", 1), ("背景调查", 1), ("人才测评", 1), ("员工福利", 1), ("人事仪表盘", 1), ("第三方对接", 1), ("培训商城", 1), ("短信发送", 1), ("电子工资条", 1), ("智能薪酬", 1), ("个税对接", 1), ("AI员工助手", 1), ("应用管理", 1), ] def call(action, args): body = json.dumps({"action": action, "args": args, "session": SESSION}).encode() req = urllib.request.Request(DAEMON, data=body, headers={"Content-Type": "application/json"}) with urllib.request.urlopen(req, timeout=45) as resp: return json.loads(resp.read().decode()) def ev(code): try: return call("evaluate", {"code": code}).get("data", {}).get("value") except Exception as e: return "ERR:" + str(e) CLICK_TOP = """ (()=>{const name=%s; const els=[...document.querySelectorAll("a,span,div,li")].filter(e=>{ const t=(e.innerText||"").trim();const r=e.getBoundingClientRect(); return t===name && r.top<60 && r.width>8 && r.width<160;}); if(els.length){els[0].click();return "top-clicked";}return "not-found";})() """ OPEN_MORE = """ (()=>{const els=[...document.querySelectorAll("a,span,div,li")].filter(e=>{ const t=(e.innerText||"").trim();const r=e.getBoundingClientRect(); return t==="更多" && r.top<60 && r.width>8 && r.width<160;}); if(!els.length)return "no-more"; const el=els[0]; ["pointerover","mouseover","mouseenter","pointerenter"].forEach(tp=>el.dispatchEvent(new MouseEvent(tp,{bubbles:true,cancelable:true,view:window}))); el.click(); return "more-hover";})() """ CLICK_ANY = """ (()=>{const name=%s; const els=[...document.querySelectorAll("a,span,div,li")].filter(e=>(e.innerText||"").trim()===name && e.getBoundingClientRect().width>=0); if(els.length){els[els.length-1].click();return "clicked";}return "not-found";})() """ EXTRACT = r""" (()=>{ const root=document.querySelector("[class*=leftMenu]")||document.querySelector("[class*=menu_]"); const groups=[]; let cur={title:"",items:[]}; groups.push(cur); const seen=new Set(); if(root){const walk=(el)=>{for(const c of el.children){const cls=(c.className||"").toString(); if(/nameTitle/.test(cls)&&!c.closest("a")){const t=(c.innerText||"").trim();if(t){cur={title:t,items:[]};groups.push(cur);}} if(c.tagName==="A"){const h=c.getAttribute("href")||"";const t=(c.innerText||"").trim().replace(/\s+/g," "); if(h&&h.length>1&&t&&!seen.has(h)){seen.add(h);cur.items.push({name:t,href:h});}} walk(c);}}; walk(root);} return JSON.stringify({loc:location.href,groups:groups.filter(g=>g.items.length)}); })() """ def log(msg): line = time.strftime("%H:%M:%S ") + msg print(line, flush=True) with open(LOG, "a") as f: f.write(line + "\n") def main(): results = {} if os.path.exists(OUT): results = json.load(open(OUT)) for i, (mod, in_more) in enumerate(MODULES): if mod in results and results[mod].get("groups"): log(f"skip {mod} (already mapped)"); continue delay = random.uniform(5, 15) log(f"[{i+1}/{len(MODULES)}] wait {delay:.1f}s -> {mod}") time.sleep(delay) name_js = json.dumps(mod, ensure_ascii=False) if in_more: ev(OPEN_MORE); time.sleep(1.5); r = ev(CLICK_ANY % name_js) else: r = ev(CLICK_TOP % name_js) if r == "not-found": ev(OPEN_MORE); time.sleep(1.5); r = ev(CLICK_ANY % name_js) time.sleep(3.2) data = ev(EXTRACT) try: parsed = json.loads(data) except Exception: parsed = {"loc": "", "groups": [], "raw": str(data)[:200]} parsed["in_more"] = in_more parsed["click"] = r results[mod] = parsed n = sum(len(g["items"]) for g in parsed.get("groups", [])) log(f" {mod}: loc={parsed.get('loc','')} groups={len(parsed.get('groups',[]))} items={n} click={r}") json.dump(results, open(OUT, "w"), ensure_ascii=False, indent=1) log("DONE all modules") if __name__ == "__main__": main()