# Avaliação inicial do banco de dados **Data da avaliação:** 13 de julho de 2026 **Banco:** `dfimoveis_data/dfimoveis.sqlite3` **Escopo:** diagnóstico estrutural e de qualidade anterior a qualquer análise dos imóveis ## 1. Pergunta respondida O banco já representa de forma segura anúncios, snapshots históricos e imóveis físicos, com cobertura e qualidade suficientes para iniciar análises imobiliárias? **Resposta curta:** ele representa anúncios e estados de conteúdo, mas ainda não representa imóveis físicos deduplicados. A coleta cobre apenas um dia e contém exatamente um snapshot por anúncio. Preços, áreas e quartos podem ser usados somente após validações específicas; localização e dados de anunciante estão fortemente contaminados e não devem ser usados como estão. Portanto, o banco ainda não sustenta tendência histórica, tempo de mercado, preço por m² ou comparáveis por localização. ## 2. Segurança e método A análise foi feita exclusivamente com consultas agregadas ou amostras pequenas. O banco foi aberto com `sqlite3 -readonly` ou URI `mode=ro&immutable=1`, sempre com `PRAGMA query_only = ON`. Nenhum scraper foi executado e nenhuma instrução SQL de escrita foi enviada ao banco. Identificação do arquivo analisado: - tamanho: 5.017.600 bytes; - modificação: `2026-07-13 14:20:34.601285975 -03:00`; - SHA-256 inicial: `1ce3128b488c50c993d7328ac6e24b5905f083a7e04e3f636407eea1edd02b9d`; - sem arquivos `-wal` ou `-shm` e sem processo com o arquivo aberto na inspeção inicial; - `PRAGMA quick_check`: `ok`; - `PRAGMA foreign_key_check`: nenhuma violação. Na validação final, o arquivo principal continuava com o mesmo tamanho, `mtime` e SHA-256. Como seu modo persistido é WAL, o cliente SQLite havia criado dois sidecars de runtime: `dfimoveis.sqlite3-shm`, com 32.768 bytes, e `dfimoveis.sqlite3-wal`, vazio. Eles não foram removidos, conforme a regra explícita do projeto. O esquema completo e as decisões de interpretação estão em `docs/ACTUAL_SCHEMA.md`. ## 3. Universo, período e cobertura Não houve filtro ou exclusão de registros para este diagnóstico. O universo físico contém: | Unidade | Quantidade | |---|---:| | Execuções do coletor | 4 | | Anúncios únicos por `listing_id` | 156 | | URLs de anúncio únicas | 156 | | Snapshots de conteúdo | 156 | | Vínculos anúncio–busca | 156 | | Fotos registradas | 5.386 | | Anúncios com pelo menos uma foto | 156 | ### Cobertura temporal Todas as datas técnicas válidas estão em ISO 8601. A coleta cobre somente **13/07/2026**: | Evento | UTC | Horário de Brasília (UTC−3) | |---|---|---| | Primeira execução iniciada | 12:20:58 | 09:20:58 | | Primeira observação de anúncio | 12:56:32 | 09:56:32 | | Última observação de anúncio | 13:25:51 | 10:25:51 | | Última execução concluída | 17:20:34 | 14:20:34 | As duas primeiras execuções terminaram com `status = ok`, mas registraram zero páginas, URLs e detalhes. As duas execuções com coleta efetiva somaram: - 9 páginas de busca; - 156 URLs encontradas; - 156 detalhes processados com sucesso e zero falhas; - 5.383 fotos baixadas e 3 falhas; - zero anúncios marcados como inativos. As buscas cobertas foram: 1. `https://www.dfimoveis.com.br/venda/df/cruzeiro/novo/apartamento/3-quartos` — 65 anúncios; 2. `https://www.dfimoveis.com.br/venda/df/brasilia/jardins-mangueiral/casa/3,4-quartos` — 91 anúncios. O universo é, portanto, intencionalmente limitado a um portal e a duas consultas. A busca do Jardins Mangueiral aceita três **ou quatro** quartos: 89 casas foram extraídas com três quartos anunciados e 2 com quatro. Mesmo os 89 registros de três quartos não comprovam a planta original exigida pelo projeto. ## 4. Como as entidades são representadas ### Anúncio `listings` guarda uma linha por `listing_id` do DF Imóveis, com URL única e o estado corrente. Os 156 IDs, URLs e hashes correntes são distintos. Essa é uma identidade de publicação no portal, não de unidade residencial. ### Snapshot ou observação histórica `listing_history` guarda uma linha por par único `(listing_id, content_hash)`. Isso é um histórico de **mudanças de conteúdo**: uma nova observação idêntica não cria novo snapshot. `listings.first_seen_at` e `last_seen_at` condensam a janela observada. No snapshot atual: - todos os 156 anúncios têm exatamente um registro histórico; - o hash histórico é igual ao hash corrente em todos os casos; - `captured_at = first_seen_at` em todos os casos; - `first_seen_at = last_seen_at` em todos os casos. Assim, não há série histórica real ainda. Não é possível medir mudança de preço, republicação, duração, saída ou retorno do estoque. ### Execução do scraper `crawl_runs` registra horários, estado e contadores JSON, mas não existe `run_id` em `listings`, `listing_history` ou `listing_searches`. A execução geradora só pode ser inferida por horário. Isso limita auditoria, cobertura por execução e diagnóstico de ausências. ### Imóvel físico Não existe tabela ou chave para imóvel físico, unidade, endereço normalizado ou relação de duplicidade. Anúncios de corretores diferentes permanecem independentes. A base também não contém hashes perceptuais; possui apenas SHA-256 exato das imagens. Logo, as unidades válidas hoje são: - anúncio único: suportado por `listing_id`; - estado alterado do anúncio: suportado por `listing_history`; - imóvel físico provável/confirmado: **não suportado sem derivação e revisão**. ## 5. Qualidade dos dados ### 5.1 Integridade estrutural Pontos positivos: - arquivo íntegro segundo `quick_check`; - nenhuma FK órfã em histórico, buscas ou fotos; - todos os JSONs são sintaticamente válidos; - todos os IDs de anúncio têm 6–7 caracteres numéricos; - todas as URLs pertencem ao domínio e padrão esperados; - todos os 156 HTMLs brutos referenciados existem; - todos os hashes de conteúdo têm 64 caracteres; - não há URL, hash corrente ou caminho de HTML repetido. Limitações estruturais: - nenhuma relação entre execução e item; - fonte implícita, sem `source_id`; - área armazenada em um único conceito; - mídia ligada ao anúncio, não ao snapshot; - ausência de entidade de imóvel físico e de localização normalizada; - apenas autoíndices; não há índices por datas ou atributos analíticos. ### 5.2 Completude dos principais campos | Campo | Ausentes | Percentual | |---|---:|---:| | preço | 0 | 0,0% | | área | 0 | 0,0% | | quartos | 0 | 0,0% | | condomínio | 59 | 37,8% | | IPTU | 144 | 92,3% | | suítes | 41 | 26,3% | | vagas | 57 | 36,5% | | cidade | 156 | 100,0% | | UF | 156 | 100,0% | | nome do anunciante | 156 | 100,0% | | latitude/longitude | 156 / 156 | 100,0% / 100,0% | | data de publicação | 156 | 100,0% | | data de inativação | 156 | 100,0% | Esses percentuais medem apenas nulos. Para campos textuais, a taxa de preenchimento é enganosa porque o parser capturou conteúdo da página em vez do valor pretendido. ### 5.3 Contaminação de texto Os problemas mais graves são: - 146 endereços (93,6%) têm mais de 300 caracteres; - 154 bairros (98,7%) têm mais de 100 caracteres; - os dois valores curtos de bairro são `.` e um trecho de descrição, também inválidos; - 98 códigos de anunciante (62,8%) têm mais de 100 caracteres; - 153 valores de CRECI (98,1%) têm mais de 100 caracteres. Os textos longos incluem navegação, formulários, política de privacidade e outros fragmentos da página. Eles podem conter dados de contato exibidos pelo portal; por isso, não foram reproduzidos neste relatório. Esses campos devem ser considerados **indisponíveis**, não apenas ruidosos. Os títulos têm somente quatro valores distintos e funcionam como títulos genéricos por tipologia. As 156 descrições são distintas, mas têm apenas 156–161 caracteres, compatíveis com resumos/metadados truncados, não com a descrição completa. `extra_json` contém um array `json_ld` vazio nos 156 registros e não resolve a reextração. ### 5.4 Valores estruturados e outliers | Tipo anunciado | N | Preço mínimo | Preço máximo | Área mínima | Área máxima | Quartos | |---|---:|---:|---:|---:|---:|---:| | Apartamento | 65 | R$ 480.000 | R$ 2.030.598 | 61 m² | 113 m² | 3 | | Casa | 91 | R$ 450.000 | R$ 570.000.000 | 0,11 m² | 131,46 m² | 3–4 | Não há valores nulos ou não positivos em preço e área, mas isso não significa validade: - anúncio `1351663`: R$ 570.000.000, forte indício de erro de escala ou parsing; - anúncio `1370918`: 0,11 m², área fisicamente implausível. Não foi aplicado corte nem correção silenciosa. Também não se calculou preço por m², pois `area_m2` não distingue área útil, privativa, construída, total ou terreno. ### 5.5 Fotos e arquivos brutos Das 5.386 linhas em `photos`: - 5.383 têm arquivo local, e o tamanho em disco coincide com `bytes`; - 3 não têm hash, MIME, tamanho ou caminho, correspondendo às três falhas registradas pelo coletor; - há 4.514 WebP, 558 JPEG, 311 PNG e 3 sem MIME; - os anúncios possuem de 5 a 61 fotos; - não há caminho local nem ordinal duplicado dentro de anúncio. Há 3.307 hashes distintos. Cento e quatorze hashes aparecem em mais de um anúncio, e um deles aparece nos 156 anúncios. Isso revela logos, selos, placeholders ou outros ativos comuns. Hash idêntico pode ser sinal de anúncio duplicado somente depois de excluir ativos genéricos e combinar outros sinais. ## 6. Duplicidades e imóveis físicos prováveis Não há duplicidade técnica imediata por `listing_id`, URL ou `content_hash`. Uma assinatura exploratória composta por bairro, tipo, preço, área, quartos e vagas encontrou 13 grupos, equivalentes a 18 linhas excedentes. Esse resultado **não é evidência de 18 imóveis duplicados**, porque: - o bairro está contaminado; - preço, área e quartos podem coincidir entre unidades distintas; - fotos compartilhadas incluem ativos genéricos; - não há endereço/unidade confiável nem telefone normalizado; - SHA-256 só detecta arquivos idênticos, não versões redimensionadas da mesma foto. Consequentemente, o método de deduplicação desta avaliação foi apenas técnico: unicidade de anúncio por `listing_id`. Nenhuma fusão de anúncios nem contagem de imóveis físicos foi produzida. Uma futura camada derivada deve combinar, com confiança e revisão manual: - endereço/QC/quadra/bloco/unidade reextraídos; - imagens após remoção de ativos comuns, idealmente com hash perceptual; - preço, área, planta, quartos e vagas validados; - anunciante e descrição normalizados; - estados `unreviewed`, `probable`, `confirmed`, `rejected` e `ambiguous`. ## 7. Possíveis quebras de coleta 1. **Localização:** `address` e `neighborhood` capturaram grandes trechos da página; cidade, UF e coordenadas ficaram vazias. 2. **Anunciante:** `advertiser_code` e `creci` frequentemente capturaram texto extenso; `advertiser_name` ficou vazio. 3. **Dados estruturados:** o `json_ld` foi preservado como array vazio em todos os anúncios. 4. **Escala numérica:** pelo menos um preço e uma área são claramente suspeitos. 5. **Fotos:** três falhas foram registradas de modo consistente, sem arquivo local. 6. **Execuções vazias:** duas execuções `ok` não coletaram páginas. Sem motivo/configuração persistidos, não é possível diferenciar teste, filtro vazio ou falha silenciosa. 7. **Filtro do Mangueiral:** a URL coletada inclui casas de quatro quartos e não identifica planta original. ## 8. Limitações para qualquer conclusão imobiliária - Um único dia não permite análise histórica ou tendência. - A amostra cobre apenas DF Imóveis e duas buscas específicas. - Preço anunciado não é preço negociado. - Não há transações, status comprovado de venda ou evidência de fechamento. - Não há imóveis físicos deduplicados. - Não há localização analítica confiável por QC, quadra ou bloco. - Não há conceito comparável de área. - O estado de reforma, planta original, elevador, garagem, andar e qualidade funcional não está modelado de forma suficiente. - Para Jardins Mangueiral, `bedrooms = 3` não valida planta original de três quartos. Portanto, ainda não é apropriado produzir medianas regionais, comparáveis, preço por m², tempo de anúncio, liquidez ou recomendações de visita com base apenas neste banco. ## 9. Próximas ações recomendadas Antes da análise dos imóveis: 1. corrigir e testar a extração de endereço, bairro, anunciante e CRECI usando os HTMLs já salvos, sem recrawling; 2. criar uma derivação reproduzível fora do banco original e preservar os valores brutos; 3. separar áreas privativa, útil, construída, total e de terreno, deixando nulo o conceito não comprovado; 4. criar regras explícitas de validação para preço e área, mantendo os outliers identificados em quarentena; 5. filtrar o Mangueiral por planta original de três quartos, não apenas pelo número anunciado; 6. coletar múltiplas datas e auditar a cobertura por busca antes de estimar permanência, entradas ou saídas; 7. numa evolução aprovada do coletor, adicionar `source_id`, `run_id` nos snapshots, referência ao HTML bruto histórico e versão/configuração da coleta; 8. construir `property_candidates` apenas em uma camada derivada, com confiança e revisão, sem fundir registros no banco original. ## 10. Consultas de reprodução Todas devem ser executadas com `sqlite3 -readonly dfimoveis_data/dfimoveis.sqlite3` ou conexão Python URI em `mode=ro` e `PRAGMA query_only = ON`. ```sql -- Objetos do esquema SELECT type, name, tbl_name, sql FROM sqlite_master ORDER BY type, name; -- Contagens (executar uma tabela por vez após validar os nomes) SELECT COUNT(*) FROM crawl_runs; SELECT COUNT(*) FROM listings; SELECT COUNT(*) FROM listing_history; SELECT COUNT(*) FROM listing_searches; SELECT COUNT(*) FROM photos; -- Cobertura SELECT MIN(started_at), MAX(finished_at), COUNT(*) FROM crawl_runs; SELECT MIN(first_seen_at), MAX(last_seen_at), COUNT(DISTINCT date(first_seen_at)) FROM listings; -- Número de snapshots por anúncio SELECT snapshots, COUNT(*) AS listings FROM ( SELECT listing_id, COUNT(*) AS snapshots FROM listing_history GROUP BY listing_id ) GROUP BY snapshots; -- Completude de campos críticos SELECT COUNT(*) AS total, SUM(price_brl IS NULL) AS missing_price, SUM(area_m2 IS NULL) AS missing_area, SUM(condominium_brl IS NULL) AS missing_condominium, SUM(iptu_brl IS NULL) AS missing_iptu, SUM(latitude IS NULL OR longitude IS NULL) AS missing_coordinates FROM listings; -- Contaminação textual sem imprimir os textos SELECT SUM(length(address) > 300) AS long_address, SUM(length(neighborhood) > 100) AS long_neighborhood, SUM(length(advertiser_code) > 100) AS long_advertiser_code, SUM(length(creci) > 100) AS long_creci FROM listings; -- Integridade referencial e física PRAGMA quick_check; PRAGMA foreign_key_check; ``` ## 11. Implicação para a decisão de compra Esta avaliação reduz o risco de transformar uma coleta tecnicamente bem-sucedida em falsa precisão de mercado. O próximo ganho real não virá de comparar preços imediatamente, mas de corrigir localização e semântica de área, acumular histórico e construir uma deduplicação reversível. Até lá, os registros são úteis como inventário bruto de anúncios e arquivos, não como avaliação consolidada de imóveis físicos.