- Expérimentation d’un scraper web assisté par IA utilisant les structured outputs de l’API OpenAI pour extraire des tableaux HTML en données structurées ; GPT-4o a montré une qualité d’extraction meilleure qu’attendu sur certains tableaux complexes
- Sur des tableaux à structure d’affichage complexe comme les prévisions à 10 jours de Weather.com, il distingue les prévisions jour/nuit et retrouve même dans le code HTML source des valeurs invisibles à l’écran
- Sur des tableaux comme le Human Development Index de Wikipedia, où des valeurs répétées apparaissent dans des lignes fusionnées, le nombre de valeurs par colonne ne correspondait pas, rendant la reconstruction du tableau difficile, et une simple modification du prompt n’a pas suffi
- Pour réduire le coût des appels API, une tentative de génération de XPath a aussi été faite, mais les XPath produits étaient parfois erronés ou vides ; il était plus fiable d’extraire d’abord les données puis de demander les XPath à partir de ces valeurs
- 24 $ ont été dépensés en deux jours d’expérimentation ; en réduisant les attributs HTML, le nombre de caractères a été divisé par deux sans baisse visible des performances, mais le coût a limité les validations supplémentaires
Extraction de tableaux HTML avec structured outputs
- Un scraper web assisté par IA a été implémenté à l’aide de la fonctionnalité structured outputs de l’API OpenAI
- La première expérimentation consistait à envoyer une chaîne HTML à GPT-4o et à lui demander d’extraire les données tabulaires sous une forme structurée
- Le modèle Pydantic utilisé se compose de
ParsedTable, qui contient le nom du tableau et la liste des colonnes, et deParsedColumn, qui contient le nom d’une colonne et la liste de ses valeurs - Le prompt système lui attribuait le rôle d’un scraper web spécialisé dans l’extraction de données structurées à partir de tableaux HTML
- GPT-4o mini a également été testé, mais les résultats étaient nettement moins bons ; les expériences suivantes ont donc été menées avec GPT-4o
Les points forts observés sur des tableaux complexes
- Après des tableaux simples, le tableau de prévisions météo sur 10 jours de Weather.com a été utilisé comme entrée
- Ce tableau comporte une grande ligne en haut, puis 9 autres jours sous forme de lignes plus petites
- GPT-4o a reconnu la structure des prévisions jour/nuit sur les 9 jours restants et a ajouté une colonne
Day/Night - Au départ, la colonne
Conditionextraite semblait relever de l’hallucination, mais une vérification du code HTML a montré qu’elle existait réellement dans des balises invisibles à l’écran
Échec face aux lignes fusionnées
- Le tableau Human Development Index de Wikipedia semble simple en apparence, mais certaines lignes avec des valeurs répétées sont fusionnées, ce que GPT-4o n’a pas su traiter correctement
- Le modèle a bien extrait les colonnes individuellement comme demandé, mais le nombre de valeurs différait selon les colonnes, rendant le résultat difficile à représenter sous forme de tableau
- Le prompt système a été modifié avec une consigne du type « pour les lignes fusionnées, extraire plusieurs valeurs JSON afin que toutes les colonnes aient le même nombre de lignes », mais cela n’a pas fonctionné
- Un prompt demandant une extraction par ligne plutôt que par colonne n’a pas encore été testé
Les limites de la génération de XPath
- Comme appeler l’API OpenAI à chaque fois peut coûter cher, une tentative a été faite pour renvoyer des XPath au lieu des données extraites
- L’objectif était de pouvoir re-récupérer les données mises à jour sur la même page via XPath, sans nouvel appel au modèle
- Le prompt recevait le contenu HTML et les noms de colonnes, et devait renvoyer des chaînes XPath évaluables dans
driver.find_elements(By.XPATH, xpath)de Selenium - Cette approche n’était pas fiable
- GPT-4o renvoyait parfois des XPath incorrects
- Mentionner Selenium dans le prompt a un peu aidé, mais certains XPath renvoyaient encore des données erronées ou aucun résultat
Combiner extraction des données et génération de XPath
- L’essai suivant consistait à faire d’abord extraire les données par GPT-4o, puis à utiliser ce résultat comme valeurs de référence pour demander les XPath
- Cette combinaison a bien mieux fonctionné que le fait de demander uniquement des XPath dès le départ
- Il arrivait que les XPath générés ne renvoient aucun résultat ; une simple logique de nouvelle tentative a donc été ajoutée en cas d’échec
- Sur les tableaux testés, cette stratégie de retry s’est révélée efficace
- De nouveaux problèmes subsistent toutefois
- Lors de la première étape d’extraction, il arrivait que des images soient converties en texte
- Par exemple, une image de flèche vers le haut pouvait être extraite comme
arrow-upwards - La deuxième étape cherchait alors un texte qui n’existait pas réellement dans le HTML, ce qui entraînait un échec
- Ce problème n’a pas été corrigé séparément
Coût et nettoyage du HTML
- Faire du scraping web avec GPT-4o peut vite devenir coûteux, car même un petit tableau HTML contient beaucoup de caractères
- 24 $ ont déjà été dépensés en deux jours d’expérimentation
- Pour réduire les coûts, une logique de nettoyage a été ajoutée afin de supprimer les données inutiles de la chaîne HTML avant son envoi au modèle
- En supprimant tous les attributs sauf
class,idetdata-testid, qui étaient les plus souvent utilisés par les XPath générés, le nombre de caractères du tableau a été réduit de moitié - Aucune baisse de performance n’a été observée après ce nettoyage, et il est même jugé possible que la qualité d’extraction s’en trouve améliorée
- À l’heure actuelle, l’étape de génération de XPath effectue un appel au modèle pour chaque colonne du tableau
- Une amélioration consistant à générer plusieurs XPath en une seule fois serait possible, mais n’a pas encore été testée ni évaluée
Démo et pistes d’expérimentation supplémentaires
- La qualité d’extraction de GPT-4o a été jugée impressionnante, mais le coût à payer à OpenAI l’a été tout autant
- Une démo simple réalisée avec Streamlit est disponible sur https://orange-resonance-9766.ploomberapp.io
- Le code source est publié sur GitHub, avec l’avertissement qu’il ne faut pas s’attendre à un outil très abouti
- Davantage de tableaux auraient été testés, mais l’augmentation des coûts OpenAI a limité l’expérimentation à un petit nombre de cas
- Avec plus de temps, les pistes suivantes seraient explorées
- La démo actuelle est un processus ponctuel où l’utilisateur saisit une URL et un XPath initial ; une meilleure expérience utilisateur consisterait peut-être à laisser l’utilisateur cliquer sur le tableau à extraire et fournir des lignes d’exemple
- Sur des tableaux complexes, il peut être difficile d’extraire une colonne entière avec un seul XPath ; il serait intéressant de tester une approche où le LLM renvoie un programme en Python, par exemple
- Le coût d’utilisation de GPT-4o est élevé et beaucoup de données inutiles semblent être envoyées ; il serait donc utile d’expérimenter davantage les méthodes de nettoyage du HTML
1 commentaires
Avis sur Hacker News
Cela a le mieux fonctionné quand, au lieu d’envoyer directement le HTML au LLM, on le convertissait d’abord dans un format simple comme Markdown
Parmi les outils que j’ai essayés, il y a Extractus https://github.com/extractus et dom-to-semantic-markdown https://github.com/romansky/dom-to-semantic-markdown
Dans Magic Loops https://magicloops.dev/, qui tourne dans le cloud, on utilise Apify https://apify.com/ et Firecrawl https://www.firecrawl.dev/, et dans l’extension Chrome, on utilise dom-to-semantic-markdown
Pour l’instant, on expérimente une approche qui génère des XPath propres à certains sites via un flux assisté par l’utilisateur, puis n’extrait que les éléments nécessaires avant l’appel au LLM ; en simplifiant le problème, GPT-4o mini donnait déjà des résultats plutôt corrects
Comme on peut exécuter une extension Chrome dans un navigateur headless, on peut faire la conversion en Markdown sémantique directement dans le navigateur avant de sortir les données
Il existe aussi une vue temps réel du navigateur intégrable en iFrame, ce qui permet de recevoir immédiatement les retours de l’utilisateur sur le XPath en cours de génération : https://docs.browserbase.com/features/session-live-view#give...
D’après mon expérience, les performances étaient assez proches de Markdown, tandis que la transformation était plus simple et comportait moins de cas particuliers
La page trouvée étant marquée “Alpha Draft”, il ne doit pas y avoir beaucoup de corpus de contenus en Semantic Markdown, et le manque de données d’entraînement pourrait même gêner la compréhension par les LLM
Cela dit, le format reste suffisamment lisible, donc si le LLM traite les métadonnées structurées comme des explications entre parenthèses, il devrait pouvoir s’en sortir assez bien
OpenAI a récemment annoncé la Batch API, qui permet de préparer tous les prompts puis de les exécuter par lots, avec un coût réduit de 50 % : https://platform.openai.com/docs/guides/batch
Je l’ai beaucoup utilisée auparavant avec GPT-4o mini, et elle pouvait traiter 3 000 éléments en moins de 5 minutes
Cela peut être assez intéressant pour les applications qui n’ont pas besoin de temps réel
vLLM a ajouté une partie du support d’un “offline batch mode” au même format, mais n’en est pas encore à l’implémentation de l’endpoint OpenAI
C’est une fonctionnalité tellement naturelle
Pour du contenu structuré, par exemple des listes d’éléments ou des tableaux simples, on n’a pas vraiment besoin d’un LLM
J’ai récemment créé un scraper web qui fonctionne automatiquement sur n’importe quel site https://easyscraper.com ; la version initiale utilisait l’IA, mais au final des heuristiques basées sur les attributs des éléments et leur position se sont révélées plus rapides, moins chères et plus précises
Sur la plupart des sites web, les approches sans IA fonctionnent très bien ; il vaut donc mieux vérifier d’abord si l’IA est vraiment nécessaire, par exemple lorsque les données sont non structurées ou qu’il faut déduire le format de sortie à partir des données de la page
Comme l’auteur, on peut faire générer un XPath par le LLM, utiliser ensuite ce XPath pour le scraping classique au quotidien, puis demander au LLM de mettre à jour le XPath quand ça casse
Et si les données ne circulent toujours pas, ou si une étape plus loin dans le pipeline casse à cause d’un format inattendu, on peut alors prévenir un humain
Je me suis toujours demandé pourquoi ce genre d’outil n’existait pas
Je me demande s’il existe déjà quelque chose comme un « HTML reducer »
Quand on regarde le source d’une page tel quel, 90 % des tokens sont des déchets à cause du JavaScript aléatoire, des publicités, des attributs inutiles et d’une imbrication excessive destinée au rendu
En parcourant le DOM avec un parseur et en ne gardant que les nœuds contenant du texte, la structure HTML et les attributs de balises nécessaires (du type class/id), on pourrait sans doute réduire fortement les coûts, et les approches XPath pourraient aussi mieux fonctionner
J’utilise aussi Readability, mais on perd la structure du DOM, et la qualité baisse sur les sites très chargés en JavaScript ou les pages qui développent le texte avec des éléments comme « continue reading »
Je me demande quel est l’outil le plus proche d’un standard pour cet usage
Si ça intéresse du monde, nous pourrions le publier en open source, et c’était surprenant de voir à quel point la sortie du LLM s’améliorait après passage par le réducteur
En ajoutant https://r.jina.ai/ devant n’importe quelle URL, elle renvoie une version Markdown du contenu principal de cette page, adaptée à une entrée dans un LLM
Exemple : https://r.jina.ai/https://simonwillison.net/2024/Sep/2/anato... ; la page originale est https://simonwillison.net/2024/Sep/2/anatomy-of-a-textual-us...
Le code est open source et peut être exécuté soi-même : https://github.com/jina-ai/reader
Il est écrit en TypeScript et utilise Puppeteer ainsi que https://github.com/mozilla/readability
De mon côté, j’ai simplement utilisé Readability sans conversion Markdown pour extraire le titre et le corps de la page, et il existe aussi une recette pour l’exécuter avec Playwright et shot-scraper : https://shot-scraper.datasette.io/en/stable/javascript.html#...
C’est une approche reprise de vimium puis modifiée, qui distille le HTML pour ne garder que les parties importantes tout en gérant plusieurs cas limites
Je suis surpris que l’auteur ait fait 99 % du travail et de la rédaction, mais n’ait pas fait le dernier 1 % : télécharger un moteur basé sur ollama ou llama.cpp pour tester un bon LLM local
Pour ce cas d’usage, des modèles 7B ou 30B pourraient très bien faire l’affaire, avec un coût d’exécution suffisamment faible pour que GPT-4o ne soit peut-être pas nécessaire
Je commence tout juste à manipuler les LLM, et je compte aussi regarder les modèles hébergés localement
Chez Kadoa https://kadoa.com, nous faisons du web scraping automatisé par IA, et nos premières expérimentations ressemblaient à ce qui est décrit dans l’article
Nous avons commencé à l’époque où seul GPT-3, cher et lent, était disponible, et il nous fallait une solution rentable à grande échelle
Au final, plutôt que d’utiliser un LLM à chaque extraction, nous avons opté pour la génération de code afin de produire des sélecteurs CSS ou du code d’extraction XPath, puis d’adapter ensuite le code du scraper aux changements des sites web
Pour le nettoyage et la transformation, nous utilisons un petit LLM fine-tuné, et pour la validation, nous évaluons la qualité des données avec du LLM-as-a-judge, en complément de méthodes traditionnelles comme la recherche inversée
L’appliquer à quelques sources de données simples et le faire tourner de manière fiable, scalable et rentable sur des milliers de sites web sont deux problèmes complètement différents ; la bonne approche a été de combiner de l’ingénierie ETL traditionnelle avec de petites étapes LLM bien évaluées
Donner un exemple du HTML à scraper et demander un bout de code BeautifulSoup a plutôt bien fonctionné
En général, la structure que l’on veut récupérer reste stable, mais écrire soi-même la chaîne de parsing obscure qui va avec est fastidieux
Confier le parsing réel au LLM est excessif, et il y a en plus un risque que des hallucinations contaminent le résultat
Prétraiter d’abord le HTML permet d’obtenir de meilleurs résultats à moindre coût
Je crois que cela n’a pas encore été mentionné, mais personnellement j’ai obtenu de bons résultats avec trafilatura https://trafilatura.readthedocs.io/en/latest/
Comme il suffit d’envoyer du texte au LLM, on peut réduire énormément les coûts
Je l’ai aussi utilisé dans un projet récent, https://github.com/philippe2803/contentmap, une petite bibliothèque Python qui prend comme point de départ le sitemap XML d’un domaine pour créer un stockage vectoriel pour n’importe quel site web
Comme la structure HTML varie d’un domaine à l’autre, il fallait extraire uniquement le contenu réel et supprimer les balises HTML, etc. ; Trafilatura le fait pour presque toutes les URL en quelques lignes de code
C’est la première fois que je vois un article avec autant de tags orange
Chez NewsCatcher, nous faisons beaucoup de tests avec GPT-4o, et nous devons explorer plus de 100 000 sites d’actualité, puis parser le contenu des articles
Un modèle à base de règles pour extraire les données de n’importe quel article fonctionnait plutôt bien, et nous n’avons pas trouvé comment l’améliorer avec GPT
Le plus intéressant, c’est plutôt le crawling : il faut connaître tous les endroits où des articles peuvent être publiés, et il y a parfois plus de 50 sous-sections
Comme la structure propre à chaque site web ne change pas souvent, je pense que, pour beaucoup de projets, générer le code d’extraction peut suffire
Nous nous intéressons donc à la génération de code de parsing HTML avec des LLM ; si cela vous intéresse, contactez artem [at] newscatcherapi.com
Ce serait bien s’il y avait une inscription en libre-service
Le web scraping a été la vraie raison qui a poussé mon cofondateur et moi à créer openpipe.ai
GPT-4 est vraiment très bon pour ça, mais il coûte trop cher
Cela dit, il est assez facile de distiller la capacité à scraper certains types de sites dans un modèle fine-tuné beaucoup moins cher, et sur ce type de sites, il scrape de façon fiable
Nous travaillons aussi sur ce problème depuis un bon moment, et je te contacterai pour te montrer où nous en sommes