Durante os primeiros dias como Engenheiro de Dados (que remontam a 2016), tive a responsabilidade de coletar dados de diferentes websites. Web scraping consiste em utilizar ferramentas automatizadas para obter grandes quantidades de dados dos sites, geralmente a partir de seu HTML.
I remember building around the application, digging into the HTML code, and trying to figure out the best solutions for scraping all the data. One of my main challenges was dealing with frequent changes to the websites: for example, the Amazon pages I was scraping changed every one to two weeks.
Uma ideia que me ocorreu quando comecei a ler sobre Modelos de Linguagem Grandes (LLMs) foi: “Posso evitar todos esses problemas que enfrentei usando LLMs para estruturar dados de páginas da web?”
Vamos ver se consigo.
Ferramentas e Técnicas de Web Scraping
Naquela época, as principais ferramentas que eu estava usando eram Requests, BeautifulSoup, e Selenium. Cada serviço tem um propósito diferente e é direcionado a diferentes tipos de ambientes web.
- Requests é uma biblioteca Python que pode ser usada para facilmente fazer requisições HTTP. Essa biblioteca realiza operações GET e POST contra URLs fornecidas nas requisições. É frequentemente usada para buscar conteúdo HTML que pode ser analisado por BeautifulSoup.
- BeautifulSoup é uma biblioteca Python para análise de documentos HTML e XML, constrói uma árvore de análise a partir da fonte da página que permite acessar facilmente os vários elementos na página. Geralmente, é emparelhado com outras bibliotecas como Requests ou Selenium que fornecem o código HTML de origem.
- Selenium é empregado principalmente para sites que envolvem muito JavaScript. Ao contrário de BeautifulSoup, Selenium não analisa simplesmente o código HTML: interage com sites simulando ações do usuário, como cliques e rolagem. Isso facilita a extração de dados de sites que criam conteúdo dinamicamente.
Essas ferramentas eram indispensáveis quando eu tentava extrair dados de sites. No entanto, também apresentavam alguns desafios: código, tags e elementos estruturais tinham que ser atualizados regularmente para acomodar mudanças na disposição do site, complicando a manutenção a longo prazo.
O que são Modelos de Linguagem Grandes (LLMs)?
Modelos de Linguagem Grandes (LLMs) são programas de computador de próxima geração que podem aprender lendo e analisando vastas quantidades de dados de texto. Nesta era, eles possuem a incrível capacidade de escrever em uma narrativa semelhante ao humano, tornando-os agentes eficientes para processar linguagem e compreender a linguagem humana. A habilidade excepcional se destacou nesse tipo de situação, onde o contexto do texto era realmente importante.
Integrando LLMs em raspagem de dados web
O processo de raspagem de dados pode ser bastante otimizado ao incorporar LLMs. Precisamos pegar o código HTML de uma página da web e alimentá-lo no LLM, que extrairá os objetos aos quais se refere. Portanto, essa tática facilita a manutenção, já que a estrutura de marcação pode evoluir, mas o conteúdo em si geralmente não muda.
Veja como seria a arquitetura de um sistema integrado como este:
- Obtendo HTML: Utilize ferramentas como Selenium ou Requests para buscar o conteúdo HTML de uma página da web. O Selenium pode lidar com conteúdo dinâmico carregado com JavaScript, enquanto o Requests é adequado para páginas estáticas.
- Analisando HTML: Usando BeautifulSoup, podemos analisar esse HTML como texto, removendo assim o ruído do HTML (rodapé, cabeçalho, etc.).
- Criando modelos Pydantic: Digite o modelo Pydantic no qual vamos realizar a raspagem. Isso garante que os dados digitados e estruturados sigam os esquemas pré-definidos.
- Gerando prompts para LLMs: Crie um prompt que informará ao LLM quais informações devem ser extraídas.
- Processamento pelo LLM: O modelo lê o HTML, o entende e aplica as instruções para processamento e estruturação de dados.
- Saída de dados estruturados: O LLM fornecerá a saída na forma de objetos estruturados definidos pelo modelo Pydantic.
Este fluxo de trabalho ajuda a transformar HTML (dados não estruturados) em dados estruturados usando LLMs, resolvendo problemas como design não padronizado ou modificação dinâmica da fonte HTML da web.
Integração de LangChain com BeautifulSoup e Pydantic
Esta é a página web estática selecionada para o exemplo. A ideia é raspar todas as atividades listadas lá e apresentá-las de maneira estruturada.
Este método irá extrair o HTML bruto da página web estática e limpá-lo antes de o LLM processá-lo.
from bs4 import BeautifulSoup
import requests
def extract_html_from_url(url):
try:
# Buscar conteúdo HTML da URL usando requests
response = requests.get(url)
response.raise_for_status() # Raise an exception for bad responses (4xx and 5xx)
# Analisar conteúdo HTML usando BeautifulSoup
soup = BeautifulSoup(response.content, "html.parser")
excluded_tagNames = ["footer", "nav"]
# Excluir elementos com nomes de tags 'footer' e 'nav'
for tag_name in excluded_tagNames:
for unwanted_tag in soup.find_all(tag_name):
unwanted_tag.extract()
# Processar a sopa para manter hrefs nos tags de âncora
for a_tag in soup.find_all("a"):
href = a_tag.get("href")
if href:
a_tag.string = f"{a_tag.get_text()} ({href})"
return ' '.join(soup.stripped_strings) # Return text content with preserved hrefs
except requests.exceptions.RequestException as e:
print(f"Error fetching data from {url}: {e}")
return None
O próximo passo é definir os objetos Pydantic que vamos raspar da página web. Dois objetos precisam ser criados:
Activity
: Este é um objeto Pydantic que representa todos os metadados relacionados à atividade, com seus atributos e tipos de dados especificados. Marcamos alguns campos comoOptional
no caso de não estarem disponíveis para todas as atividades. Fornecer uma descrição, exemplos e quaisquer metadados ajudará o LLM a ter uma definição melhor do atributo.ActivityScraper
: Este é o wrapper Pydantic em torno doActivity
. O objetivo deste objeto é garantir que o LLM entenda que é necessário raspar várias atividades.
from pydantic import BaseModel, Field
from typing import Optional
class Activity(BaseModel):
title: str = Field(description="The title of the activity.")
rating: float = Field(description="The average user rating out of 10.")
reviews_count: int = Field(description="The total number of reviews received.")
travelers_count: Optional[int] = Field(description="The number of travelers who have participated.")
cancellation_policy: Optional[str] = Field(description="The cancellation policy for the activity.")
description: str = Field(description="A detailed description of what the activity entails.")
duration: str = Field(description="The duration of the activity, usually given in hours or days.")
language: Optional[str] = Field(description="The primary language in which the activity is conducted.")
category: str = Field(description="The category of the activity, such as 'Boat Trip', 'City Tours', etc.")
price: float = Field(description="The price of the activity.")
currency: str = Field(description="The currency in which the price is denominated, such as USD, EUR, GBP, etc.")
class ActivityScrapper(BaseModel):
Activities: list[Activity] = Field("List of all the activities listed in the text")
Finalmente, temos a configuração do LLM. Utilizaremos a biblioteca LangChain, que fornece uma excelente ferramenta para começar.
A key component here is the PydanticOutputParser
. Essentially, this will translate our object into instructions, as illustrated in the Prompt
, and also parse the output of the LLM to retrieve the corresponding list of objects.
from langchain.prompts import PromptTemplate
from langchain.output_parsers import PydanticOutputParser
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
load_dotenv()
llm = ChatOpenAI(temperature=0)
output_parser = PydanticOutputParser(pydantic_object = ActivityScrapper)
prompt_template = """
You are an expert making web scrapping and analyzing HTML raw code.
If there is no explicit information don't make any assumption.
Extract all objects that matched the instructions from the following html
{html_text}
Provide them in a list, also if there is a next page link remember to add it to the object.
Please, follow carefulling the following instructions
{format_instructions}
"""
prompt = PromptTemplate(
template=prompt_template,
input_variables=["html_text"],
partial_variables={"format_instructions": output_parser.get_format_instructions}
)
chain = prompt | llm | output_parser
O passo final é invocar a cadeia e recuperar os resultados.
url = "https://www.civitatis.com/es/budapest/"
html_text_parsed = extract_html_from_url(url)
activites = chain.invoke(input={
"html_text": html_text_parsed
})
activites.Activities
Aqui está como os dados se parecem. Leva 46 segundos para raspar toda a página da web.
[Activity(title='Paseo en barco al anochecer', rating=8.4, reviews_count=9439, travelers_count=118389, cancellation_policy='Cancelación gratuita', description='En este crucero disfrutaréis de las mejores vistas de Budapest cuando se viste de gala, al anochecer. El barco es panorámico y tiene partes descubiertas.', duration='1 hora', language='Español', category='Paseos en barco', price=21.0, currency='€'),
Activity(title='Visita guiada por el Parlamento de Budapest', rating=8.8, reviews_count=2647, travelers_count=34872, cancellation_policy='Cancelación gratuita', description='El Parlamento de Budapest es uno de los edificios más bonitos de la capital húngara. Comprobadlo vosotros mismos en este tour en español que incluye la entrada.', duration='2 horas', language='Español', category='Visitas guiadas y free tours', price=27.0, currency='€')
...
]
Demo e Repositório Completo
I have created a quick demo using Streamlit available here.
Na primeira parte, você é apresentado ao modelo. Você pode adicionar tantas linhas quanto precisar e especificar o nome, tipo e descrição de cada atributo. Isso automaticamente gerará um modelo Pydantic a ser usado no componente de raspagem da web.
A próxima parte permite que você insira uma URL e raspe todos os dados clicando no botão na página da web. Um botão de download aparecerá quando a raspagem estiver concluída, permitindo que você baixe os dados no formato JSON.
Sinta-se à vontade para brincar com isso!
Conclusão
O LLM proporciona novas possibilidades para extrair dados de forma eficiente de dados não estruturados como sites, PDFs, etc. A automatização da raspagem da web pelo LLM não apenas economizará tempo, mas também garantirá a qualidade dos dados recuperados.
No entanto, enviar HTML bruto para o LLM pode aumentar o custo de tokens e torná-lo ineficiente. Como o HTML frequentemente inclui vários tags, atributos e conteúdo, o custo pode aumentar rapidamente.
Portanto, é crucial pré-processar e limpar o HTML, removendo todo o metadado desnecessário e informações não utilizadas. Essa abordagem ajudará a usar o LLM como extraidor de dados para sites, mantendo um custo decente.
A ferramenta certa para o trabalho certo!
Source:
https://dzone.com/articles/enhancing-web-scraping-with-large-language-models