Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
Avatar de l’utilisateur
Myrville
Messages : 7
Inscription : mer. 04/mars/2026 15:42

Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par Myrville »

Bonjour à tous,
Je lance ce sujet parce que je suis en train de bricoler un petit outil personnel en PureBasic et je me pose quelques questions sur la meilleure manière de faire certaines choses.
L’idée de base est assez simple : j’aimerais créer un programme qui récupère le code HTML d’une page web et qui analyse rapidement certains éléments de structure (par exemple les balises importantes, les titres, ou la façon dont le contenu est organisé). Ce n’est pas un gros projet, plutôt un petit utilitaire pour mes tests quand je travaille sur des sites et c'est ce que j'ai vue sur ce site qui m'a un peu inspiré.
Pour récupérer la page, je pense utiliser les fonctions réseau de PureBasic, ça ne devrait pas être trop compliqué. Là où j’hésite davantage, c’est sur la partie analyse du HTML. Je me demande si certains d’entre vous ont déjà fait ce genre de chose avec PureBasic. Est-ce que vous passez par un parsing maison avec des expressions régulières, ou vous avez déjà utilisé une librairie externe pour manipuler le HTML plus proprement ?
Je me dis que je ne suis sûrement pas le premier à vouloir analyser la structure d’une page web avec PB, donc je serais curieuse d’avoir vos retours d’expérience avant de partir dans une mauvaise direction.
Si certains ont déjà tenté ce genre d’outil ou ont des pistes techniques, je suis preneuse. Merci d’avance pour vos idées !
Dernière modification par Myrville le mer. 11/mars/2026 22:32, modifié 1 fois.
boddhi
Messages : 611
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par boddhi »

Salut et bienvenue sur le site.

Ici, un petit code qui pourra peut-être t'aider. Il demandera certainement à être amélioré.
Avatar de l’utilisateur
Myrville
Messages : 7
Inscription : mer. 04/mars/2026 15:42

Re: Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par Myrville »

Cc boddhi,
Merci pour le lien et pour l’accueil.
Je suis allée regarder la discussion que tu m'as partagée et je trouve le code assez intéressant. Je trouve aussi l’approche avec les RegEx pour détecter les balises assez impressionnante, surtout pour gérer l’indentation et récupérer les attributs.
Je comprends mieux pourquoi beaucoup de gens disent que l’analyse du HTML peut vite devenir compliquée dès qu’on veut faire quelque chose d’un peu robuste.
Avatar de l’utilisateur
Myrville
Messages : 7
Inscription : mer. 04/mars/2026 15:42

Re: Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par Myrville »

boddhi a écrit : mar. 10/mars/2026 16:55 Salut et bienvenue sur le site.

Ici, un petit code qui pourra peut-être t'aider. Il demandera certainement à être amélioré.
Une autre chose qui m’a aussi interpellée dans la discussion, c’est le passage sur les problèmes liés à certains caractères Unicode. C’est justement le genre de cas auquel je n’aurais probablement pas pensé au départ.
Dans mon cas l’idée n’est pas forcément de refaire un outil aussi complet, mais plutôt de récupérer certaines informations simples dans une page (par exemple les balises de titres ou certains attributs). Du coup je me demandes encore, avec le recul, si danss ce cas précis on peut repartir toujours sur une approche RegEx pour ce type d’analyse ou on peut privilégier aujourd’hui une autre méthode.
En tout cas merci pour le partage du code (enfin de la discussion hein) ça donne déjà une bonne base de réflexion.
boddhi
Messages : 611
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par boddhi »

Myrville a écrit : [...] les problèmes liés à certains caractères Unicode. [...]
En effet, la bibliothèque RegularExpression ne gère pas correctement les caractères Unicode codés sur plus de deux octets et plus particulièrement la fonction RegularExpressionMatchLength().
Le recours à cette bibliothèque pour l'analyse des balises HTML se révèle des plus pertinents dès lors que l'on est certain de ne pas être confronté à cette problématique sinon, à moins d'une alternative que j'ignore, il convient de passer par une gestion purement textuelle, ce qui est plus chiant.
Myrville a écrit : Dans mon cas l’idée n’est pas forcément de refaire un outil aussi complet, mais plutôt de récupérer certaines informations simples dans une page (par exemple les balises de titres ou certains attributs). Du coup je me demandes encore, avec le recul, si danss ce cas précis on peut repartir toujours sur une approche RegEx pour ce type d’analyse ou on peut privilégier aujourd’hui une autre méthode.
Je ne sais pas quel est ton but précis mais ci-dessous un petit exemple qui affiche les sections et sous-sections du forum PB anglais :

Code : Tout sélectionner

EnableExplicit
; ╔═════════════════════════════════════════════════════════════════════════════╗
; ║ STRUCTURES - ENUMERATIONS - CONSTANTES - MACROS - MAPS - VARIABLES GLOBALES ║
; ╚═════════════════════════════════════════════════════════════════════════════╝
;{ ════  ENUMERATIONS        ════
;- ════  ENUMERATIONS
Enumeration RegEx
  #REGEX_HTMLBAL
  #REGEX_CSID
EndEnumeration
;}
;{ ════  CONSTANTES          ════
;- ════  CONSTANTES
#HTML_REGEXHTMLBAL="</?\w+:?\w*((\s+(\w+-?+)+:?\w?+(\s*=\s*(?:"+#DQUOTE$+".*?"+#DQUOTE$+"|'.*?'|[^'"+#DQUOTE$+">\s]+))?)+\s*|\s*)/?>"
#HTML_REGEXCSID="(?i)-[\d\w]{8}(-[\d\w]{4}){3}-[\d\w]{12}$"
;}
;-══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
; ╔════════════╗
; ║ PROCEDURES ║
; ╚════════════╝
Procedure.s Fc_Recuperation_DonneesHTMLRequeteHTTP(ArgRequeteHTTP.s,ArgMode=#PB_UTF8)
  Protected.i IDRequeteHTTP
  Protected.s TitreMSG="Requête HTTP",Statut,TexteHTML

  IDRequeteHTTP=HTTPRequest(#PB_HTTP_Get,ArgRequeteHTTP)
  If IDRequeteHTTP
    Statut=HTTPInfo(IDRequeteHTTP,#PB_HTTP_StatusCode)
    If Statut="200"
      TexteHTML=HTTPInfo(IDRequeteHTTP,#PB_HTTP_Response,ArgMode)
      FinishHTTP(IDRequeteHTTP)
      ProcedureReturn TexteHTML
    ElseIf Statut<>"0" And Statut<>"404"
      MessageRequester(TitreMSG,UnescapeString("Erreur lors du retour de la requête :\n\nCode : "+Statut+"\n\n"+ArgRequeteHTTP),#PB_MessageRequester_Error)
    EndIf
  Else
    MessageRequester(TitreMSG,UnescapeString("Echec de la requête\n\n"+ArgRequeteHTTP),#PB_MessageRequester_Error)
  EndIf
EndProcedure
Procedure   Pc_Recuperation_DonneesChaineHTML_ListeSectionsForum(ArgChaineHTML.s)
  ;{ Variables obligatoires
  Protected.i AncPosition=1         ; Position précédente dans la variable TexteFichier
  Protected.i NouvPosition          ; Position actuelle dans la variable TexteFichier après appel RegEx
  Protected.i LongChaine            ; Longueur de la chaine textuelle entre deux balises
  Protected.a Commentaire           ; Commentaire HTML en cours de traitement (Booléen)
  Protected.s ChaineHTML            ; Contenu balise HTML ou Contenu entre deux balises
  Protected.s ChaineHTMLModifiee    ; ChaineFichier expurgée des espaces
  ;}
  Protected.a BaliseTableTrouvee
  Protected.a BaliseSectionForumTrouvee
  Protected.a BaliseTitreSectionTrouvee
  Protected.a BaliseForumTrouvee
  Protected.a BaliseDetailsForumTrouvee
  Protected.a BaliseTitreForum
  Protected.a NoLigneInformation
  
  CreateRegularExpression(#REGEX_CSID,#HTML_REGEXCSID,#PB_RegularExpression_NoCase)
  ; Suppression des LF, CR & TAB
  CompilerIf #PB_Compiler_Version<=630
    ReplaceString(ArgChaineHTML,Chr(10)," ",#PB_String_InPlace)
    ReplaceString(ArgChaineHTML,Chr(13)," ",#PB_String_InPlace)
    ReplaceString(ArgChaineHTML,Chr(9)," ",#PB_String_InPlace)
  CompilerElse
    ArgChaineHTML=ReplaceString(ReplaceString(ReplaceString(ArgChaineHTML,Chr(10)," "),Chr(13)," "),Chr(9)," ")
  CompilerEndIf
  ArgChaineHTML=Trim(ArgChaineHTML)
  ; Test entête fichier HTML
  If UCase(Left(ArgChaineHTML,15))<>"<!DOCTYPE HTML>"                                       ; ← A adapter en fonction de l'entête de la page HTML
    MessageRequester("Analyse Balises et attributs HTML","Le fichier ne semble pas être un fichier HTML",#PB_MessageRequester_Error)
    ProcedureReturn
  EndIf
  ; Boucle lecture des balises
  If CreateRegularExpression(#REGEX_HTMLBAL,#HTML_REGEXHTMLBAL)
    If ExamineRegularExpression(#REGEX_HTMLBAL,ArgChaineHTML)
      While NextRegularExpressionMatch(#REGEX_HTMLBAL)
        NouvPosition=RegularExpressionMatchPosition(#REGEX_HTMLBAL)

        ; Analyse du contenu entre deux balises ou commentaires HTML "<!-- blabla -->"
        If AncPosition<>NouvPosition                                                                  ; Texte ou commentaire
          LongChaine=NouvPosition-AncPosition
          ChaineHTML=Mid(ArgChaineHTML,AncPosition,LongChaine)                                        ; ATTENTION : Ajout de +1 pour compenser un caractère Unicode dans l'en-tête
          If Left(LTrim(ChaineHTML),4)="<!--"                                                         ; Balise début commentaire
            If Right(RTrim(ChaineHTML),3)<>"-->"                                                      ; Commentaire encadrant:"<!-- blabla > <blabla> blabla <!-->"
              Commentaire=#True
            EndIf
          ElseIf Right(RTrim(ChaineHTML),3)="-->"                                                     ; Balise fin commentaire encadrant
            Commentaire=#False
          ElseIf BaliseTableTrouvee
            If BaliseTitreSectionTrouvee
              Debug "Section du forum : "+ChaineHTML
              BaliseSectionForumTrouvee=#False:BaliseTitreSectionTrouvee=#False                       ; On n'a plus besoin pour le moment de ces balises
            ElseIf BaliseTitreForum
              ChaineHTML=Trim(ChaineHTML)
              If ChaineHTML
                Select NoLigneInformation
                  Case 0
                    Debug "  Titre du forum : "+ChaineHTML
                    NoLigneInformation+1
                  Case 1
                    Debug "    Description du forum : "+ChaineHTML
                    NoLigneInformation=0
                    BaliseTitreForum=#False                                                           ; On n'a plus besoin pour le moment de cette balise et on s'évite ainsi des tests ultérieurs inutiles
                EndSelect
              EndIf
            EndIf
          EndIf
        EndIf
        ; Analyse balise HTML
        LongChaine=RegularExpressionMatchLength(#REGEX_HTMLBAL)
        ChaineHTML=RegularExpressionMatchString(#REGEX_HTMLBAL)
        If Left(ChaineHTML,19)="<div id="+Chr(34)+"page-body"+Chr(34)                                 ; Balise table sections forum
          BaliseTableTrouvee=#True
        ElseIf BaliseTableTrouvee
          If ChaineHTML="</ul>"                                                                       ; Balise fin section forum
          ElseIf ChaineHTML="<ul class="+Chr(34)+"topiclist"+Chr(34)+">"                              ; Balise section forum
            BaliseSectionForumTrouvee=#True
          ElseIf BaliseSectionForumTrouvee
            If Left(ChaineHTML,27)="<a href="+Chr(34)+"./viewforum.php?f="                            ; Balise lien hypertexte section comportant le nom de la section
              BaliseTitreSectionTrouvee=#True
            EndIf
          ElseIf ChaineHTML="<ul class="+Chr(34)+"topiclist forums"+Chr(34)+">"                       ; Balise section forum
            BaliseForumTrouvee=#True
          ElseIf BaliseForumTrouvee
            If ChaineHTML="<dl class="+Chr(34)+"row-item forum_read"+Chr(34)+">"
              BaliseDetailsForumTrouvee=#True
            ElseIf BaliseDetailsForumTrouvee
              If Left(ChaineHTML,27)="<a href="+Chr(34)+"./viewforum.php?f="
                BaliseTitreForum=#True
              EndIf
            EndIf
          ElseIf ChaineHTML="<form method="+Chr(34)+"post" +Chr(34)+"action="+Chr(34)+"./ucp.php?mode=login" +Chr(34)+"class="+Chr(34)+"headerspace panel"+Chr(34)+">"
            ; On n'a pas besoin d'analyser plus loin
            Break
          EndIf
        EndIf
        AncPosition=NouvPosition+LongChaine
      Wend
    EndIf
  EndIf
EndProcedure
;
Define.s ChaineHTMML=Fc_Recuperation_DonneesHTMLRequeteHTTP("https://www.purebasic.fr/english/index.php")
If ChaineHTMML
  Pc_Recuperation_DonneesChaineHTML_ListeSectionsForum(ChaineHTMML)
EndIf
Avatar de l’utilisateur
falsam
Messages : 7402
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par falsam »

Une solution sans regex.

Un mini parser HTML simple mais très pratique pour récupérer rapidement :
.<title>
.les balises <meta>
.Les liens <a>
.Les images <img>

Le principe : on parcourt les balises < > et on extrait leurs attributs.

Code : Tout sélectionner

;Voici un mini parser HTML simple mais très pratique  pour récupérer rapidement :
; .<title>
; .les balises <meta>
; .Les liens <a>
; .Les images <img>

;Le principe : on parcourt les balises < > et on extrait leurs attributs.

; Récupérer un attribut HTML
Procedure.s GetAttribute(tag.s, attribute.s)
  Protected pos, pos2
  Protected quote.s = Chr(34)
  
  pos = FindString(tag, attribute + "=" + quote, 1, #PB_String_NoCase)
  
  If pos
    pos + Len(attribute + "=" + quote)
    pos2 = FindString(tag, quote, pos)
    
    If pos2
      ProcedureReturn Mid(tag, pos, pos2-pos)
    EndIf
  EndIf
  ProcedureReturn ""
EndProcedure

; Parser HTML simple
Procedure ParseHTML(html.s)
  Protected pos1, pos2
  Protected tag.s
  
  pos1 = 1
  
  While pos1
  
    pos1 = FindString(html, "<", pos1)
    If pos1 = 0 : Break : EndIf
    
    pos2 = FindString(html, ">", pos1)
    If pos2 = 0 : Break : EndIf
    
    tag = Mid(html, pos1+1, pos2-pos1-1)
    
    ; ----- META DESCRIPTION -----
    If FindString(tag, "meta", 1, #PB_String_NoCase)
      
      If LCase(GetAttribute(tag,"name")) = "description"
        Debug "Description : " + GetAttribute(tag,"content")
      EndIf
      
      If LCase(GetAttribute(tag,"name")) = "title"
        Debug "title : " + GetAttribute(tag,"content")
      EndIf
      
    EndIf
    
    ; ----- LIENS -----
    If Left(LCase(tag),1) = "a"
      If GetAttribute(tag,"href") <> ""
        Debug "Lien : " + GetAttribute(tag,"href")
      EndIf
    EndIf
    
    ; ----- IMAGES -----
    If FindString(tag, "img", 1, #PB_String_NoCase)
      
      Debug "Image : " + GetAttribute(tag,"src")
      
    EndIf
    
    pos1 = pos2 + 1
    
  Wend
EndProcedure

; Extraction du TITLE quand il se trouve dans une balise <title>
Procedure.s GetTitle(html.s)
  Protected pos1, pos2
  
  pos1 = FindString(html, "<title>",1,#PB_String_NoCase)
  
  If pos1
    pos1 + 7
    pos2 = FindString(html, "</title>",pos1,#PB_String_NoCase)
    
    If pos2
      ProcedureReturn Mid(html,pos1,pos2-pos1)
    EndIf
  EndIf
  
  ProcedureReturn ""
EndProcedure

;-
;- Zone de test
;-
Define url.s = "https://purebasic.com"
Define *mem
Define html.s

*mem = ReceiveHTTPMemory(url)

If *mem
  html = PeekS(*mem, MemorySize(*mem), #PB_UTF8)
  FreeMemory(*mem)
  
  Debug "TITLE : " + GetTitle(html)
  
  ParseHTML(html)
Else
  Debug "Erreur téléchargement"
EndIf
C'est pas mal mais volontairement pas assez robuste pour montrer le principe.
Pourquoi pas assez robuste ? Ce code simple fonctionne par exemple avec name="description" mais fonctionnera pas avec name = "description" 🤪
Configuration : Windows 11 Famille 64-bit - PB 6.23 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
falsam
Messages : 7402
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par falsam »

Une version un peu plus robuste. La procédure GetAttribute(tag.s, attribute.s) est modifiée.

■ Elle gère correctement :
name="description"
name = "description"
name='description'
name=description
et évite les faux positifs (data-name, itemname, etc.)

Code : Tout sélectionner

;Voici un mini parser HTML simple mais très pratique  pour récupérer rapidement :
; .<title>
; .les balises <meta>
; .Les liens <a>
; .Les images <img>

;Le principe : on parcourt les balises < > et on extrait leurs attributs.

; Récupérer un attribut HTML
Procedure.s GetAttribute(tag.s, attribute.s)
  Protected pos, i, c.s
  Protected valueStart, valueEnd
  Protected quote.s
  
  pos = FindString(LCase(tag), LCase(attribute), 1)
  If pos = 0
    ProcedureReturn ""
  EndIf
  
  ; vérifier que ce n'est pas une sous-chaîne (ex: data-name)
  If pos > 1
    c = Mid(tag, pos-1, 1)
    If FindString("abcdefghijklmnopqrstuvwxyz-", LCase(c), 1)
      ProcedureReturn ""
    EndIf
  EndIf
  
  ; trouver =
  pos = FindString(tag, "=", pos)
  If pos = 0
    ProcedureReturn ""
  EndIf
  
  i = pos + 1
  
  ; ignorer les espaces
  While Mid(tag, i, 1) = " "
    i + 1
  Wend
  
  c = Mid(tag, i, 1)
  
  ; valeur entre guillemets
  If c = Chr(34) Or c = "'"
    
    quote = c
    valueStart = i + 1
    valueEnd = FindString(tag, quote, valueStart)
    
    If valueEnd
      ProcedureReturn Mid(tag, valueStart, valueEnd - valueStart)
    EndIf
    
  Else
    
    ; valeur sans guillemets
    valueStart = i
    
    While i <= Len(tag)
      
      c = Mid(tag, i, 1)
      
      If c = " " Or c = ">"
        valueEnd = i
        Break
      EndIf
      
      i + 1
      
    Wend  
    ProcedureReturn Mid(tag, valueStart, valueEnd - valueStart)
  EndIf
  
  ProcedureReturn ""
EndProcedure

; Parser HTML simple
Procedure ParseHTML(html.s)
  Protected pos1, pos2
  Protected tag.s
  
  pos1 = 1
  
  While pos1
  
    pos1 = FindString(html, "<", pos1)
    If pos1 = 0 : Break : EndIf
    
    pos2 = FindString(html, ">", pos1)
    If pos2 = 0 : Break : EndIf
    
    tag = Mid(html, pos1+1, pos2-pos1-1)
    
    ; ----- META DESCRIPTION -----
    If FindString(tag, "meta", 1, #PB_String_NoCase)
      
      If LCase(GetAttribute(tag,"name")) = "description"
        Debug "Description : " + GetAttribute(tag,"content")
      EndIf
      
      If LCase(GetAttribute(tag,"name")) = "title"
        Debug "title : " + GetAttribute(tag,"content")
      EndIf
      
    EndIf
    
    ; ----- LIENS -----
    If Left(LCase(tag),1) = "a"
      If GetAttribute(tag,"href") <> ""
        Debug "Lien : " + GetAttribute(tag,"href")
      EndIf
    EndIf
    
    ; ----- IMAGES -----
    If FindString(tag, "img", 1, #PB_String_NoCase)
      Debug "Image : " + GetAttribute(tag,"src")
    EndIf
    
    pos1 = pos2 + 1
  Wend
EndProcedure

; Extraction du TITLE quand il se trouve dans une balise <title>
Procedure.s GetTitle(html.s)
  Protected pos1, pos2
  
  pos1 = FindString(html, "<title>",1,#PB_String_NoCase)
  
  If pos1
    pos1 + 7
    pos2 = FindString(html, "</title>",pos1,#PB_String_NoCase)
    
    If pos2
      ProcedureReturn Mid(html,pos1,pos2-pos1)
    EndIf
  EndIf
  
  ProcedureReturn ""
EndProcedure

;-
;- Zone de test
;-
Define url.s = "https://falsam.com"
Define *mem
Define html.s

*mem = ReceiveHTTPMemory(url)

If *mem
  html = PeekS(*mem, MemorySize(*mem), #PB_UTF8)
  FreeMemory(*mem)
  
  Debug "TITLE : " + GetTitle(html)
  
  ParseHTML(html)
Else
  Debug "Erreur téléchargement"
EndIf
➡️ Un peu plus de détail sur ton projet. un outil SEO ?
Configuration : Windows 11 Famille 64-bit - PB 6.23 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
Myrville
Messages : 7
Inscription : mer. 04/mars/2026 15:42

Re: Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par Myrville »

boddhi a écrit : mar. 10/mars/2026 22:53 En effet, la bibliothèque RegularExpression ne gère pas correctement les caractères Unicode codés sur plus de deux octets et plus particulièrement la fonction RegularExpressionMatchLength().
Le recours à cette bibliothèque pour l'analyse des balises HTML se révèle des plus pertinents dès lors que l'on est certain de ne pas être confronté à cette problématique sinon, à moins d'une alternative que j'ignore, il convient de passer par une gestion purement textuelle, ce qui est plus chiant.

Je ne sais pas quel est ton but précis mais ci-dessous un petit exemple qui affiche les sections et sous-sections du forum PB anglais :

Code : Tout sélectionner

EnableExplicit
; ╔═════════════════════════════════════════════════════════════════════════════╗
; ║ STRUCTURES - ENUMERATIONS - CONSTANTES - MACROS - MAPS - VARIABLES GLOBALES ║
; ╚═════════════════════════════════════════════════════════════════════════════╝
;{ ════  ENUMERATIONS        ════
;- ════  ENUMERATIONS
Enumeration RegEx
  #REGEX_HTMLBAL
  #REGEX_CSID
EndEnumeration
;}
;{ ════  CONSTANTES          ════
;- ════  CONSTANTES
#HTML_REGEXHTMLBAL="</?\w+:?\w*((\s+(\w+-?+)+:?\w?+(\s*=\s*(?:"+#DQUOTE$+".*?"+#DQUOTE$+"|'.*?'|[^'"+#DQUOTE$+">\s]+))?)+\s*|\s*)/?>"
#HTML_REGEXCSID="(?i)-[\d\w]{8}(-[\d\w]{4}){3}-[\d\w]{12}$"
;}
;-══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
; ╔════════════╗
; ║ PROCEDURES ║
; ╚════════════╝
Procedure.s Fc_Recuperation_DonneesHTMLRequeteHTTP(ArgRequeteHTTP.s,ArgMode=#PB_UTF8)
  Protected.i IDRequeteHTTP
  Protected.s TitreMSG="Requête HTTP",Statut,TexteHTML

  IDRequeteHTTP=HTTPRequest(#PB_HTTP_Get,ArgRequeteHTTP)
  If IDRequeteHTTP
    Statut=HTTPInfo(IDRequeteHTTP,#PB_HTTP_StatusCode)
    If Statut="200"
      TexteHTML=HTTPInfo(IDRequeteHTTP,#PB_HTTP_Response,ArgMode)
      FinishHTTP(IDRequeteHTTP)
      ProcedureReturn TexteHTML
    ElseIf Statut<>"0" And Statut<>"404"
      MessageRequester(TitreMSG,UnescapeString("Erreur lors du retour de la requête :\n\nCode : "+Statut+"\n\n"+ArgRequeteHTTP),#PB_MessageRequester_Error)
    EndIf
  Else
    MessageRequester(TitreMSG,UnescapeString("Echec de la requête\n\n"+ArgRequeteHTTP),#PB_MessageRequester_Error)
  EndIf
EndProcedure
Procedure   Pc_Recuperation_DonneesChaineHTML_ListeSectionsForum(ArgChaineHTML.s)
  ;{ Variables obligatoires
  Protected.i AncPosition=1         ; Position précédente dans la variable TexteFichier
  Protected.i NouvPosition          ; Position actuelle dans la variable TexteFichier après appel RegEx
  Protected.i LongChaine            ; Longueur de la chaine textuelle entre deux balises
  Protected.a Commentaire           ; Commentaire HTML en cours de traitement (Booléen)
  Protected.s ChaineHTML            ; Contenu balise HTML ou Contenu entre deux balises
  Protected.s ChaineHTMLModifiee    ; ChaineFichier expurgée des espaces
  ;}
  Protected.a BaliseTableTrouvee
  Protected.a BaliseSectionForumTrouvee
  Protected.a BaliseTitreSectionTrouvee
  Protected.a BaliseForumTrouvee
  Protected.a BaliseDetailsForumTrouvee
  Protected.a BaliseTitreForum
  Protected.a NoLigneInformation
  
  CreateRegularExpression(#REGEX_CSID,#HTML_REGEXCSID,#PB_RegularExpression_NoCase)
  ; Suppression des LF, CR & TAB
  CompilerIf #PB_Compiler_Version<=630
    ReplaceString(ArgChaineHTML,Chr(10)," ",#PB_String_InPlace)
    ReplaceString(ArgChaineHTML,Chr(13)," ",#PB_String_InPlace)
    ReplaceString(ArgChaineHTML,Chr(9)," ",#PB_String_InPlace)
  CompilerElse
    ArgChaineHTML=ReplaceString(ReplaceString(ReplaceString(ArgChaineHTML,Chr(10)," "),Chr(13)," "),Chr(9)," ")
  CompilerEndIf
  ArgChaineHTML=Trim(ArgChaineHTML)
  ; Test entête fichier HTML
  If UCase(Left(ArgChaineHTML,15))<>"<!DOCTYPE HTML>"                                       ; ← A adapter en fonction de l'entête de la page HTML
    MessageRequester("Analyse Balises et attributs HTML","Le fichier ne semble pas être un fichier HTML",#PB_MessageRequester_Error)
    ProcedureReturn
  EndIf
  ; Boucle lecture des balises
  If CreateRegularExpression(#REGEX_HTMLBAL,#HTML_REGEXHTMLBAL)
    If ExamineRegularExpression(#REGEX_HTMLBAL,ArgChaineHTML)
      While NextRegularExpressionMatch(#REGEX_HTMLBAL)
        NouvPosition=RegularExpressionMatchPosition(#REGEX_HTMLBAL)

        ; Analyse du contenu entre deux balises ou commentaires HTML "<!-- blabla -->"
        If AncPosition<>NouvPosition                                                                  ; Texte ou commentaire
          LongChaine=NouvPosition-AncPosition
          ChaineHTML=Mid(ArgChaineHTML,AncPosition,LongChaine)                                        ; ATTENTION : Ajout de +1 pour compenser un caractère Unicode dans l'en-tête
          If Left(LTrim(ChaineHTML),4)="<!--"                                                         ; Balise début commentaire
            If Right(RTrim(ChaineHTML),3)<>"-->"                                                      ; Commentaire encadrant:"<!-- blabla > <blabla> blabla <!-->"
              Commentaire=#True
            EndIf
          ElseIf Right(RTrim(ChaineHTML),3)="-->"                                                     ; Balise fin commentaire encadrant
            Commentaire=#False
          ElseIf BaliseTableTrouvee
            If BaliseTitreSectionTrouvee
              Debug "Section du forum : "+ChaineHTML
              BaliseSectionForumTrouvee=#False:BaliseTitreSectionTrouvee=#False                       ; On n'a plus besoin pour le moment de ces balises
            ElseIf BaliseTitreForum
              ChaineHTML=Trim(ChaineHTML)
              If ChaineHTML
                Select NoLigneInformation
                  Case 0
                    Debug "  Titre du forum : "+ChaineHTML
                    NoLigneInformation+1
                  Case 1
                    Debug "    Description du forum : "+ChaineHTML
                    NoLigneInformation=0
                    BaliseTitreForum=#False                                                           ; On n'a plus besoin pour le moment de cette balise et on s'évite ainsi des tests ultérieurs inutiles
                EndSelect
              EndIf
            EndIf
          EndIf
        EndIf
        ; Analyse balise HTML
        LongChaine=RegularExpressionMatchLength(#REGEX_HTMLBAL)
        ChaineHTML=RegularExpressionMatchString(#REGEX_HTMLBAL)
        If Left(ChaineHTML,19)="<div id="+Chr(34)+"page-body"+Chr(34)                                 ; Balise table sections forum
          BaliseTableTrouvee=#True
        ElseIf BaliseTableTrouvee
          If ChaineHTML="</ul>"                                                                       ; Balise fin section forum
          ElseIf ChaineHTML="<ul class="+Chr(34)+"topiclist"+Chr(34)+">"                              ; Balise section forum
            BaliseSectionForumTrouvee=#True
          ElseIf BaliseSectionForumTrouvee
            If Left(ChaineHTML,27)="<a href="+Chr(34)+"./viewforum.php?f="                            ; Balise lien hypertexte section comportant le nom de la section
              BaliseTitreSectionTrouvee=#True
            EndIf
          ElseIf ChaineHTML="<ul class="+Chr(34)+"topiclist forums"+Chr(34)+">"                       ; Balise section forum
            BaliseForumTrouvee=#True
          ElseIf BaliseForumTrouvee
            If ChaineHTML="<dl class="+Chr(34)+"row-item forum_read"+Chr(34)+">"
              BaliseDetailsForumTrouvee=#True
            ElseIf BaliseDetailsForumTrouvee
              If Left(ChaineHTML,27)="<a href="+Chr(34)+"./viewforum.php?f="
                BaliseTitreForum=#True
              EndIf
            EndIf
          ElseIf ChaineHTML="<form method="+Chr(34)+"post" +Chr(34)+"action="+Chr(34)+"./ucp.php?mode=login" +Chr(34)+"class="+Chr(34)+"headerspace panel"+Chr(34)+">"
            ; On n'a pas besoin d'analyser plus loin
            Break
          EndIf
        EndIf
        AncPosition=NouvPosition+LongChaine
      Wend
    EndIf
  EndIf
EndProcedure
;
Define.s ChaineHTMML=Fc_Recuperation_DonneesHTMLRequeteHTTP("https://www.purebasic.fr/english/index.php")
If ChaineHTMML
  Pc_Recuperation_DonneesChaineHTML_ListeSectionsForum(ChaineHTMML)
EndIf
cc boddhi,

Merci pour l’exemple de code, je l'ai observé de près et je trouve que c’est instructif de voir comment tu structures l’analyse des balises avec les expressions régulières et la gestion des différentes sections.
Je vois mieux la logique quand on veut parcourir toute la structure d’une page et récupérer des éléments précis. Dans mon cas je vais probablement commencer plus simple, mais ton exemple me donne déjà une bonne idée de la manière dont on peut organiser ce type de traitement.
merci aussi pour la précision sur les limitations Unicode de la bibliothèque, c’est ce genre de détail qui peut éviter pas mal de surprises.
Avatar de l’utilisateur
Myrville
Messages : 7
Inscription : mer. 04/mars/2026 15:42

Re: Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par Myrville »

falsam a écrit : mer. 11/mars/2026 15:51 Une solution sans regex.

Un mini parser HTML simple mais très pratique pour récupérer rapidement :
.<title>
.les balises <meta>
.Les liens <a>
.Les images <img>

Le principe : on parcourt les balises < > et on extrait leurs attributs.

Code : Tout sélectionner

;Voici un mini parser HTML simple mais très pratique  pour récupérer rapidement :
; .<title>
; .les balises <meta>
; .Les liens <a>
; .Les images <img>

;Le principe : on parcourt les balises < > et on extrait leurs attributs.

; Récupérer un attribut HTML
Procedure.s GetAttribute(tag.s, attribute.s)
  Protected pos, pos2
  Protected quote.s = Chr(34)
  
  pos = FindString(tag, attribute + "=" + quote, 1, #PB_String_NoCase)
  
  If pos
    pos + Len(attribute + "=" + quote)
    pos2 = FindString(tag, quote, pos)
    
    If pos2
      ProcedureReturn Mid(tag, pos, pos2-pos)
    EndIf
  EndIf
  ProcedureReturn ""
EndProcedure

; Parser HTML simple
Procedure ParseHTML(html.s)
  Protected pos1, pos2
  Protected tag.s
  
  pos1 = 1
  
  While pos1
  
    pos1 = FindString(html, "<", pos1)
    If pos1 = 0 : Break : EndIf
    
    pos2 = FindString(html, ">", pos1)
    If pos2 = 0 : Break : EndIf
    
    tag = Mid(html, pos1+1, pos2-pos1-1)
    
    ; ----- META DESCRIPTION -----
    If FindString(tag, "meta", 1, #PB_String_NoCase)
      
      If LCase(GetAttribute(tag,"name")) = "description"
        Debug "Description : " + GetAttribute(tag,"content")
      EndIf
      
      If LCase(GetAttribute(tag,"name")) = "title"
        Debug "title : " + GetAttribute(tag,"content")
      EndIf
      
    EndIf
    
    ; ----- LIENS -----
    If Left(LCase(tag),1) = "a"
      If GetAttribute(tag,"href") <> ""
        Debug "Lien : " + GetAttribute(tag,"href")
      EndIf
    EndIf
    
    ; ----- IMAGES -----
    If FindString(tag, "img", 1, #PB_String_NoCase)
      
      Debug "Image : " + GetAttribute(tag,"src")
      
    EndIf
    
    pos1 = pos2 + 1
    
  Wend
EndProcedure

; Extraction du TITLE quand il se trouve dans une balise <title>
Procedure.s GetTitle(html.s)
  Protected pos1, pos2
  
  pos1 = FindString(html, "<title>",1,#PB_String_NoCase)
  
  If pos1
    pos1 + 7
    pos2 = FindString(html, "</title>",pos1,#PB_String_NoCase)
    
    If pos2
      ProcedureReturn Mid(html,pos1,pos2-pos1)
    EndIf
  EndIf
  
  ProcedureReturn ""
EndProcedure

;-
;- Zone de test
;-
Define url.s = "https://purebasic.com"
Define *mem
Define html.s

*mem = ReceiveHTTPMemory(url)

If *mem
  html = PeekS(*mem, MemorySize(*mem), #PB_UTF8)
  FreeMemory(*mem)
  
  Debug "TITLE : " + GetTitle(html)
  
  ParseHTML(html)
Else
  Debug "Erreur téléchargement"
EndIf
C'est pas mal mais volontairement pas assez robuste pour montrer le principe.
Pourquoi pas assez robuste ? Ce code simple fonctionne par exemple avec name="description" mais fonctionnera pas avec name = "description" 🤪
falsam merci pour l’exemple. C'est quand même intéressant l’idée du mini parser basé simplement sur le parcours des balises < >. J'y avait pas pensé. Pour récupérer des éléments comme les liens, les images ou les meta çà va être rapide à mettre en place.
Ton approche pour extraire directement les attributs est assez lisible pour comprendre le principe.
Avatar de l’utilisateur
Myrville
Messages : 7
Inscription : mer. 04/mars/2026 15:42

Re: Petit outil pour analyser rapidement le contenu HTML d’une page web : vous feriez comment en PureBasic ?

Message par Myrville »

falsam j'avais déjà mis ma réponse précédente avant de voir ton message avec la version améliorée. La gestion des différents cas (name="...", name = "...", quotes simples, etc.) rend déjà la chose beaucoup plus solide. Je comprends mieux où peuvent apparaître les petits pièges quand on commence à parser du HTML de manière simple.
Et pour répondre à ta question, oui l’idée derrière mon petit outil est surtout d’analyser rapidement certains éléments d’une page (titres, meta, liens…) pour mes tests quand je travaille sur des sites. Rien de très gros, plutôt un utilitaire maison.
Merci en tout cas pour les exemples, ça me donne de bonnes pistes pour démarrer.
Répondre