81 lines
3.3 KiB
Python
81 lines
3.3 KiB
Python
import asyncio, json, urllib.request, sys
|
|
|
|
URL = sys.argv[1] if len(sys.argv) > 1 else 'https://example.com'
|
|
|
|
async def recv_one(reader):
|
|
b1 = await reader.readexactly(2)
|
|
opcode = b1[0] & 0x0F
|
|
masked_len = b1[1]
|
|
length = masked_len & 0x7F
|
|
if length == 126:
|
|
length = int.from_bytes(await reader.readexactly(2), 'big')
|
|
elif length == 127:
|
|
length = int.from_bytes(await reader.readexactly(8), 'big')
|
|
if masked_len & 0x80:
|
|
mask = await reader.readexactly(4)
|
|
payload = await reader.readexactly(length) if length else b''
|
|
payload = bytes(b ^ mask[i % 4] for i, b in enumerate(payload))
|
|
else:
|
|
payload = await reader.readexactly(length) if length else b''
|
|
return opcode, payload
|
|
|
|
async def send_text(writer, obj):
|
|
data = json.dumps(obj).encode()
|
|
mask = b'ABCD'
|
|
frame = bytearray([0x81])
|
|
n = len(data)
|
|
if n < 126:
|
|
frame.append(0x80 | n)
|
|
elif n < (1<<16):
|
|
frame.append(0x80 | 126); frame += n.to_bytes(2,'big')
|
|
else:
|
|
frame.append(0x80 | 127); frame += n.to_bytes(8,'big')
|
|
frame += mask + bytes(b ^ mask[i % 4] for i,b in enumerate(data))
|
|
writer.write(frame); await writer.drain()
|
|
|
|
async def main():
|
|
ws = json.load(urllib.request.urlopen('http://127.0.0.1:9222/json/version'))['webSocketDebuggerUrl']
|
|
hp = ws[len('ws://'):]
|
|
hostport, _, path = hp.partition('/')
|
|
host, port = hostport.split(':'); port = int(port); path='/' + path
|
|
reader, writer = await asyncio.open_connection(host, port)
|
|
req = f'GET {path} HTTP/1.1\r\nHost: {host}:{port}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n'
|
|
writer.write(req.encode()); await writer.drain(); await reader.readuntil(b'\r\n\r\n')
|
|
|
|
next_id = 1
|
|
async def call(method, params=None, sessionId=None, timeout=15):
|
|
nonlocal next_id
|
|
cid = next_id; next_id += 1
|
|
msg = {'id': cid, 'method': method}
|
|
if params is not None: msg['params'] = params
|
|
if sessionId is not None: msg['sessionId'] = sessionId
|
|
await send_text(writer, msg)
|
|
while True:
|
|
op, payload = await asyncio.wait_for(recv_one(reader), timeout=timeout)
|
|
if op != 1:
|
|
continue
|
|
data = json.loads(payload.decode(errors='ignore'))
|
|
if data.get('id') == cid:
|
|
return data
|
|
|
|
tid = (await call('Target.createTarget', {'url':'about:blank'}))['result']['targetId']
|
|
sid = (await call('Target.attachToTarget', {'targetId': tid, 'flatten': True}))['result']['sessionId']
|
|
await call('Page.enable', sessionId=sid)
|
|
await call('Runtime.enable', sessionId=sid)
|
|
await call('Page.navigate', {'url': URL}, sessionId=sid)
|
|
await asyncio.sleep(5)
|
|
exprs = {
|
|
'title': 'document.title',
|
|
'url': 'location.href',
|
|
'anchors': 'document.querySelectorAll("a").length',
|
|
'body': 'document.body ? document.body.innerText.slice(0,1200) : "NO_BODY"'
|
|
}
|
|
out = {}
|
|
for k,e in exprs.items():
|
|
r = await call('Runtime.evaluate', {'expression': e, 'returnByValue': True}, sessionId=sid)
|
|
out[k] = r['result']['result'].get('value')
|
|
print(json.dumps(out, ensure_ascii=False, indent=2))
|
|
writer.close(); await writer.wait_closed()
|
|
|
|
asyncio.run(main())
|