Playwright, ChatGPT e Web Scraping
Revolucionando a Extração de Dados com Inteligência Artificial
Introdução
No final de 2022 e início do ano 2023 foram marcados pelos avanços em AI, principalmente com o lançamento da OpenAI do ChatGPT, onde sua primeira release foi em 30 de novembro de 2022. E isso revolucionou os mercados, profissões e produtos. Para os mais pessimistas a inteligência artificial vai tomar todos os empregos e foi dada a escalada na corrida pelos modelos generativos. De uma forma ou de outra fomos impactados por essa ferramenta e a forma como trabalhamos na área de dados mudou ou você se adapta ou apenas se adapta.
A internet é um grande repositório de dados públicos e as técnicas de web scraping são eficientes para treinar modelos grandes, os chamados LLMs (Large Language Models) não sou nenhum especialista nessa área e o objetivo deste artigo não é aprofundar nesses modelos. Para aprofundar no tema recomendo fortemente a leitura desse artigo.
Na medida que acompanhava as notícias e os podcasts sempre fiquei refletindo como usar isso no campo de web scraping e suas aplicações. Hoje apresento uma aplicação utilizando raspagem de dados e uso da API dos modelos da OpenAI.
LLM, LangChain e Playwright
Lançado em 22 de outubro de 2022 como a própria documentação explica, LangChain é um estrutura para desenvolver aplicativos que são fortemente abastecidos por modelos grandes de linguagem. Esse framework simplifica o processo de integração de diversos bancos de dados vetoriais no seu aplicativo.
O LangChain facilita a integração com modelos de inteligência artificial, estruturas e plataformas de destaque, como OpenAI, Hugging Face. Ele permite a conexão desses recursos com fontes externas de dados e informações, que podem ser obtidas através de web scraping. Com esta ferramenta, é possível criar sequências de comandos de modo que os modelos de IA compreendam e executem tarefas específicas dadas pelo usuário, fornecendo respostas precisas e realizando as ações necessárias de acordo com o contexto. Para mais detalhes sobre a arquitetura, funcionamento e integração você pode consultar a documentação oficial.
Abaixo segue um exemplo em termos gerais de como funcionaria os componentes do LangChain:
Componentes LangChain
- Agentes: representam a lógica de interação os os usuários ou fontes de entrada.
- LLMs: O coração dos modelos de linguagem natural.
- Conectores: Módulos ou APIs que realizam a comunicação comas diferentes fontes de dados e é aqui que facilita a integração com sistemas externos
- Processadores de texto: aqui são as ferramentas que vão auxiliar na preparação do pós processamento de texto.
Fluxo de Integração
- Entrada do usuário: O usuário insere o promt de texto e os agentes fazem a captura.
- Processamento LLMs: Os agentes enviam a solicitação ao LLM para interpretar a resposta.
- Interação conectores: O LLM interage com os conectores e acessa as informações externa ou realiza ações específicas.
- Pós- processamento: Resposta do texto ao usuário
O Playwright é reconhecido por sua capacidade de automação de navegadores web, oferecendo meios para realizar interações complexas e renderizar conteúdo dinâmico de forma eficiente. Em contrapartida, o ChatGPT se apresenta como um avançado modelo de processamento de linguagem natural, apto tanto a compreender quanto a gerar textos, o que o torna um instrumento valioso para inferências baseadas em texto. A combinação dessas tecnologias proporciona um conjunto de ferramentas extremamente poderosa para a automação e enriquecimento de tarefas de web scraping e interações com conteúdo web, abrindo novos horizontes para o desenvolvimento de aplicativos inteligentes.
Aplicações
Sabemos que o dia a dia de um desenvolvedor scraper não é fácil e isso representa um desafio considerável devido a muitos fatores. O primeiro deles é a constante evolução dos layouts e os conteúdos que os sites possuem, pois são dinâmicos. Isso obriga ao desenvolvedor a uma adaptação e atualização frequentes dos scripts de raspagem para que possam eficientemente extrair informações, ou um código bem modular levando em conta as novas estruturas de tags, ou novos formatos que surgem ao longo da jornada, mas graças a tecnologia que está em constante evolução pode nos ajudar a melhorar o processo como construímos nossas aplicações.
Na documentação oficial do LangChain, um exemplo interessante é destacado. Ele descreve um caso em Python que cria um buscador de artigos de notícias. Neste exemplo, o usuário define um esquema com regras específicas para extrair informações, focando apenas em texto. Esse esquema determina o tipo de informação a ser extraída, permitindo uma busca e coleta de dados eficaz e direcionada. Este exemplo ilustra a flexibilidade e a capacidade do LangChain em lidar com tarefas complexas de raspagem de dados na web.
Vamos criar um exemplo simples de como usar o ChatGPT para realizar o parsing dos dados solicitando a ele que nos informe os seletores passando apenas o HTML para a ele, nossa aplicação será em Javascript. Em nosso exemplo inicial usamos o site teste Scrapeme. Para copiar o conteúdo HTML basta inspecionar e selecionar o objeto no Google Chrome e realizar o Copy outerHTML, assim conseguimos importar a página.
Com a interface do ChatGPT aberta podemos dar um prompt com esse HTML ao modelo e ele nos retornará os seletores conforme imagem abaixo.
Nesse exemplo basicamente pedimos ao modelo os seletores nome do produto, preço, estoque, código sku e a categoria. Com esses seletores em mãos vamos aplicar em nossa função Javascript.
const axios=require("axios");
const cheerio=require("cheerio");
async function fetchData(){
const response = await axios.get("https://scrapeme.live/shop/Squirtle/");
const html = response.data;
const $ = cheerio.load(html);
const container = $("#primary")
const _name = ".product_title.entry-title";
const _price = "p.price > span";
const _description = ".woocommerce-product-details__short-description";
const _stock = "p.stock";
const _sku = ".sku_wrapper .sku";
const _categories = ".posted_in a";
for (const c of container) {
const extractData = {
name: $(c).find(_name).text(),
price: $(c).find(_price).text(),
description: $(c).find(_description).text().trim(),
stock: $(c).find(_stock).text(),
sku: $(c).find(_sku).text(),
categories: $(c).find(_categories).text()
};
console.log(extractData);
}
}
fetchData();
Copiamos os nossos seletores para nosso código de teste e vemos o resultado logo abaixo. O código completo está aqui nesse repositório.
Para os executar este exemplo você deve ter em seu ambiente, a versão do Node.js mais recente, Axios para realizar requisições e Cheerio para fazer o parsing do HTML.
Um exemplo mais avançado
Nas seções anteriores discutimos os conceitos básicos de LLM e Playwright como também uma aplicação simples utilizando a interface do ChatGPT, mas ainda não utilizamos todo o poder das ferramentas e a integração com a API e é no próximo tópico que vamos realizar essa integração.
Mas antes de iniciar vamos precisar criar uma conta na OpenAI para termos acesso às credenciais da API, o que é diferente de ter uma conta no ChatGPT, lá a orientação é depois de criar salvar as chaves em um local seguro. Lembrando que é de responsabilidade de cada usuário como usar com moderação e verificar os termos de uso dos sites. Criamos um plano simples de $10.00 dólares em créditos. Além da conta criada será necessário instalação do Node.js em seu ambiente e as bibliotecas descritas no package.json:
{
"devDependencies": {
"@playwright/test": "^1.40.0",
"@types/node": "^20.10.0"
},
"dependencies": {
"cheerio": "^1.0.0-rc.12",
"dotenv": "^16.3.1",
"langchain": "^0.0.198",
"openai": "^4.20.0"
Fluxo de Execução
Abaixo podemos visualizar o fluxo de execução do nosso sistema de web scraping via regras de extração:
- Inicialmente, um navegador da web interage com o framework Playwright, que é especializado na automação de tarefas de navegador. O Playwright é utilizado para recuperar o código HTML de uma página da web, o que é indicado pela função getHTML. Existe uma sub etapa essencial de modificação no HTML recuperado, aqui é aplicado uma espécie de filtro para dar ao modelo mais precisão visto no passo adicional “Filter HTML”, que está conectado ao getHTML, isto é, necessitamos truncar o texto e remover o que for inútil.
- Paralelamente, temos o processo de scraping que utiliza esquemas predefinidos e o framework Langchain para analisar os dados estruturados. Essa análise, ou parsing, é uma operação fundamental para interpretar e converter os dados brutos em um formato mais acessível e manipulável para o processamento subsequente.
- Em seguida, o modelo de processamento de linguagem natural, faz uso da API da OpenAI. A função dessa interação é extract informações específicas, que podem ser dados ou insights valiosos, dos dados estruturados previamente pelo processo de scraping.
- As informações extraídas são então encaminhadas para uma função denominada processURL. Essa função encapsula as funções de acessar a web e a parte de extração dos dados usando o modelo da OpenAI .
- Por fim, na última etapa conseguimos coletar todos os resultados da URL analisada.
Resultados
Depois de definir alguns conceitos e apresentar o pipeline de execução passamos aos resultados alcançados.
Inicialmente definimos as regras utilizadas pelo modelo em uma espécie de prompt de busca. Criamos um construtor definimos as regras, isto é o schema rules.
const z = require("zod");
class zSchema {
//classe que define o construtor
constructor () {
this.schema = z.object({
"product_name": z.string().optional(),
"product_current_price":z.string().optional(),
"product_previous_price":z.string().optional(),
"image_url":z.string().optional(),
"product_sku":z.string().optional(),
})
}
//funcao que retorna o esqueleto do esquema
getSchema() {
return this.schema;
}
};
module.exports = { zSchema};
Para todos os experimentos, optamos pelo uso do modelo GPT-3.5-turbo-1106 devido ao seu custo-benefício e desempenho com um limite máximo de 256 tokens por solicitação. O modelo tem custo relativamente baixo e conseguimos rodar os exemplos sem maiores problemas em relação ao consumo da API como pode ser visto no gráfico abaixo.
Para o primeiro experimento, utilizamos um site mais simples, amplamente reconhecido pela comunidade em testes de scraping. O processo envolveu a especificação de um seletor css único e o contêiner de localização dos itens que queremos que o modelo faça a busca textual.
No segundo exemplo, introduzimos um nível maior de complexidade, especialmente no que diz respeito à aos tipos de preços que estão disponíveis na página, isto é, aqui nesse exemplo queremos buscar a capacidade do modelo realmente identificar o preço correto já definido nas regras. A seguir, detalhamos as regras aplicadas e os resultados do segundo site.
No terceiro exemplo, enfrentamos o desafio ainda maior de trabalhar com um site de complexidade elevada, isto é, caracterizado por um tempo de carregamento maior de conteúdo e um arquivo HTML mais denso.
Para o último exemplo buscamos considerar o contexto onde não existe o objeto preço anterior para analisar o comportamento de busca do modelo, que em muitos casos poderia criar um preço inexistente.
Os resultados obtidos são bastante promissores para um teste preliminar dentro do escopo da nossa proposta. É importante levar em consideração as limitações do modelo, especialmente em cenários onde o HTML não é completamente carregado ou o seletor especificado não corresponde à área alvo. Nessas situações, o modelo pode gerar conteúdo inexistente na página de forma automática, como observado nas execuções do terceiro exemplo. Para alcançar dados confiáveis, foi necessário realizar cinco tentativas, durante as quais o modelo gerou preços aleatórios e incluiu links sem qualquer relação lógica nos resultados. Além disso, o processo de definição das regras de extração exigiu ajustes até que conseguíssemos identificar as palavras-chave mais assertivas.
Olhando para o futuro, uma possibilidade seria explorar a aplicação de variantes avançadas do modelo do GPT-4 assim observaríamos como diferentes configurações de tokens e a inclusão de mais variáveis poderiam influenciar os resultados. Uma adição interessante seria a implementação do carregamento dinâmico de JavaScript, para avaliar como essa funcionalidade afeta a performance e a precisão do modelo em situações de extração de dados mais complexas. É óbvio que quanto mais performance no modelo mais custos para o nosso cenário o que tornaria algo interessante de se ver. Uma outra questão é adicionar uma camada de proxy e ver o comportamento de toda a nossa aplicação.
Para além disso, podemos perceber como essas ferramentas juntas podem tornar a vida do desenvolvedor mais fácil e mais produtivo focando no produto ao cliente e automatizando ainda vez mais o processo de coleta de dados. Isso proporcionará para a industria de scraping maiores crescimentos dentro desse mercado e abrindo um leque de aplicação com os LLMs em scraping.
Abaixo temos a sessão dos repositórios utilizados no projeto e as referências.