Raspagem web com GPT-4o: poderosa, mas cara
(blancas.io)- Um scraper web assistido por IA foi testado usando structured outputs da API da OpenAI para extrair tabelas HTML como dados estruturados, e o GPT-4o mostrou uma qualidade de extração melhor do que o esperado em algumas tabelas complexas
- Em tabelas com estrutura de exibição complexa, como a previsão de 10 dias do Weather.com, ele separou previsões de dia/noite e encontrou no HTML-fonte valores que não apareciam na tela
- Em tabelas com linhas mescladas com valores repetidos, como a do Human Development Index da Wikipedia, a contagem de valores por coluna ficou desalinhada e a reconstrução da tabela se tornou difícil, sem solução apenas com ajustes de prompt
- Para reduzir o custo de chamadas da API, também foi testada a geração de XPath, mas surgiram XPaths incorretos ou vazios; pedir XPath com base em valores já extraídos primeiro se mostrou mais estável
- Foram gastos $24 em dois dias de experimento; ao reduzir atributos no HTML, a contagem de caracteres caiu pela metade sem perda visível de desempenho, mas o custo limitou validações adicionais
Extração de tabelas HTML com structured outputs
- Foi implementado um scraper web assistido por IA com o recurso structured outputs da API da OpenAI
- O primeiro experimento consistiu em enviar uma string HTML ao GPT-4o e pedir que extraísse os dados da tabela em formato estruturado
- O modelo Pydantic usado tinha a estrutura
ParsedTable, com nome da tabela e lista de colunas, eParsedColumn, com nome da coluna e lista de valores - No prompt de sistema, foi atribuído ao modelo o papel de scraper web especializado em extrair dados estruturados de tabelas HTML
- O GPT-4o mini também foi testado, mas os resultados foram bem piores, então os experimentos seguintes seguiram com o GPT-4o
Pontos fortes mostrados em tabelas complexas
- Depois de tabelas simples, foi usada como entrada a tabela de previsão do tempo de 10 dias do Weather.com
- Essa tabela tem uma linha grande no topo, e os outros 9 dias são compostos por linhas menores
- O GPT-4o reconheceu a estrutura de previsão de dia/noite dos 9 dias restantes e adicionou uma coluna
Day/Night - No início, a coluna
Conditionextraída parecia uma alucinação, mas ao verificar o HTML-fonte ficou claro que ela realmente existia em tags não visíveis na tela
Falha causada por linhas mescladas
- A tabela de Human Development Index da Wikipedia parece simples à primeira vista, mas como linhas com valores repetidos estão mescladas, o GPT-4o não conseguiu lidar corretamente com ela
- O modelo extraiu as colunas individualmente conforme instruído, mas cada coluna acabou com uma quantidade diferente de valores, dificultando representar o resultado como tabela
- O prompt de sistema foi alterado para algo como “extraia linhas mescladas em vários valores JSON para que todas as colunas tenham o mesmo número de linhas”, mas isso não funcionou
- Ainda não foi testado um prompt que instrua a extração por linha, em vez de por coluna
Limites da geração de XPath
- Como chamar a API da OpenAI a cada vez pode sair caro, também foi testado retornar XPath em vez dos dados extraídos
- O objetivo era reutilizar o XPath para raspar dados atualizados da mesma página sem precisar chamar o modelo novamente
- O prompt foi montado para receber o conteúdo HTML e os nomes das colunas e retornar uma string XPath avaliável em
driver.find_elements(By.XPATH, xpath)do Selenium - Essa abordagem não foi estável
- Em alguns casos, o GPT-4o retornou XPath incorreto
- Mencionar Selenium ajudou um pouco, mas ainda surgiram XPaths que retornavam dados errados ou resultados vazios
Combinando extração de dados e geração de XPath
- A tentativa seguinte foi fazer o GPT-4o extrair os dados primeiro e depois pedir o XPath usando esse resultado como valor de referência
- Essa combinação funcionou muito melhor do que pedir apenas o XPath desde o início
- Como em alguns casos o XPath gerado não retornava resultado, foi adicionada uma lógica simples de retry para tentar novamente quando não houvesse retorno
- Nas tabelas testadas, esse mecanismo de repetição funcionou bem
- Ainda restam novos problemas
- Às vezes, a extração de dados da primeira etapa convertia imagens em texto
- Por exemplo, uma imagem de seta para cima podia ser extraída como um texto como
arrow-upwards - Na segunda etapa, o sistema então tentava encontrar um texto que não existia de fato no HTML e falhava
- Esse problema não foi corrigido separadamente
Custos e limpeza do HTML
- Fazer raspagem web com GPT-4o pode elevar o custo, porque até tabelas HTML pequenas podem conter muitos caracteres
- Em dois dias de experimento, já foram gastos $24
- Para reduzir custos, foi adicionada uma lógica de limpeza para remover dados desnecessários da string HTML antes de enviá-la ao modelo
- Ao remover todos os atributos, exceto
class,idedata-testid, que eram os mais usados pelo XPath gerado, a quantidade de caracteres da tabela caiu pela metade - Depois dessa limpeza, não foi observada perda de desempenho, e há a impressão de que a qualidade da extração pode até ter melhorado
- Atualmente, a etapa de geração de XPath faz uma chamada ao modelo para cada coluna da tabela
- Também seria possível melhorar isso gerando vários XPaths de uma vez, mas isso ainda não foi testado nem avaliado em desempenho
Demo e ideias para mais experimentos
- A qualidade de extração do GPT-4o foi impressionante, mas o custo pago à OpenAI também pesou bastante
- Uma demo simples feita com Streamlit pode ser vista em https://orange-resonance-9766.ploomberapp.io
- O código-fonte está disponível no GitHub, com o aviso de que não se deve esperar uma ferramenta muito polida
- Havia interesse em testar mais tabelas, mas o aumento do custo da OpenAI limitou os experimentos a apenas algumas
- Se houvesse mais tempo, os próximos testes seriam:
- A demo atual é um processo único em que o usuário informa a URL e o XPath inicial, mas uma experiência melhor poderia permitir que o usuário clicasse na tabela a ser extraída e fornecesse linhas de exemplo como experiência do usuário
- Em tabelas complexas, pode ser difícil extrair uma coluna inteira com um único XPath, então valeria testar pedir que o LLM retornasse um programa em algo como Python
- Como o custo de usar GPT-4o é alto e parece que muitos dados desnecessários estão sendo enviados, ainda é preciso experimentar mais os métodos de limpeza do HTML
1 comentários
Opiniões do Hacker News
Funcionou melhor quando, antes de colocar o HTML diretamente no LLM, ele era convertido primeiro para um formato simples como Markdown
Entre as ferramentas que usei estão Extractus https://github.com/extractus e dom-to-semantic-markdown https://github.com/romansky/dom-to-semantic-markdown
No Magic Loops https://magicloops.dev/, que roda na nuvem, eles usam Apify https://apify.com/ e Firecrawl https://www.firecrawl.dev/; na Chrome Extension, usam dom-to-semantic-markdown
Agora estou experimentando gerar XPaths específicos para cada site por meio de um fluxo assistido pelo usuário e, antes de chamar o LLM, extrair apenas os elementos necessários; ao simplificar o problema, até o GPT-4o mini se saiu bem razoavelmente
Como é possível executar uma Chrome Extension em um navegador headless, dá para fazer a conversão para Markdown semântico dentro do navegador antes de extrair os dados para fora
Também há uma visualização em tempo real do navegador via iFrame, então o usuário pode receber feedback imediato sobre o XPath que está sendo gerado: https://docs.browserbase.com/features/session-live-view#give...
Pela minha experiência, o desempenho foi bem parecido com Markdown, e a transformação era mais fácil e com menos casos de exceção
A página que encontrei é um “Alpha Draft”, então provavelmente não há um grande corpus de conteúdo em Semantic Markdown, e a falta de dados de treinamento pode até atrapalhar o entendimento do LLM
Ainda assim, é um formato bastante legível, então, se o LLM tratar os metadados estruturados como explicações entre parênteses, acho que pode processar isso muito bem
A OpenAI anunciou recentemente a Batch API, que permite preparar todos os prompts e executá-los em lote, reduzindo também o custo em 50%: https://platform.openai.com/docs/guides/batch
Usei bastante isso antes com o GPT-4o mini e consegui processar 3.000 itens em menos de 5 minutos
Pode ser bem útil para aplicações que não precisam de tempo real
O vLLM adicionou algum suporte a um “offline batch mode” no mesmo formato, mas ainda não chegou a implementar o endpoint da OpenAI
É uma funcionalidade natural demais
Para conteúdo estruturado, como listas de itens ou tabelas simples, não é necessário usar LLM
Recentemente criei um web scraper https://easyscraper.com que funciona automaticamente em qualquer site; a versão inicial foi feita com IA, mas no fim heurísticas baseadas em atributos e posição dos elementos se mostraram mais rápidas, baratas e precisas
Na maioria dos sites, uma abordagem sem IA funciona muito bem, então é melhor verificar primeiro se a IA é realmente necessária, como em casos em que os dados são não estruturados ou em que o formato de saída precisa ser inferido a partir dos dados da página
Como o autor, você pode fazer o LLM gerar o XPath e, normalmente, usar esse XPath para scraping convencional; se quebrar, usar o LLM para atualizar o XPath
Ainda assim, se os dados não voltarem a fluir ou se a pipeline quebrar mais adiante por causa de um formato inesperado, aí você avisa uma pessoa
Sempre me perguntei por que uma ferramenta assim não existia
Fico me perguntando se já existe algo como um “HTML reducer”
Se você olha o código-fonte da página do jeito que está, 90% dos tokens são lixo por causa de JavaScript aleatório, anúncios, atributos desnecessários e aninhamento excessivo para renderização
Ao percorrer tudo com um parser de DOM e manter apenas nós com texto, a estrutura HTML e os atributos de tags necessários (algo como class/id), parece que daria para reduzir bastante o custo, e abordagens com XPath também poderiam funcionar melhor
Também uso Readability, mas ele perde a estrutura do DOM, e a qualidade cai em sites com muito JavaScript ou em páginas que expandem o texto, como “continue reading”
Fico curioso para saber qual é a ferramenta mais próxima de um padrão para esse uso
Se houver interesse, podemos abrir o código, e foi surpreendente ver como a saída do LLM fica muito melhor depois de passar pelo redutor
Basta colocar https://r.jina.ai/ antes de qualquer URL, e ela retorna uma versão em Markdown do conteúdo principal daquela página, adequada para enviar a um LLM
Um exemplo é https://r.jina.ai/https://simonwillison.net/2024/Sep/2/anato..., e a página original é https://simonwillison.net/2024/Sep/2/anatomy-of-a-textual-us...
O código é open source, então dá para executar por conta própria: https://github.com/jina-ai/reader
Ele foi escrito em TypeScript e usa Puppeteer e https://github.com/mozilla/readability
Eu usei apenas o Readability, sem conversão para Markdown, para extrair o título da página e o corpo do texto, e também há uma receita para executá-lo com Playwright e shot-scraper: https://shot-scraper.datasette.io/en/stable/javascript.html#...
É uma abordagem trazida do vimium e modificada, que destila o HTML para manter apenas as partes importantes, ao mesmo tempo em que trata vários casos excepcionais
É surpreendente que o autor tenha feito 99% do trabalho e da escrita, mas não tenha feito o 1% restante: baixar um motor baseado em ollama ou llama.cpp e testar um bom LLM local
Para este caso de uso, modelos 7B ou 30B também podem se sair bem, e o custo de execução é baixo o bastante para talvez não precisar do GPT-4o
Estou começando agora a mexer com LLMs e também pretendo dar uma olhada em modelos hospedados localmente
Na Kadoa https://kadoa.com, temos feito web scraping automatizado com IA, e os experimentos iniciais foram parecidos com os do artigo
Começamos na época em que só havia o GPT-3, caro e lento, então precisávamos de uma solução econômica em larga escala
No fim, em vez de usar um LLM a cada vez na etapa de extração, passamos a usar geração de código para criar código de extração com seletores CSS ou XPath, e depois fazer o código do scraper se ajustar às mudanças nos sites
Para limpeza e transformação, usamos um pequeno LLM com fine-tuning e, para validação, avaliamos a qualidade dos dados com LLM-as-a-judge, junto com métodos tradicionais como busca reversa
Aplicar isso a algumas fontes de dados simples e rodar de forma estável, escalável e econômica em milhares de sites são problemas completamente diferentes; a abordagem certa foi misturar engenharia de ETL tradicional com pequenas etapas de LLM bem avaliadas
Ao fornecer um exemplo do HTML que eu queria raspar e pedir um trecho de código com BeautifulSoup, funcionou muito bem
Em geral, a estrutura que se quer raspar permanece igual, mas escrever manualmente as strings complicadas para fazer o parsing é tedioso
Deixar o parsing de fato por conta do LLM é exagero, além de haver o risco de as alucinações contaminarem o resultado
Se você pré-processar o HTML primeiro, consegue resultados melhores e mais baratos
Acho que ainda não foi mencionado, mas pessoalmente tive bons resultados com trafilatura https://trafilatura.readthedocs.io/en/latest/
Como basta enviar apenas o texto para o LLM, dá para reduzir enormemente o custo
Também usei em um projeto recente, https://github.com/philippe2803/contentmap, uma biblioteca Python simples que cria um armazenamento vetorial para qualquer site usando o sitemap XML do domínio como ponto de partida
Como a estrutura HTML varia de domínio para domínio, era preciso extrair apenas o conteúdo real e remover tags HTML etc.; o Trafilatura fez isso para quase todas as URLs com poucas linhas de código
É a primeira vez que vejo um post com tantas tags laranja assim
Na NewsCatcher, estamos fazendo muitos testes com o GPT-4o e precisamos rastrear mais de 100 mil sites de notícias e depois fazer o parsing do conteúdo jornalístico
Um modelo baseado em regras para extrair dados de qualquer artigo funcionou muito bem, e não encontramos uma forma de melhorá-lo com GPT
O mais interessante é a parte de crawling: é preciso saber todos os lugares onde uma notícia pode ser publicada e, às vezes, há mais de 50 subseções
Como a estrutura de cada site não muda com frequência, acho que, em muitos projetos, gerar código de extração pode ser suficiente
Por isso estamos olhando para a geração de código de parsing de HTML com LLM; se tiver interesse, entre em contato em artem [at] newscatcherapi.com
Seria bom se houvesse cadastro self-service
Web scraping foi o motivo real que levou meu cofundador e eu a criar a atual openpipe.ai
GPT-4 é realmente muito bom nisso, mas é caro demais
Dito isso, a capacidade de fazer scraping de certos tipos de site é relativamente fácil de destilar em um modelo ajustado por fine-tuning muito mais barato, e ele faz a coleta de forma estável nesses tipos de site
Nós também estamos trabalhando nesse problema há bastante tempo e vou entrar em contato para mostrar até onde chegamos