Percorrer o DOM
Depois de ter um elemento, você muitas vezes precisa dos que estão ao redor dele: seus filhos, seus irmãos, elementos dentro de um shadow root, ou conteúdo dentro de um iframe. Este guia cobre a movimentação pela árvore do DOM a partir de um ponto de partida conhecido. Para localizar esse elemento inicial, veja Encontrar elementos.
Obter elementos filhos
O get_children_elements() retorna os descendentes de um elemento. max_depth controla até que profundidade ele vai (1 são apenas os filhos diretos), e tag_filter mantém somente as tags que você indicar.
import asyncio
from pydoll.browser.chromium import Chrome
async def main():
async with Chrome() as browser:
tab = await browser.start()
await tab.go_to('https://books.toscrape.com')
container = await tab.find(class_name='row', tag_name='ol')
direct = await container.get_children_elements(max_depth=1)
print(f'{len(direct)} direct children')
# descendentes até 2 níveis de profundidade, apenas links
links = await container.get_children_elements(max_depth=2, tag_filter=['a'])
print(f'{len(links)} links within two levels')
asyncio.run(main())
Obter elementos irmãos
O get_siblings_elements() retorna os elementos no mesmo nível do seu elemento, excluindo ele mesmo. tag_filter restringe o resultado a tags específicas.
active = await tab.find(class_name='active')
siblings = await active.get_siblings_elements()
print(f'{len(siblings)} siblings')
link_siblings = await active.get_siblings_elements(tag_filter=['a'])
Busca com escopo vs filhos diretos
Um find() ou query() com escopo busca em todos os descendentes de um elemento. Quando você quer apenas os filhos diretos, use o combinador de filho do CSS > ou um passo XPath, que o query() aceita:
container = await tab.find(id='cards')
# todo .card em qualquer lugar da subárvore
all_cards = await container.find(class_name='card', find_all=True)
# apenas os elementos .card que são filhos diretos
direct_cards = await container.query('> .card', find_all=True)
Use get_children_elements() quando quiser explorar a estrutura ou filtrar por tag; use um find()/query() com escopo quando quiser elementos que correspondam a atributos específicos em qualquer lugar da subárvore.
Ler texto e atributos
De qualquer elemento você pode ler seu texto visível e seus atributos HTML:
book = await tab.find(class_name='product_pod')
title = await book.find(tag_name='h3')
print(await title.text) # texto visível
link = await title.find(tag_name='a')
print(link.get_attribute('href')) # o valor de um atributo
print(link.get_attribute('title'))
text é uma propriedade awaitable; get_attribute(name) retorna a string do atributo, ou None quando o atributo está ausente.
Shadow DOM
Muitos componentes escondem suas partes internas dentro de um shadow root, que consultas normais ao DOM não conseguem ver. Acesse o shadow host, obtenha seu shadow root, e então busque dentro dele.
host = await tab.find(id='my-component')
shadow = await host.get_shadow_root()
button = await shadow.query('.internal-btn')
await button.click()
Dentro de um shadow root, use query() com CSS
find() e XPath não são suportados em um ShadowRoot e levantam NotImplementedError. Busque em shadow roots apenas com query() e seletores CSS.
O query() dentro de um shadow root recebe os parâmetros habituais find_all, timeout e raise_exc:
items = await shadow.query('.item', find_all=True)
dynamic = await shadow.query('#late', timeout=5, raise_exc=False)
Web components se aninham, então um shadow root pode conter outro shadow host:
outer = await tab.find(tag_name='outer-component')
outer_shadow = await outer.get_shadow_root()
inner = await outer_shadow.query('inner-component')
inner_shadow = await inner.get_shadow_root()
deep = await inner_shadow.query('.deep-btn')
Descobrir shadow roots em uma página
Quando você não sabe quais shadow roots existem (depuração, ou widgets dinâmicos como o Cloudflare Turnstile), find_shadow_roots() retorna todos eles. Shadow hosts costumam carregar tarde, então passe timeout para verificar repetidamente até que apareçam:
shadow_roots = await tab.find_shadow_roots(timeout=10)
for sr in shadow_roots:
print(f'mode={sr.mode}, host={sr.host_element}')
checkbox = await sr.query('input[type="checkbox"]', raise_exc=False)
if checkbox:
await checkbox.click()
Por padrão a busca cobre o documento principal (incluindo iframes de mesma origem). Passe deep=True para também alcançar shadow roots dentro de iframes de origem cruzada (OOPIFs), que é o que widgets como o Turnstile usam:
Trabalhar dentro de um iframe
Um iframe tem seu próprio contexto de DOM. Encontre o elemento iframe, e então chame find() ou query() nele; o Pydoll direciona a busca para dentro do frame automaticamente. Continue encadeando para iframes aninhados.
iframe = await tab.query('iframe.embedded-content', timeout=10)
button = await iframe.find(tag_name='button', class_name='submit')
await button.click()
# iframe aninhado
inner = await iframe.find(tag_name='iframe')
link = await inner.find(text='Download PDF')
await link.click()
Para um guia completo de iframes, incluindo frames de CAPTCHA e resolução de problemas, veja Iframes.
Capturas de tela dentro de iframes
tab.take_screenshot() captura apenas a página de nível superior. Para capturar o conteúdo de um iframe, encontre um elemento dentro do frame e chame element.take_screenshot().
Próximos passos
- Encontrar elementos: localize os elementos a partir dos quais você navega.
- Iframes: o guia completo para contextos de frame.
- Extração estruturada: deixe um modelo percorrer estruturas repetidas por você.