7 pontos por GN⁺ 2024-09-03 | 1 comentários | Compartilhar no WhatsApp
  • 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, e ParsedColumn, 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 Condition extraí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, id e data-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

 
GN⁺ 2024-09-03
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

    • Também seria legal dar uma olhada no que estamos criando em https://browserbase.com/
      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...
    • Fiquei curioso se você comparou converter para Markdown com uma abordagem que apenas remove atributos de tags do HTML, expande links e remove elementos não exibidos
      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
    • Eu nunca tinha ouvido falar de Semantic Markdown https://hackmd.io/@sparna/semantic-markdown-draft, mas parece uma forma de inserir dados RDF dentro de um documento Markdown
      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
    • Fiz algo parecido em outro contexto: primeiro converti um JSON complexo que representava um grafo de execução para um formato Graphviz dot mais simples e depois o passei ao LLM; funcionou bem razoavelmente
  • 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

    • Espero que os servidores de inferência open source também passem a oferecer suporte a esse endpoint em breve
      O vLLM adicionou algum suporte a um “offline batch mode” no mesmo formato, mas ainda não chegou a implementar o endpoint da OpenAI
    • A proposta da OpenAI é boa, mas, comparada a abordagens tradicionais de extração de texto que entregam precisão e recall semelhantes, ainda acho que custa uma ou duas ordens de grandeza a mais
    • Foi uma decisão muito acertada da OpenAI, e espero que outras ferramentas de nuvem, como o Azure, ofereçam o mesmo recurso
      É 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

    • LLMs são mais resistentes a atualizações de sites que poderiam quebrar scraping comum
      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
    • É a primeira vez que vejo uma ferramenta que permite selecionar diretamente o que será raspado
      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

  • É 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

    • Boa sugestão
      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/

    • Concordo muito com trafilatura
      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

    • Quero experimentar isso em um projeto de hobby
      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

    • Kyle, eu deveria ter falado disso antes
      Nós também estamos trabalhando nesse problema há bastante tempo e vou entrar em contato para mostrar até onde chegamos