5
votes

Réparer les crochets de balises HTML à l'aide de Python

J'ai beaucoup de texte HTML, comme

text = re.sub( r'</sub ', r'</sub>', text) 
text = re.sub( r' /sub>', r'</sub>', text)

Parfois, des balises HTML, comme , code > n'ont pas leurs crochets . Cela peut entraîner des difficultés plus tard dans le code. Maintenant, ma question est: Comment puis-je détecter intelligemment ces crochets manquants et les réparer?

Le texte correct serait:

text = 'Hello, how <sub> are </sub> you ? There is a <sub> small error </sub>  in this text here and another one <sub> here </sub> .'

Bien sûr, je pourrais coder en dur toutes les configurations de parenthèses possibles, mais cela prendrait trop de temps car il y a plus d'erreurs comme ça dans mon texte.

text = 'Hello, how <sub> are </sub> you ? There is a <sub> small error </sub  in this text here and another one <sub> here /sub> .'

... et le code précédent pourrait ajouter un autre crochet pour corriger les échantillons.


3 commentaires

Comment savez-vous qu'une «parenthèse» manquante n'a pas été intentionnellement omise?


Je ne pense pas que le code que vous avez ajouterait un autre crochet pour corriger les échantillons car vous avez un espace inclus.


Le point de la question est le fait que le nom de la balise «sous» pourrait être n'importe quel mot, n'est-ce pas?


4 Réponses :


3
votes

essayez cette

Hello, how <sub> are </sub> you ? There is a <sub> small error </sub> in this text here and another one <sub> here </sub> .

la sortie sera

text = 'Hello, how <sub> are </sub> you ? There is a <sub> small error </sub  in this text here and another one <sub> here /sub> .'

text_list = text.split();
for i, word in enumerate(text.split()):
    if 'sub' in word:
        if '<' != word[0]:
            word = '<' + word
        if '>' != word[-1]:
            word += '>'
        text_list[i] = word

result = ' '.join(text_list)
print(result)


1 commentaires

Merci beaucoup pour votre réponse !



2
votes

Je chercherais une expression comme sub. *? / sub . Il ne suppose rien du tout sur les crochets, mais il ne correspondra qu'à sub qui est associé à / sub , ce qui diminue la probabilité de fausses correspondances. Le quantificateur réticent *? est nécessaire pour éviter qu'il ne corresponde au premier sous et au dernier / sub :

Ajoutez à cela le fait que les groupes de capture sont autorisés par re.sub :

text = re.sub('<?sub>?(.*?)<?/sub>?', '<sub>\\1</sub>', text)


0 commentaires

1
votes

using regex;

Hello, how <sub> are </sub> you ? There is a <sub> small error </sub>  in this text here and another one <sub> here </sub> .

sortie:

import re
text = 'Hello, how <sub are </sub> you ? There is a <sub> small error </sub  in this text here and another one <sub> here /sub> .'

text = re.sub(r'<?[^/]sub>?', '<sub>', text)
text = re.sub(r'<?/sub>?', '</sub>', text)

print(text)

Modifier : comment ça marche, p >

re.sub (modèle de recherche, réplication, chaîne) il recherchera un modèle de chaîne et remplacez-le par un autre

' [^ /] sub>?' pour expliquer la signification de ce modèle le démontera:

"" signifie rechercher un texte 'peut-être' contient '

[^ /] signifie qu'il ne contient pas de '/'

'sub' qu'il doit contient le mot 'sous'

'>?' il contient peut-être '>'


1 commentaires

Merci beaucoup pour l'explication!



1
votes

Excellente question! Voici une solution qui ne code pas en dur le mot sub et fonctionne sur des balises arbitraires, tant qu'il n'y a qu'un seul crochet manquant et que la balise HTML ne contient aucun attribut (sinon, comment savoir quand la balise devrait être fermé? Nous pourrions utiliser le format attr = "" , mais cela devient risqué). De plus, les balises n'ont pas besoin d'être séparées par des espaces comme le montre votre exemple, ce qui n'est pas le cas habituel en HTML.


Code

<li>
    <a>
        About Us
        <span>
            Learn more about Stack Overflow the company
        </span>
    </a>
</li>
<li>
    <a>
        Business
        <span>
            Learn more about hiring developers or posting ads with us
        </span>
    </a>
</li>
repaired matches original? True

Test

if __name__ == "__main__":
    original = '''<li>
    <a>
        About Us
        <span>
            Learn more about Stack Overflow the company
        </span>
    </a>
</li>
<li>
    <a>
        Business
        <span>
            Learn more about hiring developers or posting ads with us
        </span>
    </a>
</li>'''
    corrupted = '''li>
    <a
        About Us
        span>
            Learn more about Stack Overflow the company
        </span
    </a
/li>
<li
    <a
        Business
        span>
            Learn more about hiring developers or posting ads with us
        /span>
    </a
</li'''

    print(repair_tags(corrupted))
    print("repaired matches original?", repair_tags(corrupted) == original)

Output

import re

def repair(text, backwards=False):
    left_bracket, right_bracket = "<", ">"

    if backwards:
        left_bracket, right_bracket = ">", "<"

    i = 0

    while i < len(text):
        if text[i] == left_bracket:
            j = i + 1

            while j < len(text) and re.match(r"[/\w]", text[j]):
                j += 1

                if backwards and text[j-1] == "/":
                    break

            if j >= len(text) or text[j] != right_bracket:
                text = text[:j] + right_bracket + text[j:]

            i = j

        i += 1

    return text

def repair_tags(html):
    return repair(repair(html[::-1], True)[::-1])

Comment ça marche

Parcourez le recherche de chaîne pour un caractère entre crochets. Quand on en trouve un, avancez jusqu'à ce que la fin de la chaîne soit atteinte ou qu'un caractère non-mot soit rencontré. Placez un crochet compagnon si la recherche atteint la fin de la chaîne ou si le caractère non-mot actuel n'est pas le crochet compagnon correct.

Ensuite, effectuez la même opération sur la chaîne inversée, en changeant les crochets cibles et faire un léger ajustement pour casser sur le / lors de la recherche de l'emplacement de la balise de fermeture.

La complexité temporelle n'est pas grande à cause de la construction de chaînes. Il y a sans aucun doute une expression régulière simple, alors prenez-la comme une preuve de concept.

Essayez-le!


10 commentaires

C'est une excellente réponse ! Merci beaucoup d'avoir réfléchi à la façon de résoudre ce problème en général !! Cela a résolu mon problème.


Avez-vous une idée pour un meilleur titre pour cette question qui décrirait mieux le problème?


Bien sûr pas de problème. Cette solution est probablement exagérée, mais je pense que c'est une question intéressante qui peut être généralisée beaucoup plus loin. Quant au titre, je ne vois aucun problème avec lui, mais peut-être que "chaînes" -> "crochets" et "texte" -> "HTML est un peu plus spécifique, en supposant que je comprends bien la question.


Merci pour votre contribution. J'ai changé le titre et la description en conséquence.


repair est juste une aide pour repair_tags () , alors appelez-le avec repair_tags ('Si W / i> cela fonctionne?') . Le wrapper est nécessaire pour effectuer les traversées avant et arrière. Ouais, c'est un peu moche (j'espère que j'aurai une chance de le nettoyer à l'avenir). J'adore ces questions d'analyse HTML!


Oh oui, c'est vrai. Maintenant ça marche! Très agréable ! Merci beaucoup !! Votre code est si utile!


Presque tout fonctionne maintenant. Cependant, je suis confronté à un problème: considérez cette chaîne, qui est tout à fait correcte: s = ' <,> ' . Si j'exécute repair_tags (s) , j'obtiens: <>, <> . Peut-être faudrait-il ajouter au code, que s'il y a un espace "" après ou avant un < ou un > , alors il ne devrait pas essayez de le "réparer" ... Que pensez-vous?


Eh bien, vous pouvez devenir arbitrairement complexe en fonction des règles que vous créez pour la saisie de texte. Le code que j'ai écrit suppose que les balises sont définies comme <\w+> uniquement, donc les espaces ne sont pas autorisés dans les balises HTML. En d'autres termes, les balises avec des attributs comme ne sont pas prises en charge par cette fonction de réparation. Cela peut être ajouté, mais vous vous dirigez ensuite sur une pente glissante avec le bas étant l'écriture d'une bibliothèque d'analyseur HTML entière, c'est donc une bonne idée d'énumérer tous les cas que vous souhaitez gérer à l'avance, sinon nous sommes en fluage / terrain du poteau de but mobile.


Oui, vous avez tout à fait raison. Le problème est que je découvre au fur et à mesure. Je transcris le texte et parfois, des choses inattendues comme celle-là se produisent. J'ai résolu le problème en ajoutant simplement et text [j]! = "" à l'instruction if j> = len (text) ou text [j]! = Right_bracket . Je sais, ce n'est vraiment pas beau d'être dans la fonction "fluage / déplacement du poteau de but", mais pour l'instant, je ne vois pas d'autre moyen. :)


Oui, je comprends le problème - en l'absence de la plupart des contraintes, j'en ai juste inventé pour les besoins de cette réponse, donc c'est principalement une preuve de concept pour aider à illustrer comment vous pourriez mettre en place une solution qui peut être développé au besoin.