#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 映射某个顶导航模块的侧边栏功能结构。 用法: python3 map_menu.py "<模块名>" [in_more] in_more: 传 1 表示该模块在"更多"下拉里, 需先展开 输出: JSON {module, loc, groups:[{title, items:[{name,href}]}], flat:[...]} """ import sys, json, time, urllib.request DAEMON = "http://127.0.0.1:10086/command" SESSION = "2haohr" 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=40) as resp: return json.loads(resp.read().decode()) def ev(code): return call("evaluate", {"code": code}).get("data", {}).get("value") 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>8); 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_]"); if(!root)return JSON.stringify({loc:location.href,groups:[],flat:[]}); const groups=[]; let cur={title:"",items:[]}; groups.push(cur); const seen=new Set(); 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); const flat=[]; groups.forEach(g=>g.items.forEach(it=>flat.push({group:g.title,name:it.name,href:it.href}))); return JSON.stringify({loc:location.href,groups:groups.filter(g=>g.items.length),flat}); })() """ def main(): module = sys.argv[1] in_more = len(sys.argv) > 2 and sys.argv[2] == "1" name_js = json.dumps(module, ensure_ascii=False) if in_more: print("more:", ev(OPEN_MORE)); time.sleep(1.2) print("click:", ev(CLICK_ANY % name_js)) else: r = ev(CLICK_TOP % name_js) print("top:", r) if r == "not-found": print("more:", ev(OPEN_MORE)); time.sleep(1.2) print("click:", ev(CLICK_ANY % name_js)) time.sleep(3.0) data = ev(EXTRACT) print(data) if __name__ == "__main__": main()