From 33adb097f79d775221b53a9c412de7393ab5ed1a Mon Sep 17 00:00:00 2001 From: Pedro de Oliveira Guedes Date: Tue, 11 Jan 2022 12:37:48 -0300 Subject: [PATCH] Adding the chrome client for tests. --- api/anticaptcha/chrome.py | 324 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 api/anticaptcha/chrome.py diff --git a/api/anticaptcha/chrome.py b/api/anticaptcha/chrome.py new file mode 100644 index 0000000..69e278e --- /dev/null +++ b/api/anticaptcha/chrome.py @@ -0,0 +1,324 @@ +import json +import os +import urllib +from typing import List + +import requests + +class Chrome: + """ + ## Instância do Chrome + --- + Esta classe utiliza o serviço "svc_chrome.exe" para fazer manipulações no navegador. + + Ela mapeia o robo através da propriedade rid. + + "EP" significa endpoint, e aponta para o endereco na web on se hostea o serviço, por padrão é: + + https://localhost:8443 + """ + + ep: str = "" + + def __init__(self): + self.ep = "https://localhost:8443" + + def __request_get__(self, data: str): + + """ + ## HTTP GET + --- + Este método é responsável por realizar requisições HTTP do tipo GET. + + Ele retorna o corpo de resposta da requisição, ou uma mensagem de erro, que indica qual foi a irregularidade ocorrida ao chamar a API. + """ + + url = self.ep + data + print("Calling: " + url) + + apikey = os.environ.get('REPLAY_APIKEY') + headers = {"X-API-KEY": apikey} + res = requests.get(url, headers=headers, verify=False) + + if res.status_code >= 400: + raise Exception(f"HTTP ERROR: {str(res.status_code)} - {res.text}") + if res.headers.get("Content-Type") != None and res.headers.get("Content-Type").find("json") != -1: + return json.loads(res.text) + else: + return res.text + + def __request_json_get__(self, data: str): + + """ + ## HTTP JSON GET + --- + Este método é responsável por realizar requisições HTTP do tipo GET para objetos JSON. + + Ele retorna o corpo de resposta da requisição, ou uma mensagem de erro, que indica qual foi a irregularidade ocorrida ao chamar a API. + + --- + ###### Obs: É uma função provisória, feita exclusivamente para o método open_tabs, já que o HEADER do corpo de resposta da requisição tem o atributo "Content-Type" com o valor "text/plain", ao invés de "application/json" como deveria ser. + """ + + url = self.ep + data + print("Calling: " + url) + + apikey = os.environ.get('REPLAY_APIKEY') + headers = {"X-API-KEY": apikey} + res = requests.get(url, headers=headers, verify=False) + + if res.status_code >= 400: + raise Exception(f"HTTP ERROR: {str(res.status_code)} - {res.text}") + else: + return res.json() + + def __request_json_post__(self, path: str, object: dict): + + """ + ## HTTP JSON POST + --- + Este método é responsável por realizar requisições HTTP do tipo POST para objetos JSON. + + Ele retorna o corpo de resposta da requisição, ou uma mensagem de erro, que indica qual foi a irregularidade ocorrida ao chamar a API. + """ + + url = self.ep + path + print("Calling: " + url) + + apikey = os.environ.get('REPLAY_APIKEY') + headers = {"X-API-KEY": apikey} + res = requests.post(url, json = object, headers = headers, verify = False) + + if res.status_code >= 400: + raise Exception(f"HTTP ERROR: {str(res.status_code)} - {res.text}") + if res.headers.get("Content-Type") != None and res.headers.get("Content-Type").find("json") != -1: + return json.loads(res.text) + else: + return res.text + + def __request_raw_post__(self, path: str, data: str): + + """ + ## HTTP RAW POST + --- + Este método é responsável por realizar requisições HTTP do tipo POST para objetos RAW. + + Ele retorna o corpo de resposta da requisição, ou uma mensagem de erro, que indica qual foi a irregularidade ocorrida ao chamar a API. + """ + + url = self.ep + path + print("Calling: " + url) + + apikey = os.environ.get('REPLAY_APIKEY') + headers = {"X-API-KEY": apikey} + res = requests.post(url, data = data, headers = headers, verify = False) + + if res.status_code >= 400: + raise Exception(f"HTTP ERROR: {str(res.status_code)} - {res.text}") + if res.headers.get("Content-Type") != None and res.headers.get("Content-Type").find("json") != -1: + return json.loads(res.text) + else: + return res.text + + def start (self, to: int = 120): + """ + ## Chrome Start + Inicia uma nova sessão/instância do Google Chrome na máquina. + + --- + #### IMPORTANTE: + É um erro comum já estar com o Google Chrome aberto para realizar outras tarefas enquanto programa. Porém, para que este método funcione corretamente, é necessário que não haja nenhuma instância do Chrome, em primeiro ou segundo plano, ativa. + + --- + #### Parâmetros: + - to: Tempo em segundos que a API esperará pela abertura do Chrome. Caso o tempo seja excedido sem que o Chrome seja aberto com sucesso, é retornado um erro. + --- + #### Retorna: + + "" -> String vazia + """ + return self.__request_get__(f"/ipc/chrome/start?to={to}") + + def start_headless (self): + """ + ## Chrome Start Headless + Inicia uma nova sessão/instância do Google Chrome em segundo plano na máquina. + + --- + #### IMPORTANTE: + É um erro comum já estar com o Google Chrome aberto para realizar outras tarefas enquanto programa. Porém, para que este método funcione corretamente, é necessário que não haja nenhuma instância do Chrome, em primeiro ou segundo plano, ativa. + + --- + #### Parâmetros: + --- + + --- + #### Retorna: + + "" -> String vazia + """ + return self.__request_get__("/ipc/chrome/startHeadless") + + def stop (self): + """ + ## Chrome Stop + Finaliza todas as sessões/instâncias ativas, em primeiro ou segundo plano, da máquina. Caso não haja nenhuma sessão ativa, não faz nada. + + --- + #### IMPORTANTE: + Este método funciona apenas com instâncias inicializadas pelo Client do Chrome. Caso uma sessão do Chrome tenha sido iniciada por outros modos, não surtirá efeito. + + --- + #### Parâmetros: + --- + + --- + #### Retorna: + + "" -> String vazia + """ + return self.__request_get__("/ipc/chrome/stop") + + def new (self, url: str): + """ + ## Chrome New + Abre uma nova aba na instância do Google Chrome inicializada pelo client. + + --- + #### Parâmetros: + - url: Endereço web do site que se deseja acessar na nova aba. + + --- + #### Retorna: + + -> ID da aba aberta. + """ + + url = urllib.parse.quote(url, "") + return self.__request_get__(f"/ipc/chrome/new?url={url}") + + def close (self, id: str): + """ + ## Chrome Close + Fecha a aba cujo ID foi informado como parâmetro. + + --- + #### Parâmetros: + - id: Identificador da aba que se deseja fechar. + + --- + #### Retorna: + + "" -> String vazia + """ + + return self.__request_get__("/ipc/chrome/close/"+id) + + def eval (self, id: str, command: str) -> dict: + """ + ## Chrome Eval + Digita o comando recebido no Console JavaScript da página cujo ID foi recebido. + + --- + #### Parâmetros: + - id: Identificador da aba em que se quer acessar o console. + - command: Comando DOM JavaScript + --- + #### Retorna: + -> ? + """ + + raw_return = self.__request_raw_post__("/ipc/chrome/eval/"+id, command) + + return raw_return['result']['result']['value'] if ('value' in raw_return['result']['result']) else None + + def wait (self, id: str, condition: str, to: int): + """ + ## Chrome Wait + Espera por uma determinada quantidade de tempo até que a condição passada como parâmetro seja verdadeira. + + --- + #### Parâmetros: + - id: Identificador da aba em que se quer utilizar o método. + - condition: Condição que se quer verificar. É bastante comum utilizar este método para aguardar que elementos da página web carreguem, para isso, utilize o formato: comando_para_o_elemento != undefined. + - to: Tempo limite de espera pelo valor de verdade da condição. + + --- + #### Retorna: + - Sucesso: "ok". + - Falha: Mensagem de erro. + """ + + return self.__request_raw_post__(f"/ipc/chrome/wait/{id}?to={to}", condition) + + def send(self, id: str, method: str, parameters: dict = {}): + """ + ## Chrome Send + Este método realiza a manipulação da página cujo id for informado através de domínios do Chrome Dev Tools Protocol. Para mais informações sobre como utilizar esta ferramenta, visite o site: + + https://chromedevtools.github.io/devtools-protocol/ + + --- + #### Parâmetros: + - id: Identificador da página web em que se quer realizar a manipulação. + - method: Nome do método DTP que se quer aplicar. + - parameters: Parâmetros requeridos pelo método informado no parâmetro anterior. Caso o método não exija parâmetros, não envie nada. + + --- + #### Retorna: + -> O retorno desta função não é previsivel, ele varia de acordo com qual método DVP foi utilizado e o respectivo retorno dele. + """ + + object = {"method": method, "params": parameters} + return self.__request_json_post__("/ipc/chrome/send/"+id, object) + + def open_tabs (self) -> List[dict]: + """ + ## Chrome Open Tabs + Este método busca todas as abas abertas no navegador Chrome e retorna dados sobre elas. + + --- + #### Parâmetros: + --- + + --- + #### Retorna: + -> Lista de dicionários com o nome das abas e outras informações sobre as mesmas. + """ + + return self.__request_json_get__("/ipc/chrome/opentabs") + + # # ====================================== BROKEN ====================================== + # def find_tab_by_url (self, url: str = "https://www.google.com/"): + # """ + # ## Chrome Find Tab By Url + # Este método procura a url recebida entre as abas abertas no Google Chrome. + + # --- + # #### Parâmetros: + # - url: Link que se deseja buscar entre as abas abertas. + + # --- + # #### Retorna: + # -> ID da aba encontrada com a url correspondente. + # """ + + # url = urllib.parse.quote(url, "") + # return self.__request_get__("/ipc/chrome/findtabbyurl/"+url) + + def find_tab_by_title (self, title: str): + """ + ## Chrome Find Tab By Title + Este método procura entre as abas abertas no Google Chrome, uma que possua o título correspondente ao que foi dado. + + --- + #### Parâmetros: + - title: Título da aba que se deseja procurar. + + --- + #### Retorna: + -> ID da aba encontrada com o título correspondente. + """ + + title = urllib.parse.quote (title, "") + return self.__request_get__("/ipc/chrome/findtabbytitle/"+title) \ No newline at end of file