7 puntos por GN⁺ 2024-09-03 | 1 comentarios | Compartir por WhatsApp
  • Se probó un scraper web asistido por IA que extrae tablas HTML como datos estructurados usando structured outputs de la API de OpenAI, y GPT-4o mostró una calidad de extracción mejor de lo esperado en algunas tablas complejas
  • En tablas con una estructura de visualización compleja como el pronóstico de 10 días de Weather.com, pudo distinguir entre pronósticos diurnos y nocturnos, y también encontró en el HTML fuente valores que no eran visibles en pantalla
  • En tablas como el Human Development Index de Wikipedia, donde los valores repetidos aparecen en filas combinadas, la cantidad de valores por columna quedaba desalineada y era difícil reconstruir la tabla, y no se resolvió solo ajustando el prompt
  • Para reducir el costo de las llamadas a la API también se intentó la generación de XPath, pero produjo XPath incorrectos o vacíos, y fue más estable extraer primero los datos y luego pedir el XPath usando esos valores como referencia
  • En dos días se gastaron $24; al reducir atributos del HTML, la cantidad de caracteres se redujo a la mitad y no se observó una baja de rendimiento, aunque el costo limitó verificaciones adicionales

Extracción de tablas HTML con structured outputs

  • Se implementó un scraper web asistido por IA usando la función structured outputs de la API de OpenAI
  • El primer experimento consistió en pasar una cadena HTML a GPT-4o y pedirle que extrajera los datos de la tabla en un formato estructurado
  • El modelo de Pydantic usado tenía una estructura ParsedTable, que contiene el nombre de la tabla y la lista de columnas, y ParsedColumn, que contiene el nombre de la columna y la lista de valores
  • En el prompt del sistema se le asignó el rol de scraper web especializado en extraer datos estructurados de tablas HTML
  • También se probó GPT-4o mini, pero el resultado fue mucho peor, así que los experimentos posteriores se hicieron con GPT-4o

Fortalezas observadas en tablas complejas

  • Después de tablas simples, se ingresó la tabla de pronóstico del tiempo de 10 días de Weather.com
  • Esta tabla tiene una fila grande en la parte superior y los otros 9 días están compuestos por filas más pequeñas
  • GPT-4o reconoció la estructura de pronóstico diurno/nocturno de esos 9 días restantes y agregó una columna Day/Night
  • Al principio, la columna Condition extraída parecía una alucinación, pero al revisar el HTML fuente se confirmó que realmente existía en etiquetas no visibles en pantalla

Fallo en filas combinadas

  • La tabla de Human Development Index de Wikipedia parece simple a primera vista, pero como las filas con valores repetidos están combinadas, GPT-4o no pudo procesarla correctamente
  • El modelo extrajo columnas individuales según las instrucciones, pero como cada columna tenía una cantidad distinta de valores, fue difícil representar el resultado como una tabla
  • Se modificó el prompt del sistema con una instrucción del tipo “extrae las filas combinadas como múltiples valores JSON para que todas las columnas tengan la misma cantidad de filas”, pero no funcionó
  • Aún no se ha probado un prompt que indique extracción por filas en lugar de por columnas

Limitaciones del enfoque de generación de XPath

  • Como llamar a la API de OpenAI cada vez puede elevar mucho el costo, se intentó devolver XPath en lugar de los datos extraídos
  • El objetivo era volver a obtener datos actualizados de la misma página mediante scraping con XPath, sin llamar al modelo
  • El prompt se diseñó para recibir el contenido HTML y los nombres de las columnas, y devolver una cadena XPath evaluable en driver.find_elements(By.XPATH, xpath) de Selenium
  • Este enfoque no fue estable
    • Hubo casos en los que GPT-4o devolvió XPath incorrectos
    • Mencionar Selenium en la instrucción ayudó en parte, pero aun así aparecieron XPath que devolvían datos incorrectos o resultados vacíos

Combinación de extracción de datos y generación de XPath

  • El siguiente intento fue hacer que GPT-4o extrajera primero los datos y luego pedir el XPath usando ese resultado como valor de referencia
  • Esta combinación funcionó mucho mejor que pedir solo XPath desde el principio
  • Como a veces el XPath generado no devolvía resultados, se agregó una lógica simple de reintento para volver a probar cuando no hubiera resultados
  • En las tablas probadas, este enfoque de reintento fue efectivo
  • También quedaron problemas nuevos
    • En la primera etapa de extracción de datos, a veces se convertían imágenes en texto
    • Por ejemplo, una imagen de flecha hacia arriba podía extraerse como texto tipo arrow-upwards
    • La segunda etapa terminaba buscando texto que en realidad no existe en el HTML y fallaba
    • Este problema no se corrigió por separado

Costos y limpieza de HTML

  • Al hacer web scraping con GPT-4o, incluso una tabla HTML pequeña puede incluir muchos caracteres y elevar el costo
  • En dos días de experimentación ya se habían gastado $24
  • Para reducir el costo, se agregó una lógica de limpieza que elimina datos innecesarios de la cadena HTML antes de enviarla al modelo
  • Al eliminar todos los atributos excepto class, id y data-testid, que eran los más usados por el XPath generado, la cantidad de caracteres de la tabla se redujo a la mitad
  • Después de esta limpieza no se observó una caída de rendimiento, e incluso se considera posible que la calidad de extracción mejore
  • Actualmente, la etapa de generación de XPath hace una llamada al modelo por cada columna de la tabla
  • Sería posible mejorar esto generando varios XPath a la vez, pero aún no se ha intentado ni evaluado su rendimiento

Demo e ideas para más experimentos

  • La calidad de extracción de GPT-4o fue impresionante, pero también se sintió alto el costo a pagar a OpenAI
  • Se puede ver una demo simple hecha con Streamlit en https://orange-resonance-9766.ploomberapp.io
  • El código fuente está publicado en GitHub y viene con la advertencia de no esperar una herramienta muy pulida
  • Se quería probar con más tablas, pero por el aumento de costos de OpenAI solo se experimentó con unas pocas
  • Si hubiera más tiempo, se planea probar lo siguiente
    • La demo actual es un proceso de una sola vez en el que el usuario ingresa una URL y un XPath inicial; una mejor experiencia de usuario podría ser permitir que el usuario haga clic en la tabla a extraer y proporcione una fila de ejemplo
    • En tablas complejas puede ser difícil extraer toda una columna con un solo XPath, así que se quiere experimentar con pedir al LLM que devuelva un programa como Python
    • Como usar GPT-4o es caro y da la impresión de que se están enviando muchos datos innecesarios, hace falta seguir experimentando con métodos de limpieza de HTML

1 comentarios

 
GN⁺ 2024-09-03
Opiniones en Hacker News
  • Me funcionó mejor convertir primero el HTML a un formato simple como Markdown en vez de pasarlo directo al LLM
    Las herramientas que he usado son Extractus https://github.com/extractus y dom-to-semantic-markdown https://github.com/romansky/dom-to-semantic-markdown
    Dentro de Magic Loops https://magicloops.dev/, que corre en la nube, usan Apify https://apify.com/ y Firecrawl https://www.firecrawl.dev/, y en la extensión de Chrome usan dom-to-semantic-markdown
    Ahora están probando generar XPath específicos para cada sitio como flujo asistido para el usuario y luego extraer solo los elementos necesarios antes de llamar al LLM; al simplificar el problema, incluso GPT-4o mini dio resultados bastante buenos

    • También agradecería que le echaran un vistazo a lo que estamos haciendo en https://browserbase.com/
      Permite ejecutar extensiones de Chrome en un navegador headless, así que puedes hacer la conversión a Markdown semántico dentro del navegador antes de sacar los datos
      También tiene una vista en vivo del navegador en tiempo real que puede mostrarse en un iFrame, así que el usuario puede recibir retroalimentación inmediata sobre el XPath que se está generando: https://docs.browserbase.com/features/session-live-view#give...
    • Me da curiosidad si compararon convertirlo a Markdown contra simplemente quitar atributos de las etiquetas, expandir enlaces y eliminar elementos no visibles del HTML
      En mi experiencia, el rendimiento fue bastante parecido al de Markdown, pero la conversión fue más sencilla y con menos casos especiales
    • Nunca había oído hablar de Semantic Markdown https://hackmd.io/@sparna/semantic-markdown-draft, pero parece una forma de meter datos RDF dentro de un documento Markdown
      La página que encontré dice “Alpha Draft”, así que probablemente no haya mucho corpus de contenido en Semantic Markdown, y la falta de datos de entrenamiento podría dificultar que el LLM lo entienda
      Aun así, es un formato lo bastante legible como para que el LLM pueda tratar los metadatos estructurados como aclaraciones entre paréntesis y procesarlo bastante bien
    • Hice algo parecido en otro contexto: primero convertí un JSON complejo que representaba un grafo de ejecución a un formato Graphviz dot más simple antes de pasarlo al LLM, y funcionó bastante bien
  • OpenAI anunció recientemente la Batch API, así que puedes preparar todos los prompts y ejecutarlos en lote, además de reducir el costo al 50%: https://platform.openai.com/docs/guides/batch
    Antes la usaba mucho con GPT-4o mini, y podía procesar 3,000 elementos en menos de 5 minutos
    Puede ser bastante útil para aplicaciones que no necesitan tiempo real

    • Ojalá los servidores de inferencia open source también soporten pronto ese endpoint
      vLLM ya agregó algo de soporte para un “offline batch mode” del mismo estilo, pero todavía no llega a implementar el endpoint de OpenAI
    • La propuesta de OpenAI está bien, pero frente a métodos tradicionales de extracción de texto con precisión y recall parecidos, me sigue pareciendo entre una y dos órdenes de magnitud más cara
    • Fue una decisión realmente acertada de OpenAI, y ojalá otras herramientas cloud como Azure también ofrecieran esa misma función
      Se siente como algo demasiado natural
  • Para contenido estructurado, por ejemplo listas de elementos o tablas simples, en realidad no hace falta un LLM
    Hace poco construí un scraper web que funciona automáticamente en cualquier sitio https://easyscraper.com; la primera versión usaba AI, pero al final las heurísticas basadas en atributos y posición de los elementos resultaron más rápidas, más baratas y más precisas
    En la mayoría de los sitios web, un enfoque sin AI funciona muy bien, así que conviene comprobar primero si de verdad necesitas AI, por ejemplo cuando los datos son no estructurados o cuando tienes que inferir el formato de salida a partir de los datos de la página

    • Los LLM son más resistentes a las actualizaciones de los sitios web que pueden romper el scraping tradicional
      Como menciona el autor, puedes hacer que el LLM genere un XPath y luego usar ese XPath para el scraping normal; si se rompe, haces que el LLM lo actualice
      Y si aun así los datos dejan de fluir otra vez o algo se rompe más adelante en el pipeline por un formato inesperado, entonces ya le avisas a una persona
    • Es la primera vez que veo una herramienta donde puedes seleccionar directamente qué quieres scrapear
      Siempre me había preguntado por qué no existía algo así
  • Me pregunto si ya existe algo como un “HTML reducer”.
    Si miras el código fuente de una página tal cual, el 90% de los tokens es basura por JavaScript aleatorio, anuncios, atributos innecesarios y anidamiento excesivo para renderizado.
    Si se recorre con un parser del DOM y se dejan solo los nodos con texto, la estructura HTML y los atributos de etiquetas necesarios (class/id, por ejemplo), parece que se podría reducir mucho el costo, y el enfoque con XPath también podría funcionar mejor.
    También uso Readability, pero se pierde la estructura del DOM, y en sitios con mucho JavaScript o páginas donde el texto se expande, como “continue reading”, la calidad baja.
    Me pregunto cuál sería la herramienta más cercana a un estándar para este uso.

  • Me sorprende que el autor haya hecho el 99% del trabajo y de la escritura, pero no haya probado un LLM local decente descargando ese 1% restante, como ollama o un motor basado en llama.cpp.
    Para este caso de uso, incluso un modelo de 7B o 30B podría hacerlo bien, y el costo de ejecución es lo bastante bajo como para que GPT-4o no sea necesario.

    • Buena sugerencia.
      Recién estoy empezando a experimentar con LLMs, y también pienso revisar modelos alojados localmente.
  • En Kadoa https://kadoa.com hemos trabajado en scraping web automático con IA, y los experimentos iniciales fueron similares al artículo.
    Empezamos en la época en que solo existía GPT-3, caro y lento, así que necesitábamos una solución rentable a gran escala.
    Al final, en lugar de usar un LLM en cada extracción, usamos generación de código para crear selectores CSS o código de extracción con XPath, y luego hacer que el scraper ajustara ese código cuando cambiara el sitio web.
    Para la limpieza y transformación usamos LLMs pequeños ajustados con fine-tuning, y para la validación evaluamos la calidad de los datos con métodos tradicionales como búsqueda inversa, junto con LLM-as-a-judge.
    Aplicarlo a unas pocas fuentes de datos simples y hacerlo funcionar de manera estable, escalable y rentable en miles de sitios web eran problemas completamente distintos, y el enfoque correcto fue mezclar ingeniería ETL tradicional con pasos pequeños de LLM bien evaluados.

  • Si le das un ejemplo del HTML que quieres scrapear y le pides un fragmento de código con BeautifulSoup, ha funcionado bastante bien.
    Normalmente la estructura que quieres extraer se mantiene tal cual, pero escribir uno mismo las cadenas enrevesadas para parsearla es tedioso.
    Dejar el parseo real en manos del LLM es excesivo, y además existe el riesgo de que las alucinaciones contaminen el resultado.

  • Si primero preprocesas el HTML, puedes obtener resultados mejores y más baratos.
    No creo que todavía lo hayan mencionado, pero personalmente me ha ido bien con trafilatura https://trafilatura.readthedocs.io/en/latest/

    • Totalmente de acuerdo con trafilatura.
      Como al LLM solo le mandas texto, el costo puede bajar muchísimo.
      También lo usé en un proyecto reciente https://github.com/philippe2803/contentmap, una librería simple de Python para crear un vector store de cualquier sitio web usando como punto de partida el sitemap XML del dominio.
      Como la estructura HTML cambia según el dominio, había que extraer solo el contenido real y quitar etiquetas HTML y demás, y Trafilatura resolvía eso para casi todas las URLs con solo unas pocas líneas de código.
  • Es la primera vez que veo una publicación con tantas etiquetas naranjas
    En NewsCatcher están haciendo muchas pruebas con GPT-4o y, después de rastrear más de 100 mil sitios web de noticias, tienen que parsear el contenido noticioso
    Un modelo basado en reglas para extraer datos de cualquier artículo funcionaba bastante bien, y no encontraron una forma de mejorarlo más con GPT
    Lo más interesante está del lado del crawling: hay que saber todas las ubicaciones donde puede publicarse un artículo de noticias y a veces hay más de 50 subsecciones
    Como la estructura de cada sitio web no cambia con frecuencia, creen que en muchos proyectos puede bastar con generar código de extracción
    Por eso están viendo la opción de generar código de parsing de HTML con LLM, y si a alguien le interesa, puede escribir a artem [at] newscatcherapi.com

    • Me gustaría probar esto en un proyecto hobby
      Ojalá hubiera registro por cuenta propia
  • El web scraping fue el motivo real por el que mi cofundador y yo terminamos creando openpipe.ai
    GPT-4 hace este trabajo realmente bien, pero es demasiado caro
    Eso sí, la capacidad de scrapear ciertos tipos de sitios es bastante fácil de destilar en modelos fine-tuned mucho más baratos, y en ese tipo de sitios extraen contenido de forma estable y confiable

    • Kyle, debiste haberlo dicho antes
      Nosotros también llevamos bastante tiempo trabajando en este problema y te voy a contactar para mostrarte hasta dónde hemos llegado