You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
O método `run` é um **async generator**: ele não retorna uma lista, mas vai `yield`ando cada item conforme os processa. Isso permite que o pipeline comece a persistir resultados antes do scraping terminar.
**O `Fetcher`** é injetado via construtor e deve ser sempre usado no lugar de `fetch`/`axios` diretamente. Ele aplica rate limiting automático (delay configurável entre requisições) e define o User-Agent do bot:
320
+
321
+
```typescript
322
+
constructor(privatefetcher: Fetcher) {}
323
+
324
+
// ✅ correto
325
+
const html =awaitthis.fetcher.get(url);
326
+
327
+
// ❌ nunca faça isso dentro de um plugin
328
+
const html =awaitfetch(url).then(r=>r.text());
329
+
```
330
+
331
+
---
332
+
333
+
#### `pages/list.ts` — Page Object da listagem
334
+
335
+
Responsável por parsear a página que lista os itens (ex: página de notícias com cards). Não faz requisições — recebe o `CheerioAPI` já carregado do `index.ts`.
336
+
337
+
Deve expor dois métodos:
338
+
-`extractItems()` → retorna os links e metadados básicos dos cards
339
+
-`nextPageUrl()` → retorna a URL da próxima página, ou `null` se não houver
> Os **seletores CSS** (`article.noticia`, `a.noticia__link`, etc.) são específicos de cada site. Inspecione o HTML da página alvo com o DevTools do navegador para descobrir os corretos.
369
+
370
+
---
371
+
372
+
#### `pages/detail.ts` — Page Object do detalhe
373
+
374
+
Responsável por parsear a página individual de cada item. Também recebe o `CheerioAPI` pronto.
375
+
376
+
Deve expor os métodos necessários para extrair os dados do `ScrapeResult`:
> Sempre use `.clone()` antes de remover elementos para não mutar o DOM original. O `replace(/\s+/g, " ").trim()` é importante para normalizar espaços e quebras de linha do HTML.
401
+
402
+
---
403
+
404
+
#### Quando usar pages/ e quando não usar
405
+
406
+
As classes `pages/` são uma convenção para manter o código organizado, não uma obrigação técnica.
407
+
408
+
| Situação | Recomendação |
409
+
|----------|-------------|
410
+
| Site com listagem + página de detalhe | Use `list.ts` + `detail.ts`|
411
+
| Página única com todo o conteúdo | Pode parsear direto no `index.ts`|
412
+
| Muitas variações de página | Crie arquivos separados por tipo |
413
+
| Plugin muito simples (1 página, 1 item) | Tudo no `index.ts` é ok |
414
+
415
+
---
416
+
417
+
#### O `ScrapeResult` — o que cada campo significa
418
+
419
+
```typescript
420
+
exportinterfaceScrapeResult {
421
+
url:string;
422
+
// URL canônica e permanente do conteúdo. Usada como chave de deduplicação
423
+
// no banco — dois itens com a mesma URL são tratados como o mesmo documento.
424
+
425
+
title:string;
426
+
// Título legível do documento. Aparece nas respostas do chat como referência.
427
+
428
+
rawText:string;
429
+
// Texto puro extraído, sem nenhuma tag HTML.
430
+
// É esse texto que será dividido em chunks e indexado vetorialmente.
431
+
// Quanto mais limpo e relevante, melhor a qualidade das respostas do Ifinho.
432
+
433
+
contentHash:string;
434
+
// MD5 do rawText. O HashCheckStep compara com o hash salvo anteriormente —
435
+
// se for igual, o item é descartado e não reprocessado.
436
+
// Sempre gere assim: crypto.createHash("md5").update(rawText).digest("hex")
437
+
438
+
category:SourceCategory;
439
+
// Classificação do conteúdo. Valores disponíveis definidos no enum do banco.
440
+
// Ex: "noticia", "edital", "documento"
441
+
442
+
sourceType:SourceType;
443
+
// Tipo da fonte. Ex: "webpage" para páginas HTML, "pdf" para arquivos PDF.
444
+
445
+
publishedAt?:Date;
446
+
// Data de publicação, quando disponível. Opcional.
447
+
448
+
metadata?:Record<string, unknown>;
449
+
// Dados extras que não cabem nos campos acima. Opcional.
0 commit comments