Chrome Automatisierung, verschiedene Techniken

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Syr2
Beiträge: 31
Registriert: 11.03.2020 13:39

Chrome Automatisierung, verschiedene Techniken

Beitrag von Syr2 »

Moin.
Ich mach's kurz: Nachdem ich mir drei Tage :shock: lang durch eine abartige Chrome-API-Dokumentation gewühlt habe :freak: , habe ich eine, zumindest bruchstrückhafte, Steuerung von Chrome hingeklatscht :allright: . Ist nicht schön, längst nicht vollständig, aber wer den Code liest versteht, wie man es richtig machen muss. Hab nix anderes sonst wo gefunden, (auch nicht außerhab der größten Comunity ever #PbFTW. Optimierungen und Weiterentwicklung erwünscht. Werde hier auch immer mal wieder reinschauen.
los gehts:

Chrome braucht Websockets, also geben wir ihm Websockets: [speichern als Websocket_Client.pb]

Code: Alles auswählen

; Websocketclient by Netzvamp
; Version: 2016/01/08
;[Von mir überarbeitet]

DeclareModule WebsocketClient
  Declare OpenWebsocketConnection(URL.s)
  Declare SendTextFrame(connection, message.s)
  Declare ReceiveFrame(connection, *MsgBuffer)
  Declare SetSSLProxy(ProxyServer.s = "", ProxyPort.l = 8182)
  
  Enumeration 1000
    #frame_text
    #frame_binary
    #frame_closing
    #frame_ping
    #frame_unknown
  EndEnumeration
  
EndDeclareModule

Module WebsocketClient
  
  ;TODO: Add function to send binary frame
  ;TODO: We don't support fragmetation right now
  ;TODO: We should send an closing frame, but server will also just close
  ;TODO: Support to send receive bigger frames
  debugmode = 0
  
Structure WebSocket
  Frametyp.i
  FrameMemory.i
EndStructure

  Declare Handshake(Connection, Servername.s, Path.s)
  Declare ApplyMasking(Array Mask.a(1), *Buffer)
  
  Global Proxy_Server.s, Proxy_Port.l
  
  Macro dbg(txt)
    If Debugmode = 1
      Debug "WebsocketClient: " + FormatDate("%yyyy-%mm-%dd %hh:%ii:%ss",Date()) + " > " + txt
    EndIf
  EndMacro
  
  Procedure SetSSLProxy(ProxyServer.s = "", ProxyPort.l = 8182)
    Proxy_Server.s = ProxyServer.s
    Proxy_Port.l = ProxyPort.l
  EndProcedure
  
  Procedure OpenWebsocketConnection(URL.s)
    Protokol.s = GetURLPart(URL.s, #PB_URL_Protocol)
    Servername.s = GetURLPart(URL.s, #PB_URL_Site)
    Port.l = Val(GetURLPart(URL.s, #PB_URL_Port))
    If Port.l = 0 : Port.l = 80 : EndIf
    Path.s = GetURLPart(URL.s, #PB_URL_Path)
    If Path.s = "" : Path.s = "/" : EndIf
    
    ;InitNetwork()
    If Protokol.s = "wss" ; If we connect with encryption (https)
      If Proxy_Port
        Connection = OpenNetworkConnection(Proxy_Server.s, Proxy_Port.l, #PB_Network_TCP, 1000)
      Else
        dbg("We need an SSL-Proxy like stunnel for encryption. Configure the proxy with SetSSLProxy().")
      EndIf
    ElseIf Protokol.s = "ws"
      Connection = OpenNetworkConnection(Servername.s, Port.l, #PB_Network_TCP, 1000)
    EndIf
    
    If Connection
      If Handshake(Connection, Servername.s, Path.s)
        dbg("Connection and Handshake ok")
        ProcedureReturn Connection
      Else
        dbg("Handshake-Error")
        ProcedureReturn #False
      EndIf
    Else
      dbg("Couldn't connect")
      ProcedureReturn #False
    EndIf
  EndProcedure
  
  Procedure Handshake(Connection, Servername.s, Path.s)
    Request.s = "GET /" + Path.s + " HTTP/1.1"+ #CRLF$ +
                "Host: " + Servername.s + #CRLF$ +
                "Upgrade: websocket" + #CRLF$ +
                "CONNECTION: UPGRADE" + #CRLF$ +
                "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" + #CRLF$ +
                "Sec-WebSocket-Version: 13" + #CRLF$ + 
                "User-Agent: CustomWebsocketClient"+ #CRLF$ + #CRLF$
                
    SendNetworkString(Connection, Request.s, #PB_UTF8)
    *Buffer = AllocateMemory(65536)
    
    ; We wait for answer
    Repeat
      Size = ReceiveNetworkData(connection, *Buffer, 65536)
      Answer.s = Answer.s + PeekS(*Buffer, Size, #PB_UTF8)
      If FindString(Answer, #CRLF$ + #CRLF$)
        Break
      EndIf
    Until Size <> 65536
    
    Answer.s = UCase(Answer.s)
    
    ; Check answer
    If FindString(Answer.s, "HTTP/1.1 101") And FindString(Answer.s, "CONNECTION: UPGRADE") And FindString(Answer.s, "UPGRADE: WEBSOCKET")
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
  EndProcedure
  
  Procedure ApplyMasking(Array Mask.a(1), *Buffer)
    For i = 0 To MemorySize(*Buffer) - 1
      PokeA(*Buffer + i, PeekA(*Buffer + i) ! Mask(i % 4))
    Next
  EndProcedure
  
  Procedure SendTextFrame(connection, message.s)
    
    ; Put String in Buffer
    MsgLength.l = StringByteLength(message.s, #PB_UTF8)
    *MsgBuffer = AllocateMemory(MsgLength)
    PokeS(*MsgBuffer, message.s, MsgLength, #PB_UTF8|#PB_String_NoZero)
    
    dbg("Messagelength to send: " + Str(MsgLength))
    
    ; The Framebuffer, we fill with senddata
    If MsgLength <= 125
      Fieldlength = 6
    ElseIf MsgLength >= 126 And MsgLength <= 65535
      Fieldlength = 8
    Else
      Fieldlength = 14
    EndIf
    
    dbg("Fieldlength to send: " + Str(Fieldlength))
    
    
    *FrameBuffer = AllocateMemory(Fieldlength + MsgLength)
    
    ; We generate 4 random masking bytes
    Dim Mask.a(3)
    Mask(0) = Random(255,0)
    Mask(1) = Random(255,0) 
    Mask(2) = Random(255,0) 
    Mask(3) = Random(255,0) 
    
    pos = 0 ; The byteposotion in the framebuffer
    
    ; First Byte: FIN(1=finished with this Frame),RSV(0),RSV(0),RSV(0),OPCODE(4 byte)=0001(text) 
    PokeB(*FrameBuffer, %10000001) : pos + 1 ; = 129
    
    ; Second Byte: Masking(1),length(to 125bytes, else we have to extend)
    If MsgLength <= 125                                             ; Length fits in first byte
      PokeA(*Framebuffer + pos, MsgLength + 128)    : pos + 1       ; + 128 for Masking
    ElseIf MsgLength >= 126 And MsgLength <= 65535                  ; We have to extend length to third byte
      PokeA(*Framebuffer + pos, 126 + 128)          : pos + 1       ; 126 for 2 extra length bytes and + 128 for Masking
      PokeA(*FrameBuffer + pos, (MsgLength >> 8))   : pos + 1       ; First Byte
      PokeA(*FrameBuffer + pos, MsgLength)          : pos + 1       ; Second Byte
    Else                                                            ; It's bigger than 65535, we also use 8 extra bytes
      PokeA(*Framebuffer + pos, 127 + 128)          : pos + 1       ; 127 for 8 extra length bytes and + 128 for Masking
      PokeA(*Framebuffer + pos, 0)                  : pos + 1       ; 8 Bytes for payload lenght. We don't support giant packages for now, so first bytes are zero :P
      PokeA(*Framebuffer + pos, 0)                  : pos + 1
      PokeA(*Framebuffer + pos, 0)                  : pos + 1
      PokeA(*Framebuffer + pos, 0)                  : pos + 1
      PokeA(*Framebuffer + pos, MsgLength >> 24)    : pos + 1
      PokeA(*Framebuffer + pos, MsgLength >> 16)    : pos + 1
      PokeA(*Framebuffer + pos, MsgLength >> 8)     : pos + 1
      PokeA(*Framebuffer + pos, MsgLength)          : pos + 1       ; = 10 Byte
    EndIf
    ; Write Masking Bytes
    PokeA(*FrameBuffer + pos, Mask(0))              : pos + 1
    PokeA(*FrameBuffer + pos, Mask(1))              : pos + 1
    PokeA(*FrameBuffer + pos, Mask(2))              : pos + 1
    PokeA(*FrameBuffer + pos, Mask(3))              : pos + 1
    
    ApplyMasking(Mask(), *MsgBuffer)
    
    CopyMemory(*MsgBuffer, *FrameBuffer + pos, MsgLength)
    
    ;For x = 0 To 100 Step 5
      ;Debug Str(PeekA(*FrameBuffer + x)) + " | " + Str(PeekA(*FrameBuffer + x + 1)) + " | " + Str(PeekA(*FrameBuffer + x + 2)) + " | " + Str(PeekA(*FrameBuffer + x + 3)) + " | " + Str(PeekA(*FrameBuffer + x + 4))
    ;Next
    
    If SendNetworkData(connection, *FrameBuffer, Fieldlength + MsgLength) = Fieldlength + MsgLength
      dbg("Textframe send, Bytes: " + Str(Fieldlength + MsgLength))
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
    
  EndProcedure
  
  Procedure ReceiveFrame(connection, *Frametyp.WebSocket)
    *FrameBuffer = AllocateMemory(65536)
    
    Repeat
      *FrameBuffer = ReAllocateMemory(*FrameBuffer, 65536)
      Size = ReceiveNetworkData(connection, *FrameBuffer, 65536)
      ;Answer.s = Answer.s + PeekS(*FrameBuffer, Size, #PB_UTF8)
    Until Size <> 65536
    dbg("Received Frame, Bytes: " + Str(Size))
    
    *FrameBuffer = ReAllocateMemory(*FrameBuffer, Size)
    
    ;     ; debug: output any single byte
    ;     If #PB_Compiler_Debugger
    ;       For x = 0 To Size - 1 Step 1
    ;         dbg_bytes.s + Str(PeekA(*FrameBuffer + x)) + " | "
    ;       Next
    ;       dbg(dbg_bytes)
    ;     EndIf
    
    ; Getting informations about package
    If PeekA(*FrameBuffer) & %10000000 > #False
      ;dbg("Frame not fragmented")
      fragmentation.b = #False
    Else
      dbg("Frame fragmented! This not supported for now!")
      fragmentation.b = #True
    EndIf
    
    ; Check for Opcodes
    If PeekA(*FrameBuffer) = %10000001 ; Textframe
      dbg("Text frame")
      frame_typ.w = #frame_text
    ElseIf PeekA(*FrameBuffer) = %10000010 ; Binary Frame
      dbg("Binary frame")
      frame_typ.w = #frame_binary
    ElseIf PeekA(*FrameBuffer) = %10001000 ; Closing Frame
      dbg("Closing frame")
      frame_typ.w = #frame_closing
    ElseIf PeekA(*FrameBuffer) = %10001001 ; Ping
      ; We just answer pings
      *pongbuffer = AllocateMemory(2)
      PokeA(*pongbuffer, 138)
      PokeA(*pongbuffer+1, 0)
      SendNetworkData(connection, *pongbuffer, 2)
      dbg("Received Ping, answered with Pong")
      frame_typ.w = #frame_ping
      FreeMemory(*pongbuffer)
      FreeMemory  (*FrameBuffer)
      ProcedureReturn
    Else
      dbg("Opcode unknown")
      frame_typ.w = #frame_unknown
      FreeMemory(*FrameBuffer)
      ProcedureReturn #False
    EndIf
    
    ; Check masking
    If PeekA(*FrameBuffer + 1) & %10000000 = 128 : masking.b = #True : Else : masking.b = #False : EndIf
    
    dbg("Masking: " + Str(masking))
    
    pos.l = 1
    
    ; check size
    If PeekA(*FrameBuffer + 1) & %01111111 <= 125 ; size is in this byte
      frame_size.l = PeekA(*FrameBuffer + pos) & %01111111 : pos + 1
    ElseIf PeekA(*FrameBuffer + 1) & %01111111 >= 126 ; Size is in 2 extra bytes
      frame_size.l = PeekA(*FrameBuffer + 2) << 8 + PeekA(*FrameBuffer + 3) : pos + 2
    EndIf
    dbg("FrameSize: " + Str(frame_size.l))
    
    If masking = #True
      Dim Mask.a(3)
      Mask(0) = PeekA(*FrameBuffer + pos) : pos + 1
      Mask(1) = PeekA(*FrameBuffer + pos) : pos + 1
      Mask(2) = PeekA(*FrameBuffer + pos) : pos + 1
      Mask(3) = PeekA(*FrameBuffer + pos) : pos + 1
      
    *Frametyp\FrameMemory = ReAllocateMemory(*Frametyp\FrameMemory  ,frame_size)
    
      CopyMemory(*FrameBuffer + pos, *Frametyp\FrameMemory, frame_size)
      
      ApplyMasking(Mask(), *Frametyp\FrameMemory)
      
    FreeArray(Mask())
    
    Else
      *Frametyp\FrameMemory = ReAllocateMemory(*Frametyp\FrameMemory,frame_size+1)
      CopyMemory(*FrameBuffer + pos, *Frametyp\FrameMemory, frame_size)
    EndIf
    
    FreeMemory(*FrameBuffer)
    ProcedureReturn frame_typ
    
  EndProcedure
  
EndModule


CompilerIf #PB_Compiler_IsMainFile
  
  
Structure WebSocket
  Frametyp.i
  FrameMemory.i
EndStructure

  ; Minimal example to send and receive textmessages
  ; The preconfigured testserver "echo.websocket.org" will just echo back everything you've send.
  
  XIncludeFile "module_websocketclient_gui.pbf"
  
  Global connection
  
  Procedure gui_button_connect(EventType)
    If EventType = #PB_EventType_LeftClick
      Debug "Connect clicked"
      ;connection = WebsocketClient::OpenWebsocketConnection(GetGadgetText(#String_url))
      connection = WebsocketClient::OpenWebsocketConnection(GetGadgetText(#String_url))
      
      AddGadgetItem(#ListView_Output, -1, "# Connected to " + GetGadgetText(#String_url))
    EndIf
  EndProcedure
  
  Procedure gui_button_send(EventType)
      Debug "Send clicked"
      If WebsocketClient::SendTextFrame(connection, GetGadgetText(#String_send)) = #False
        Debug "Couldn't send. Are we disconnected?"
      Else
        AddGadgetItem(#ListView_Output, -1, "> " + GetGadgetText(#String_send))
      EndIf
  EndProcedure
  
  OpenWindow_Websocketclient()
  
  ; Proxy Setting:
  ; If you need an encyrpted connection (https/wss), you currently have to use an 
  ; proxy software like stunnel (https://www.stunnel.org) to redirect unencrypted data into an encrypted connection
  ; Example stunnel.conf section:
  ;   [websocket]
  ;   client = yes
  ;   accept = 127.0.0.1:8182
  ;   connect = echo.websocket.org:443
  WebsocketClient::SetSSLProxy("127.0.0.1",8182)
  
  Repeat
    
    If Window_Websocketclient_Events( WaitWindowEvent(1) ) = #False : End : EndIf
    
    If connection
      
      NetworkEvent = NetworkClientEvent(connection)
 
      Select NetworkEvent
          
        Case #PB_NetworkEvent_Data
          Debug "data coming"
          Frametyp.Websocket\FrameMemory = AllocateMemory(1)
          Frametyp\Frametyp = WebsocketClient::ReceiveFrame(connection,@Frametyp)
          If Frametyp\Frametyp = WebsocketClient::#frame_text
            Debug MemorySize(Frametyp\FrameMemory)
            Packet$ = PeekS(Frametyp\FrameMemory,MemoryStringLength(Frametyp\FrameMemory,#PB_UTF8)-1,#PB_UTF8)
            Debug Packet$
          ElseIf Frametyp\Frametyp = WebsocketClient::#frame_binary
            Debug "Received Binaryframe"
          EndIf
          FreeMemory(Frametyp\FrameMemory)
        Case #PB_NetworkEvent_Disconnect
          If disconnected = #False
            Debug "Disconnected"
          EndIf
          disconnected = #True
          NetworkEvent = #PB_NetworkEvent_None
          
        Case #PB_NetworkEvent_None
          Debug "idle"
      EndSelect
      
    EndIf
  ForEver
  
CompilerEndIf
Jetzt der eigentliche Spass:

Code: Alles auswählen

InitNetwork()
#SystemProcessInformation = $0005

IncludeFile "Websocket_Client.pb"

Structure WebSocket
  Frametyp.i
  FrameMemory.i
EndStructure

Structure _UNICODE_STRING Align #PB_Structure_AlignC
  usLength.w 
  usMaximumLength.w   
  usBuffer.i
EndStructure

Structure _SYSTEM_PROCESS_INFO Align #PB_Structure_AlignC
  NextEntryOffset.l
  NumberOfThreads.l
  Reserved.q[3]
  CreateTime.q
  UserTime.q
  KernelTime.q
  ImageName._UNICODE_STRING
  BasePriority.l
  ProcessId.i
  InheritedFromProcessId.i
EndStructure

Structure co
  description.s
  devtoolsFrontendUrl.s
  title.s
  type.s
  url.s
  webSocketDebuggerUrl.s
  WsConnection.i
EndStructure
Structure vs
  Browser.s
  ProtocolVersion.s
  UserAgent.s
  V8Version.s
  WebKitVersion.s
  webSocketDebuggerUrl.s
EndStructure

Structure All
  Map Objects.co()
  Version.vs
  RequestId.i
EndStructure


Global DebugPort = 9222
Global Chrome.All

Procedure KillProcess (pid)
    phandle = OpenProcess_ (#PROCESS_TERMINATE, #False, pid)
    If phandle <> #Null
        If TerminateProcess_ (phandle, 1)
            result = #True
        EndIf
        CloseHandle_ (phandle)
    EndIf
    ProcedureReturn result
  EndProcedure
Procedure CloseAllProcess(name.s)
  Define dwlen, *Buffer, *SPI._SYSTEM_PROCESS_INFO
NtQuerySystemInformation_(#SystemProcessInformation, 0, 0, @dwlen)
If dwlen
  dwlen * 2
  *Buffer = AllocateMemory(dwlen)
  If *Buffer
    If NtQuerySystemInformation_(#SystemProcessInformation, *Buffer, dwlen, @dwlen) = #ERROR_SUCCESS
      
      *SPI = *Buffer
      
      While *SPI\NextEntryOffset
        
        If *SPI\ImageName\usBuffer
          If PeekS(*SPI\ImageName\usBuffer, -1, #PB_Unicode) = name
            KillProcess(*SPI\ProcessId)
         EndIf
        EndIf
        *SPI + *SPI\NextEntryOffset
      Wend
      
    EndIf
    FreeMemory(*Buffer)
  EndIf
EndIf


EndProcedure

;Internal Functions
Procedure   ChromeDefaultObjectAdd(JsonObject)
  If JSONType(JsonObject) = #PB_JSON_Object 
    id.s = GetJSONString(GetJSONMember(JsonObject,"id"))
    Chrome\Objects(id)\description =  GetJSONString(GetJSONMember(JsonObject,"description"))
    Chrome\Objects(id)\devtoolsFrontendUrl =  GetJSONString(GetJSONMember(JsonObject,"devtoolsFrontendUrl"))
    Chrome\Objects(id)\title =  GetJSONString(GetJSONMember(JsonObject,"title"))
    Chrome\Objects(id)\type =  GetJSONString(GetJSONMember(JsonObject,"type"))
    Chrome\Objects(id)\url =  GetJSONString(GetJSONMember(JsonObject,"url"))
    Chrome\Objects(id)\webSocketDebuggerUrl =  GetJSONString(GetJSONMember(JsonObject,"webSocketDebuggerUrl"))
  EndIf
EndProcedure
Procedure   ChromeDefaultJson(json.i,Method.s)
  mainobj = SetJSONObject(JSONValue(json))
  SetJSONInteger(AddJSONMember(mainobj, "id"), Chrome\RequestId) : Chrome\RequestId = Chrome\RequestId+1
  SetJSONString(AddJSONMember(mainobj, "method"), Method)
  params = AddJSONMember(mainobj,"params")
  SetJSONObject(params)
  ProcedureReturn params
EndProcedure
Procedure   Chrome_OpenWebSocket(TabID.s)
  Chrome\Objects(TabID)\WsConnection = WebsocketClient::OpenWebsocketConnection(Chrome\Objects(TabID)\webSocketDebuggerUrl)
  ProcedureReturn #True
EndProcedure
Procedure.s Chrome_GetWebsocketResponse(connection,timeout = 0)
  p = ElapsedMilliseconds()
  Packet$ = ""
  Repeat
    Delay(1)
    If connection
      NetworkEvent = NetworkClientEvent(connection)
      Select NetworkEvent
        Case #PB_NetworkEvent_Data
          Frametyp.Websocket\FrameMemory = AllocateMemory(1)
          Frametyp\Frametyp = WebsocketClient::ReceiveFrame(connection,@Frametyp)
          If Frametyp\Frametyp = WebsocketClient::#frame_text
            Packet$ + PeekS(Frametyp\FrameMemory,MemoryStringLength(Frametyp\FrameMemory,#PB_UTF8)-1,#PB_UTF8)
           ElseIf Frametyp\Frametyp = WebsocketClient::#frame_binary
             Debug "Received Binaryframe"
          EndIf
          FreeMemory(Frametyp\FrameMemory)
          p = ElapsedMilliseconds()
        Case #PB_NetworkEvent_Disconnect
          ProcedureReturn ""
        Case #PB_NetworkEvent_None
          If Len(Packet$) > 0
            If Not timeout
              ProcedureReturn Packet$
            EndIf
          EndIf
      EndSelect
    Else
      Debug "Connection lost?"
      Break
    EndIf
  Until ElapsedMilliseconds() - p > timeout And timeout > 0
  ProcedureReturn Packet$
EndProcedure

;Fuctions from HTTP-Endpoints
Procedure   Chrome_List()
   HttpRequest = HTTPRequestMemory(#PB_HTTP_Get, "localhost:"+Str(DebugPort)+"/json/list")
  If HttpRequest
    Response$ = HTTPInfo(HTTPRequest, #PB_HTTP_Response)
    FinishHTTP(HTTPRequest)
  Else
    ProcedureReturn -1
  EndIf
  json = ParseJSON(#PB_Any,Response$)
  If json
    If JSONType(JSONValue(json)) = #PB_JSON_Array
      For i = 0 To JSONArraySize(JSONValue(json)) - 1
        Element = GetJSONElement(JSONValue(json), i)
        ChromeDefaultObjectAdd(Element)
      Next
    EndIf
    FreeJSON(json)
  EndIf
EndProcedure
Procedure   Chrome_GetVersion()
  HttpRequest = HTTPRequestMemory(#PB_HTTP_Get, "localhost:"+Str(DebugPort)+"/json/version")
  If HttpRequest
    Response$ = HTTPInfo(HTTPRequest, #PB_HTTP_Response)
    FinishHTTP(HTTPRequest)
  Else
    ProcedureReturn -1
  EndIf
  
    json = ParseJSON(#PB_Any,Response$)
    If json
      value = JSONValue(json)
      If JSONType(value) = #PB_JSON_Object
        Chrome\Version\Browser=  GetJSONString(GetJSONMember(value,"Browser"))
        Chrome\Version\ProtocolVersion=  GetJSONString(GetJSONMember(value,"Protocol-Version"))
        Chrome\Version\UserAgent=  GetJSONString(GetJSONMember(value,"User-Agent"))
        Chrome\Version\V8Version=  GetJSONString(GetJSONMember(value,"V8-Version"))
        Chrome\Version\WebKitVersion=  GetJSONString(GetJSONMember(value,"WebKit-Version"))
        Chrome\Version\webSocketDebuggerUrl=  GetJSONString(GetJSONMember(value,"webSocketDebuggerUrl"))
      EndIf
    EndIf
    FreeJSON(json)
EndProcedure
Procedure.s Chrome_NewTab();Returns ID
  HttpRequest = HTTPRequestMemory(#PB_HTTP_Get, "localhost:"+Str(DebugPort)+"/json/new")
  If HttpRequest
    Response$ = HTTPInfo(HTTPRequest, #PB_HTTP_Response)
    FinishHTTP(HTTPRequest)
  Else
    ProcedureReturn ""
  EndIf
  json = ParseJSON(#PB_Any,Response$)
  If json
    ChromeDefaultObjectAdd(JSONValue(json))
    FreeJSON(json)
  EndIf
  ProcedureReturn MapKey(Chrome\Objects())
EndProcedure
Procedure   Chrome_ActivateTab(TabID.s)
  HttpRequest = HTTPRequestMemory(#PB_HTTP_Get, "localhost:"+Str(DebugPort)+"/json/activate/"+TabID)
  If HttpRequest
    Response$ = HTTPInfo(HTTPRequest, #PB_HTTP_Response)
    FinishHTTP(HTTPRequest)
  Else
    ProcedureReturn -1
  EndIf
  If Response$ = "Target activated"
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure
Procedure   Chrome_CloseTab(TabID.s)
  HttpRequest = HTTPRequestMemory(#PB_HTTP_Get, "localhost:"+Str(DebugPort)+"/json/close/"+TabID)
  If HttpRequest
    Response$ = HTTPInfo(HTTPRequest, #PB_HTTP_Response)
    FinishHTTP(HTTPRequest)
  Else
    ProcedureReturn -1
  EndIf
  If Response$ = "Target is closing"
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure


; Functions for Websocket
; More commands can be found here(also browse left nav-bar):
; https://chromedevtools.github.io/devtools-protocol/tot/Browser/

Structure PageNavigateReturn
  frameId.s
  loaderId.s
  errorText.s
EndStructure

Procedure.i Chrome_PageNavigate(TabID.s,*Returnvalue.PageNavigateReturn,url.s,referrer.s="",transitionType.i=-1,frameId.i=-1,referrerPolicy.i=-1)
  json = CreateJSON(#PB_Any) 
  params = ChromeDefaultJson(json,"Page.navigate");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  
  ;Page.navigate Parameters
  SetJSONString(AddJSONMember(params, "url"), url)
  If Len(referrer ) > 0  : SetJSONString(AddJSONMember(params, "referrer"), referrer) :EndIf
  If transitionType > 0  : SetJSONInteger(AddJSONMember(params, "transitionType"), transitionType) :EndIf
  If frameId > 0  : SetJSONInteger(AddJSONMember(params, "frameId"), frameId) :EndIf
  If referrerPolicy > 0  : SetJSONInteger(AddJSONMember(params, "referrerPolicy"), referrerPolicy) :EndIf
  request$ = ComposeJSON(json):FreeJSON(json)
  ;Hau raus die Scheisse
  WebsocketClient::SendTextFrame(Chrome\Objects(TabID)\WsConnection,request$)
  Chrome_GetWebsocketResponse(Chrome\Objects(TabID)\WsConnection)
  
EndProcedure
Procedure.s Chrome_DOMenable(TabID.s,enable)
  json = CreateJSON(#PB_Any) 
  
  If enable
    params = ChromeDefaultJson(json,"DOM.enable");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  Else
    params = ChromeDefaultJson(json,"DOM.disable");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  EndIf
      
  request$ = ComposeJSON(json):FreeJSON(json)
  WebsocketClient::SendTextFrame(Chrome\Objects(TabID)\WsConnection,request$)
EndProcedure
Procedure Chrome_DOMgetDocument(TabID.s,depth.i=-1,pierce.b=0) ;NOT WORKING
  json = CreateJSON(#PB_Any) 
  
  params = ChromeDefaultJson(json,"DOM.getDocument");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
   SetJSONInteger(AddJSONMember(params, "depth"), depth)
;   SetJSONInteger(AddJSONMember(params, "pierce"), pierce)
   
  Debug Chrome_GetWebsocketResponse(Chrome\Objects(TabID)\WsConnection,200)
  request$ = ComposeJSON(json):FreeJSON(json)
  WebsocketClient::SendTextFrame(Chrome\Objects(TabID)\WsConnection,request$)
  Result$ = Chrome_GetWebsocketResponse(Chrome\Objects(TabID)\WsConnection,5000)
  Debug Result$
  nodeID = Val(StringField(Mid(Result$,FindString(Result$,"nodeId"+Chr(34)+":")+8),1,","))
  
  ProcedureReturn NodeID ; I only want the root nodeID
EndProcedure
Procedure.s Chrome_DOMgetOuterHTML(TabID.s,NodeId.i=-1,BackendNodeId=-1,RuntomeRometObjectID.i=-1)
  json = CreateJSON(#PB_Any) 
  
  params = ChromeDefaultJson(json,"DOM.getOuterHTML");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  If NodeID > -1
    SetJSONInteger(AddJSONMember(params, "nodeId"), NodeId)
  EndIf
  
  request$ = ComposeJSON(json):FreeJSON(json)
  WebsocketClient::SendTextFrame(Chrome\Objects(TabID)\WsConnection,request$)
  Debug Chrome_GetWebsocketResponse(Chrome\Objects(TabID)\WsConnection,500)
  
EndProcedure
Procedure.s Chrome_Runtimeenable(TabID.s,enable)
  json = CreateJSON(#PB_Any) 
  
  If enable
    params = ChromeDefaultJson(json,"Runtime.enable");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  Else
    params = ChromeDefaultJson(json,"Runtime.disable");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  EndIf
  request$ = ComposeJSON(json):FreeJSON(json)
  WebsocketClient::SendTextFrame(Chrome\Objects(TabID)\WsConnection,request$)
EndProcedure
Procedure.s Chrome_Logenable(TabID.s,enable)
  json = CreateJSON(#PB_Any) 
  
  If enable
    params = ChromeDefaultJson(json,"Log.enable");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  Else
    params = ChromeDefaultJson(json,"Log.disable");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  EndIf
  request$ = ComposeJSON(json):FreeJSON(json)
  WebsocketClient::SendTextFrame(Chrome\Objects(TabID)\WsConnection,request$)
EndProcedure
Procedure.s Chrome_Runtimeevaluate(TabID.s,expression.s)
  If Len(expression) = 0
    ProcedureReturn ""
  EndIf
  
  json = CreateJSON(#PB_Any) 
  params = ChromeDefaultJson(json,"Runtime.evaluate");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  SetJSONString (AddJSONMember(params, "expression"), expression)
  
  request$ = ComposeJSON(json):FreeJSON(json)
  WebsocketClient::SendTextFrame(Chrome\Objects(TabID)\WsConnection,request$)
  
EndProcedure
Procedure Chrome_BrowsersetDownloadBehavior(behaviour.s,browserContextId=-1,downloadPath.s="",eventsEnabled=-1)
  ;Not working?
  If Len(behaviour) = 0
    ProcedureReturn 0
  EndIf
  
  json = CreateJSON(#PB_Any) 
  params = ChromeDefaultJson(json,"Browser.setDownloadBehavior");erstelle schonmal en JSON mit Requestid(ein counter) und Methode
  SetJSONString (AddJSONMember(params, "behaviour"), behaviour)
  If browserContextId > -1 : SetJSONInteger (AddJSONMember(params, "browserContextId"), browserContextId):EndIf
  If Len(downloadPath) > -1 : SetJSONString (AddJSONMember(params, "downloadPath"), downloadPath):EndIf
  If eventsEnabled > -1 : SetJSONInteger (AddJSONMember(params, "eventsEnabled"), eventsEnabled):EndIf
  
  request$ = ComposeJSON(json):FreeJSON(json)
  
EndProcedure
Procedure.s Chrome_GetHMTL(TabID.s)

; Bishierhin war noch alles gut. Jetzt kommt Dreck. Ausleiten des HTML als Download...
Filename$ = "Controller_Download_Text"
Filepath.s = GetUserDirectory(#PB_Directory_Downloads)+Filename$ +".txt"

  If FileSize(Filepath) > 0
    DeleteFile(Filepath)
  EndIf
  
  Chrome_Runtimeevaluate(TabID,"const data = new XMLSerializer().serializeToString(document); const a = document.createElement('a');const blob = new Blob([JSON.stringify(data)]);a.href = URL.createObjectURL(blob);a.download = '"+Filename$+"';a.click();")
  Delay(600);min 450
  
  ;Noch am schreiben?
  Repeat
    Delay(10)
    f = ReadFile(#PB_Any,Filepath)
  Until f
  content$ = ReadString(f,#PB_UTF8,FileSize(Filepath))

CloseFile(f)
DeleteFile(Filepath)
ProcedureReturn content$
EndProcedure

Procedure Chrome_JS_ClickOnButtonByID(TabID.s,ButtonID.s)
  Chrome_Runtimeevaluate(TabID,"document.getElementById('"+ButtonID+"').click();")
EndProcedure
Procedure Chrome_JS_ClickOnButtonByClass(TabID.s,Classname.s)
  Chrome_Runtimeevaluate(TabID,"document.querySelector('."+ReplaceString(Classname," ",".")+"').click();")
EndProcedure

Beispiel (einfach unter den Code drunter klatschen und Feuer frei)

Code: Alles auswählen

;Ka wie das Messaging funktioniert. Daher arbeite ich nur mit Funktionen und Rückgabewerten. Ausleiten von Console und allgemeinen Events fehlt daher.
;Kill all other Chrome Tabs (important to kill tabs, not process)
CloseAllProcess("chrome.exe")

;Start new one
Phwnd = RunProgram("chrome.exe","--remote-debugging-port="+DebugPort,GetCurrentDirectory())

;{ Wait for start
Repeat
  conn = OpenNetworkConnection("127.0.0.1",9222)
  If conn
    Break
  EndIf
  Delay(100)
ForEver
CloseNetworkConnection(conn)
;}

 Chrome_List()
;{ Close all Extentions and other tabs
; ForEach Chrome\Objects()
;   If Chrome\Objects()\type = "page"
;     Continue
;   Else
;     Chrome_CloseTab(MapKey(Chrome\Objects()))
;   EndIf
; Next
; Chrome_List()
;}

;{ Get first page ID
ForEach Chrome\Objects()
  If Chrome\Objects()\type = "page"
   Break
 EndIf
Next
;}

ID.s = MapKey(Chrome\Objects())
Chrome_OpenWebSocket(ID)

Chrome_Runtimeenable(ID,1)
Chrome_PageNavigate(ID,@ReturnValue.PageNavigateReturn,"https://www.w3schools.com/tags/tag_button.asp")

Delay(1000)

Chrome_JS_ClickOnButtonByID(ID,"w3loginbtn")
Delay(5000)

HTML$ = Chrome_GetHMTL(ID)
Debug HTML$

CloseNetworkConnection(Chrome\Objects()\WsConnection)

Lieferumfang:
Per HTTP-Endpoints
Tabs Öffnen, schliessen, fokusieren, Liste mit allen Tabs und Objekten bekommen
Per Process-Control
Chrome im debug-modus starten, "--headless" könnte man auch noch nehmen, dann wäre der Spass auch noch unsichtbar m Hintergrund. (Was eigentlich der ganze Sinn der Sache ist)
Per Websocket & Chromedevtools
Siehe: chromedevtools.github.io
Implementiert und getestet hab ich: Page.Navigate,DOM.enable,DOMget.OuterHTML,Runtime.Evaluate [weitere Funktionen einbauen ist ein Klacks, orientiert euch einfach anderen]
Per Javascript -> Websocket
Button-clilck über ID oder Classname
HTML ausleiten
Ganz behindert, über ein JS-Befehl der das Ding runterlädt. Danach lese ich die Datei ein *sorry*. Bitte um Verbesserung!
Gruß
Syr2
Syr2
Beiträge: 31
Registriert: 11.03.2020 13:39

Re: Chrome Automatisierung, verschiedene Techniken

Beitrag von Syr2 »

Für alle die Updates wollen, das ganze ist inzwische in meine Mega-Lib aufgegangen:
https://github.com/Syr0/Joe-s-Purebasic-Automation-Lib
gl & hf
Antworten