Files
2026-09-03 15:35:23 +08:00

87 lines
3.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""干净验证:报表页点下载 -> 捕获CDN直链 -> 页面内同步XHR取字节 -> base64回传 -> 落盘。"""
import json, urllib.request, base64, time, os
D = "http://127.0.0.1:10086/command"; S = "2haohr"
def call(a, args, timeout=120):
b = json.dumps({"action": a, "args": args, "session": S}).encode()
r = urllib.request.Request(D, data=b, headers={"Content-Type": "application/json"})
return json.loads(urllib.request.urlopen(r, timeout=timeout).read().decode())
def ev(code):
return call("evaluate", {"code": code}).get("data", {}).get("value")
# 单一、幂等的 hook(navigate 后页面重载,hook 会被清)
HOOK = r"""(()=>{
if(window.__H)return 'already';
window.__H=1; window.__cap=[];
const ac=HTMLAnchorElement.prototype.click;
HTMLAnchorElement.prototype.click=function(){
try{const h=this.href||'';const d=this.getAttribute('download')||'';
if(h && (/prv-cdn|attname=|\.xlsx|\.xls|\.docx|\.doc|\.zip|\.pdf/i.test(h)||d))
window.__cap.push(h);}catch(e){}
return ac.apply(this,arguments);
};
return 'hooked';
})()"""
CLICK_DL = r"""(()=>{const cand=['直接下载','下载','生成excel','导出Excel','导出'];
for(const t of cand){const b=[...document.querySelectorAll('button,a,[role=button]')]
.find(x=>(x.innerText||'').trim()===t && x.getBoundingClientRect().width>0);
if(b){b.click();return t;}}return 'no-btn';})()"""
# 弹窗里确认导出(选择部门后 生成excel/确定/下载)
CONFIRM = r"""(()=>{const cand=['生成excel','确定','下载','导出','确认'];
const ms=[...document.querySelectorAll('[class*=modal],[class*=Modal],[role=dialog],[class*=dialog],[class*=drawer]')];
for(const m of ms){const r=m.getBoundingClientRect();
if(r.width>200&&r.height>100&&getComputedStyle(m).display!=='none'){
for(const t of cand){const b=[...m.querySelectorAll('button,a')]
.find(x=>(x.innerText||'').trim()===t);
if(b){b.click();return 'modal:'+t;}}}}return 'no-modal';})()"""
FETCH_B64 = r"""(()=>{try{
if(!window.__cap.length)return 'NO_HREF';
const u=window.__cap[window.__cap.length-1];
const x=new XMLHttpRequest();x.open('GET',u,false);
x.overrideMimeType('text/plain;charset=x-user-defined');x.send();
if(x.status!==200)return 'HTTP'+x.status;
const s=x.responseText;let out='';const CH=8192;
for(let i=0;i<s.length;i+=CH){let c='';const e=Math.min(i+CH,s.length);
for(let j=i;j<e;j++)c+=String.fromCharCode(s.charCodeAt(j)&0xff);out+=c;}
window.__b64=btoa(out);
const m=u.match(/attname=([^&]+)/);let name=m?decodeURIComponent(m[1]):'file';
return JSON.stringify({size:s.length,name:name,url:u.slice(0,60)});
}catch(e){return 'EX '+e.message;}})()"""
def main():
call("find_tab", {"url": "2haohr.com"})
call("navigate", {"url": "https://2haohr.com/personnel/employee/personnel-report/home", "newTab": False})
# 轮询等下载按钮出现
COUNT = r"""(()=>{let n=0;document.querySelectorAll('button,a,[role=button]').forEach(x=>{const t=(x.innerText||'').trim();if(['直接下载','下载','生成excel','导出Excel','导出'].includes(t)&&x.getBoundingClientRect().width>0)n++;});return n;})()"""
for i in range(15):
time.sleep(2)
c = ev(COUNT)
print(" wait btn count=", c)
if isinstance(c, int) and c > 0:
break
print("hook:", ev(HOOK))
print("click:", ev(CLICK_DL))
time.sleep(2.5)
print("confirm:", ev(CONFIRM))
time.sleep(2.5)
meta = ev(FETCH_B64)
print("fetch:", meta)
if isinstance(meta, str) and meta.startswith("{"):
info = json.loads(meta)
b64 = ev("window.__b64")
data = base64.b64decode(b64)
os.makedirs("downloads/_test", exist_ok=True)
out = "downloads/_test/probe_" + info["name"]
open(out, "wb").write(data)
print("落盘:", out, len(data), "bytes")
if __name__ == "__main__":
main()