Como Extrair Dados do IndexedDB Com Playwright e Scrapeless
Specialist in Anti-Bot Strategies
Resumo:
- IndexedDB pode conter dados estruturados do navegador que nunca aparecem no HTML inicial. Ler isso requer a execução de código dentro da origem da página após a aplicação abrir seu banco de dados.
- A extração do IndexedDB deve começar com a descoberta de bancos de dados e esquemas. Enumere bancos de dados, armazéns de objetos e índices antes de ler registros, em vez de supor seus nomes.
- O Playwright pode conectar as solicitações baseadas em callback do IndexedDB em um fluxo de extração que pode ser aguardado. Envolva solicitações como
indexedDB.open()egetAll()em Promessas dentro depage.evaluate(). - O DOM renderizado e o IndexedDB podem expor conjuntos diferentes do mesmo estado da aplicação. Preserve ambas as contagens para detectar filtragem, paginação ou dados da UI desatualizados.
- Armazéns grandes ou sensíveis requerem extração limitada. Prefira intervalos de chaves, contagens ou cursores em vez de uma chamada
getAll()irrestrita, e inspecione apenas as sessões de navegador autorizadas. - A mesma consulta do IndexedDB funciona localmente e através do Scrapeless. Mover para o Scrapeless Scraping Browser muda a criação do navegador, não a lógica do banco de dados do lado da página.
- Livre para começar. Novas contas do Scrapeless incluem tempo de execução gratuito do Scraping Browser—inscreva-se em app.scrapeless.com.
Introdução: Dados do Navegador Não Terminam Com o DOM
IndexedDB pode armazenar registros estruturados que nunca aparecem no HTML inicial de uma página. Este guia usa o Playwright para enumerar um banco de dados público real, inspecionar seu armazém de objetos e índices, ler todos os registros e compará-los com a tabela renderizada antes de mover a mesma extração para o Scrapeless Scraping Browser.
O Que o IndexedDB Adiciona à Extração da Web
IndexedDB é um banco de dados assíncrono, com escopo de origem, incorporado ao navegador. Ao contrário do mapa de strings do localStorage, ele armazena valores JavaScript estruturados em armazéns de objetos e suporta índices, chaves e transações. O guia do IndexedDB da MDN descreve esse modelo.
As aplicações o usam para registros offline, dados de API em cache, filas e estados que podem ser muito grandes ou estruturados para o Armazenamento Web. Um extrator de navegador pode ler o estado público da aplicação depois que a página abriu seu banco de dados. Esse acesso não torna os bancos de dados de usuários privados alvos apropriados de coleta; este tutorial usa apenas o fixture de contatos públicos da MDN.
Por Que Usar Playwright Com Scrapeless
O Playwright avalia uma função assíncrona na origem da página, para que a função possa chamar indexedDB.open, aguardar callbacks de solicitação e retornar dados simples para o Python. O Scrapeless Scraping Browser mantém essa API do lado da página dentro de uma sessão hospedada do Chromium.
O Playwright conecta-se através de connect_over_cdp. Uma vez que a página de destino está carregada, a consulta do IndexedDB não muda.
Pré-requisitos
- Python 3.10 ou posterior.
playwright1.59.0 ou uma versão compatível atual.- Chrome/Chromium local para o caminho completo executável.
- Uma conta do Scrapeless financiada e
SCRAPELESS_API_KEYpara a conexão com a nuvem. A conta de verificação retornou o código 14500 para novas sessões de navegador, então essa conexão é uma lacuna de pré-requisito rotulada.
Instalação
bash
python -m pip install "playwright==1.59.0"
Conectar ao Scrapeless Scraping Browser
Nota: Essa conexão requer saldo financiado no Scraping Browser. A conta de verificação final retornou
saldo insuficiente, por favor, recarregue primeiro. A extração completa do IndexedDB abaixo foi executada no Chrome local.
python
import os
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
params = urlencode({
"token": os.environ["SCRAPELESS_API_KEY"],
"sessionTTL": 120,
"proxyCountry": "US",
})
cdp_url = f"wss://browser.scrapeless.com/api/v2/browser?{params}"
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(cdp_url)
page = browser.contexts[0].pages[0]
# Navegue e avalie a consulta do IndexedDB abaixo.
browser.close()
Use a documentação do desenvolvedor do Scrapeless para parâmetros de conexão atuais.
Passo 1 — Descobrir os Bancos de Dados da Origem
O exemplo público de IDBIndex da MDN cria um banco de dados após o carregamento:
python
TARGET_URL = (
"https://mdn.github.io/dom-examples/"
"indexeddb-examples/idbindex/"
)
page.goto(TARGET_URL, wait_until="domcontentloaded")
page.wait_for_selector("tbody tr")
databases = page.evaluate("indexedDB.databases()")
print("databases:", databases)
text
databases: [{'name': 'contactsList', 'version': 1}]
indexedDB.databases() é descrito por a referência de bancos de dados IDBFactory. Verifique o suporte do navegador antes de depender disso em motores mais antigos.
Passo 2 — Inspecionar o Armazenamento de Objetos e Índices
Abra o banco de dados descoberto e retorne os metadados do esquema:
python
schema = page.evaluate("""async () => {
const opened = await new Promise((resolve, reject) => {
const request = indexedDB.open('contactsList');
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
const storeName = opened.objectStoreNames[0];
const transaction = opened.transaction(storeName, 'readonly');
const store = transaction.objectStore(storeName);
const result = {
storeName,
indexes: Array.from(store.indexNames),
};
opened.close();
return result;
}""")
print("armazenamento de objetos:", schema["storeName"])
print("índices:", schema["indexes"])
text
armazenamento de objetos: contactsList
índices: ['age', 'company', 'eMail', 'fName', 'jTitle', 'lName', 'phone']
Este fixture utiliza contactsList tanto para o banco de dados quanto para seu único armazenamento de objetos. Enumerar ambos ainda é importante: muitos aplicativos usam nomes diferentes, e assumir que eles combinam pode direcionar ao armazenamento errado.
Passo 3 — Ler Registros do Armazenamento de Objetos
IDBObjectStore.getAll() retorna uma solicitação, não uma Promise. Envolva seus callbacks de sucesso/erro para que o Playwright possa aguardá-lo:
python
records = page.evaluate("""async () => {
const database = await new Promise((resolve, reject) => {
const request = indexedDB.open('contactsList');
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
const transaction = database.transaction('contactsList', 'readonly');
const store = transaction.objectStore('contactsList');
const rows = await new Promise((resolve, reject) => {
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
database.close();
return rows;
}""")
print("contagem de registros:", len(records))
print("primeiro:", records[0])
print("último:", records[-1])
A referência getAll observa que retornará todos os registros quando nenhuma consulta ou contagem for fornecida. Mantenha as leituras limitadas em aplicativos reais; um cursor ou contagem é mais seguro para grandes armazenamentos.
Passo 4 — Comparar IndexedDB Com o DOM Renderizado
python
table_rows = page.locator("tbody tr").count()
arkham_rows = [row for row in records if row["company"] == "Arkham"]
ages = [row["age"] for row in records]
print("linhas da tabela:", table_rows)
print("linhas do banco de dados:", len(records))
print("linhas Arkham:", len(arkham_rows))
print("faixa de idade:", min(ages), max(ages))
text
linhas da tabela: 10
linhas do banco de dados: 10
linhas Arkham: 2
faixa de idade: 24 55
Contagens correspondentes provam que este fixture renderizou o banco de dados completo. Um aplicativo diferente pode renderizar apenas a página atual ou um subconjunto filtrado, então preserve ambas as contagens em vez de forçar a igualdade.
Pronto para consultar o estado do navegador em uma sessão hospedada? Crie uma conta Scrapeless gratuita e substitua apenas a criação do navegador.
Extrator Completo Executável
python
from playwright.sync_api import sync_playwright
TARGET_URL = (
"https://mdn.github.io/dom-examples/"
"indexeddb-examples/idbindex/"
)
QUERY = """async () => {
const databases = await indexedDB.databases();
const database = await new Promise((resolve, reject) => {
const request = indexedDB.open('contactsList');
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
const storeName = database.objectStoreNames[0];
const transaction = database.transaction(storeName, 'readonly');
const store = transaction.objectStore(storeName);
const indexes = Array.from(store.indexNames);
const rows = await new Promise((resolve, reject) => {
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
database.close();
return {databases, storeName, indexes, rows};
}"""
with sync_playwright() as p:
browser = p.chromium.launch(
executable_path="/usr/bin/google-chrome",
headless=True,
)
page = browser.new_page()
page.goto(TARGET_URL, wait_until="domcontentloaded")
page.wait_for_selector("tbody tr")
result = page.evaluate(QUERY)
rows = result["rows"]
table_rows = page.locator("tbody tr").count()
python
arkham_rows = [linha for linha in rows se linha["empresa"] == "Arkham"]
idades = [linha["idade"] para linha em rows]
assert resultado["bancos_de_dados"] == [{"nome": "contactsList", "versão": 1}]
assert resultado["nome_da_loja"] == "contactsList"
assert len(resultado["índices"]) == 7
assert len(rows) == table_rows == 10
assert len(arkham_rows) == 2
print("título:", page.title())
print("bancos de dados:", resultado["bancos_de_dados"])
print("loja de objetos:", resultado["nome_da_loja"])
print("índices:", resultado["índices"])
print("linhas do banco de dados:", len(rows))
print("linhas da tabela:", table_rows)
print("primeiro contato:", rows[0]["fName"], rows[0]["lName"])
print("último contato:", rows[-1]["fName"], rows[-1]["lName"])
print("linhas Arkham:", len(arkham_rows))
print("faixa de idade:", min(idades), max(idades))
browser.close()
O resultado ativo contém um banco de dados versão-1, uma loja contactsList, sete índices, dez registros de banco de dados, dez linhas renderizadas, dois contatos Arkham e idades de 24 a 55.
O Que Você Recebe de Volta
O extractor completo retorna a identidade do banco de dados, metadados do esquema, registros armazenados e uma comparação DOM da mesma sessão do navegador:
json
{
"banco_de_dados": {
"nome": "contactsList",
"versão": 1
},
"loja_de_objetos": "contactsList",
"contagem_de_índices": 7,
"linhas_do_banco_de_dados": 10,
"linhas_renderizadas": 10,
"linhas_empresa_correspondentes": 2,
"faixa_de_idade": {
"mínimo": 24,
"máximo": 55
}
}
As contagens de banco de dados e tabela correspondentes mostram que o fixture público renderizou cada registro armazenado. Aplicativos de produção podem renderizar apenas um subconjunto filtrado ou paginado, então mantenha a identidade do banco de dados, o nome da loja de objetos, a URL da página e ambas as contagens de linhas com o resultado extraído.
Problemas Comuns na Extração do IndexedDB
A lista de bancos de dados está vazia
Aguarde o aplicativo abrir ou criar seu banco de dados. Uma lista vazia imediatamente após a navegação pode indicar um problema de tempo, uma API de descoberta não suportada ou a origem errada.
A loja de objetos é grande
Não chame getAll() sem um limite em uma loja não limitada. Use um intervalo de chaves, contagem ou cursor e pare após o corte do conjunto de dados que sua tarefa precisa.
Os valores contêm tipos não JSON
Playwright serializa os valores suportados novamente para Python. Blobs, instâncias de classes complexas e estruturas cíclicas podem precisar de mapeamento a nível de campo dentro da função avaliada.
A página e o banco de dados não concordam
A página pode estar filtrada, paginada ou desatualizada. Armazene a identidade do banco de dados, o nome da loja de objetos, a URL da página e o tempo de extração para que a incompatibilidade possa ser investigada.
Limites de Extração Seguros
O IndexedDB frequentemente contém dados privados de aplicativos offline. Use esta técnica apenas em sistemas e sessões que você está autorizado a inspecionar. Não publique credenciais, mensagens ou registros pessoais. Os contatos públicos do MDN são dados de fixture sintéticos.
Conclusão
A extração do IndexedDB começa com a descoberta e inspeção do esquema, não com um nome de loja adivinhado. Abra o banco de dados da origem, enumere lojas e índices, vincule as chamadas de solicitação a uma Promise e valide o resultado em relação a uma superfície visível, quando existir. Mover o fluxo de trabalho para o Scrapeless altera a criação do navegador, enquanto preserva a consulta do lado da página.
Pronto para Extrair o Estado Estruturado do Navegador?
Junte-se a desenvolvedores que estão construindo fluxos de trabalho de extração baseados em navegador na comunidade Scrapeless: Discord · Telegram.
Comece com Scrapeless, revise preços do Scrapeless e leia o que o CDP expõe antes de mover o mesmo fluxo de trabalho do IndexedDB para um navegador hospedado.
FAQ
Q: O Playwright pode ler IndexedDB?
Sim. Avalie uma função assíncrona na origem da página e envolva as chamadas de solicitação do IndexedDB em Promises.
Q: Por que enumerar bancos de dados primeiro?
Isso confirma a origem e a identidade do banco de dados antes que você assuma um nome ou versão. Nem todos os navegadores expõem indexedDB.databases(), então mantenha um fallback de nome conhecido quando a compatibilidade exigir.
Q: A loja de objetos é a mesma coisa que um banco de dados?
Não. Um banco de dados contém uma ou mais lojas de objetos. No fixture do MDN, tanto o banco de dados quanto sua loja de objetos são nomeados contactsList.
Q: Quando devo usar um cursor em vez de getAll?
Use um cursor ou consulta limitada quando uma loja pode ser grande, quando você precisa de ordenação, ou quando pode parar após um pequeno subconjunto.
Q: Por que comparar linhas do IndexedDB com o DOM?
A comparação revela se a interface do usuário mostra todos os registros, uma visualização filtrada ou apenas uma página. Nenhuma superfície deve sobrescrever a outra silenciosamente.
P: O IndexedDB persiste entre as sessões do navegador?
Ele pode persistir em um perfil de navegador mantido até que a origem ou o navegador o limpe. Um contexto efêmero pode desaparecer quando o contexto é fechado.
P: Extrair dados do IndexedDB é sempre permitido?
Não. Inspecione apenas sessões autorizadas e dados públicos necessários, minimize os campos retidos, siga os termos do site e a política de robôs onde aplicável, e obtenha aconselhamento jurídico para usos sensíveis.
Na Scorretless, acessamos apenas dados disponíveis ao público, enquanto cumprem estritamente as leis, regulamentos e políticas de privacidade do site aplicáveis. O conteúdo deste blog é apenas para fins de demonstração e não envolve atividades ilegais ou infratoras. Não temos garantias e negamos toda a responsabilidade pelo uso de informações deste blog ou links de terceiros. Antes de se envolver em qualquer atividade de raspagem, consulte seu consultor jurídico e revise os termos de serviço do site de destino ou obtenha as permissões necessárias.



