Esperas
As páginas mudam depois de carregar: um spinner some, um botão fica habilitado, um clique dispara uma requisição, o envio de um formulário navega. O Pydoll oferece uma espera para cada um desses momentos, então você aguarda exatamente o que precisa em vez de dormir e torcer. Toda espera lança WaitTimeout (ou WaitElementTimeout, para estados de elemento) quando o tempo acaba.
Aguardar um estado do elemento
find() com timeout aguarda um elemento existir. wait_until() aguarda o que vem depois: o elemento ficar visível, oculto, habilitado, interagível ou removido da página. Defina uma ou mais condições e a chamada retorna quando todas valem.
from pydoll.sync import Chrome
def main():
with Chrome() as browser:
tab = browser.start()
tab.go_to('https://the-internet.herokuapp.com/dynamic_loading/1')
tab.find(tag_name='button').click()
loading = tab.find(id='loading')
loading.wait_until(is_hidden=True, timeout=10)
print(tab.find(id='finish').text())
main()
import asyncio
from pydoll import Chrome
async def main():
async with Chrome() as browser:
tab = await browser.start()
await tab.go_to('https://the-internet.herokuapp.com/dynamic_loading/1')
await (await tab.find(tag_name='button')).click()
loading = await tab.find(id='loading')
await loading.wait_until(is_hidden=True, timeout=10)
print(await (await tab.find(id='finish')).text())
asyncio.run(main())
As condições que você pode definir:
| Flag | Vale quando |
|---|---|
is_visible |
O elemento tem uma caixa na tela e não está oculto por CSS. |
is_hidden |
O oposto de is_visible. |
is_enabled |
O elemento não tem o atributo disabled. |
is_interactable |
O elemento está visível, por cima dos outros e pode receber um clique. |
is_detached |
O elemento foi removido do DOM. |
Cada elemento também responde às mesmas perguntas diretamente: is_visible(), is_interactable(), is_on_top() e is_detached() são métodos, e is_enabled é uma propriedade simples.
Aguardar um elemento sumir
Quando você nunca segurou o elemento, ou um novo find() continuaria encontrando outro igual, aguarde pela tab. wait_for_absence() aceita os mesmos critérios de find() e retorna quando nada corresponde.
Aguardar uma URL
Aplicações de página única mudam a URL sem carregar a página. wait_for_url() consulta a URL atual até ela corresponder e retorna a URL que correspondeu. O padrão pode ser um glob, uma expressão regular compilada ou um callable que recebe a URL e retorna um bool.
Em um glob, * corresponde a qualquer coisa exceto /, ** corresponde a qualquer coisa incluindo /, e {a,b} corresponde a qualquer uma das alternativas. As mesmas três formas de padrão funcionam em todo lugar deste guia que aceita um padrão de URL.
Aguardar uma condição em JavaScript
wait_for_script() avalia uma expressão na página até ela ser verdadeira pelas regras do JavaScript, julgadas do lado da página, então um nó do DOM, uma função ou um objeto vazio contam como encontrados e uma promise retornada é aguardada. Retorna o valor quando é um primitivo e True quando é um objeto. Um script que lança erro levanta ScriptEvaluationError na hora, com o erro do JavaScript em error_text, em vez de esperar o timeout. Use para estado que o DOM não mostra: uma global definida pela aplicação, a flag de pronto de um store, uma contagem.
Aguardar a rede ficar quieta
wait_for_network_idle() retorna quando nenhuma requisição ficou em andamento por idle_time segundos. Chame logo depois da ação que inicia as requisições, porque só as requisições iniciadas depois da chamada são contadas.
Páginas que nunca ficam quietas
Beacons de analytics e long polling mantêm algumas páginas ocupadas para sempre. Nelas, aguarde o elemento ou a resposta de que você precisa em vez da rede inteira.
Capturar a resposta que um clique dispara
Os dados que você quer costumam estar no JSON que a página busca, não no DOM. expect_response() é um gerenciador de contexto: entre nele, faça a ação dentro do bloco e, quando o bloco sai, o handle contém a primeira resposta correspondente, corpo incluído.
O handle expõe url, status, ok (verdadeiro para status 2xx), headers, mime_type e request_id, além de body() em bytes, text() e json(). Ler um campo dentro do bloco, antes de algo chegar, lança WaitTimeout. Se o navegador falhou o carregamento depois de os cabeçalhos chegarem, status e headers continuam definidos e body() lança WaitTimeout.
expect_request() funciona do mesmo jeito para o lado de saída, e seu handle expõe url, method, headers, post_data e resource_type.
Os dois gerenciadores ligam os eventos de rede durante o bloco se estavam desligados, e os desligam de novo depois. Você não gerencia enable_network_events() por conta própria.
Aguardar uma navegação que você causa
go_to() já aguarda a página carregar. Quando é um clique ou o envio de um formulário que navega, envolva-o em expect_navigation(): o bloco sai quando o frame principal mudou (para uma URL que corresponde a url, quando informada) e alcançou o estado de carregamento configurado em options.page_load_state.
Registre antes de agir
Os três gerenciadores expect_* começam a escutar quando você entra no bloco, e a ação vai dentro dele. Fazer a ação primeiro e só depois aguardar perderia uma resposta que chegou no meio, que é justamente o caso rápido que você quer tratar.
Exemplo completo: ler uma API em vez do DOM
from pydoll.sync import Chrome
def main():
with Chrome() as browser:
tab = browser.start()
tab.go_to('https://quotes.toscrape.com/scroll')
# a página busca a página 1 ao carregar; espere por ela antes de pedir a página 2
tab.find(class_name='quote', timeout=10)
with tab.expect_response('**/api/quotes?page=2') as response:
tab.execute_script('window.scrollTo(0, document.body.scrollHeight)')
for quote in response.json()['quotes']:
print(quote['author']['name'], '-', quote['text'][:60])
tab.wait_for_network_idle(idle_time=0.5)
main()
import asyncio
from pydoll import Chrome
async def main():
async with Chrome() as browser:
tab = await browser.start()
await tab.go_to('https://quotes.toscrape.com/scroll')
# a página busca a página 1 ao carregar; espere por ela antes de pedir a página 2
await tab.find(class_name='quote', timeout=10)
async with tab.expect_response('**/api/quotes?page=2') as response:
await tab.execute_script('window.scrollTo(0, document.body.scrollHeight)')
for quote in response.json()['quotes']:
print(quote['author']['name'], '-', quote['text'][:60])
await tab.wait_for_network_idle(idle_time=0.5)
asyncio.run(main())
Próximos passos
- Encontrar elementos:
find()com timeout, a espera que vem antes de todas estas. - Monitoramento de rede: observe todas as requisições da página, não só a que você espera.
- Eventos: reaja a eventos de página e de rede conforme acontecem.
- Repetição: execute um passo inteiro de novo quando uma espera expira.