7
votes

HTML tronquant en python

Y a-t-il un outil python python pour prendre du code HTML et le tronquer aussi près que possible d'une longueur donnée, mais assurez-vous que l'extrait résultant est bien formé? Par exemple, étant donné à ce code HTML: xxx

Il ne produirait pas: xxx

mais: xxx

ou au moins: xxx

Je ne trouve pas un qui fonctionne, bien que j'ai trouvé un qui s'appuie sur pullparser , lequel est à la fois obsolète et mort.


2 commentaires

"Cela produirait:" .. donné quels paramètres? Nombre de caractères dans une rangée? Nombre de dom-éléments, hiérarchie?


Probablement soit un certain nombre de caractères de contenu, soit un certain nombre de caractères HTML. Je ne suis pas pointilleux.


8 Réponses :


0
votes

Ma première pensée serait utilisée d'un analyseur XML (peut-être Patier SAX de Python ), puis compter probablement les caractères de texte dans chaque élément XML. J'ignorerais le nombre de tags les caractères pour le rendre plus cohérent, mais plus simple, mais soit possible.


2 commentaires

Alors que j'ai commenté la réponse de Funktku, n'a pas Quelqu'un déjà fait cela?


@Jasonfruith oh je vois ce que tu veux dire maintenant - je ne sais pas si c'est vraiment ce commun franchement et c'est assez simple à faire.



0
votes

Je recommanderais d'abord complètement l'analyse du HTML puis tronquer. Un excellent analyseur HTML pour Python est lxml . Après analyse et tronquage, vous pouvez l'imprimer au format HTML.


1 commentaires

Mais n'a pas quelqu'un déjà fait cela? Je comprends le problème, mais cela semble être tel que quelqu'un doit avoir une solution.



0
votes

Regardez HTML Tidy à Nettoyage / Reformate / Reindent HTML.


3 commentaires

Pas la meilleure option, et pas vraiment une chose python.


Il y a quelques bibliothèques Python qui se lient à rangée, vérifiez-la. Je l'utilise pour nettoyer MS-Word HTML Certains utilisateurs collectent en CMS.


Je n'ai également pas précisé que j'utilise Google App Moteur, où je ne peux que introduire des bibliothèques péricit-python.



7
votes

Si vous utilisez Django Lib, vous pouvez simplement:

import re

def truncate_html_words(s, num):
    """
    Truncates html to a certain number of words (not counting tags and comments).
    Closes opened tags if they were correctly closed in the given html.
    """
    length = int(num)
    if length <= 0:
        return ''
    html4_singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area', 'hr', 'input')
    # Set up regular expressions
    re_words = re.compile(r'&.*?;|<.*?>|([A-Za-z0-9][\w-]*)')
    re_tag = re.compile(r'<(/)?([^ ]+?)(?: (/)| .*?)?>')
    # Count non-HTML words and keep note of open tags
    pos = 0
    ellipsis_pos = 0
    words = 0
    open_tags = []
    while words <= length:
        m = re_words.search(s, pos)
        if not m:
            # Checked through whole string
            break
        pos = m.end(0)
        if m.group(1):
            # It's an actual non-HTML word
            words += 1
            if words == length:
                ellipsis_pos = pos
            continue
        # Check for tag
        tag = re_tag.match(m.group(0))
        if not tag or ellipsis_pos:
            # Don't worry about non tags or tags after our truncate point
            continue
        closing_tag, tagname, self_closing = tag.groups()
        tagname = tagname.lower()  # Element names are always case-insensitive
        if self_closing or tagname in html4_singlets:
            pass
        elif closing_tag:
            # Check for match in open tags list
            try:
                i = open_tags.index(tagname)
            except ValueError:
                pass
            else:
                # SGML: An end tag closes, back to the matching start tag, all unclosed intervening start tags with omitted end tags
                open_tags = open_tags[i+1:]
        else:
            # Add it to the start of the open tags list
            open_tags.insert(0, tagname)
    if words <= length:
        # Don't try to close tags if we don't need to truncate
        return s
    out = s[:ellipsis_pos] + ' ...'
    # Close any tags still open
    for tag in open_tags:
        out += '</%s>' % tag
    # Return string
    return out


4 commentaires

J'utilise Cherrypy, mais cela pourrait valoir la peine d'importer django.utils.text si ce n'est pas un coût de démarrage trop ajouté. Je vais l'essayer.


La fonction troncate_html_words est dans . djangoproject.com/browser/django/trunk/django/utils/... .


Analyse HTML en utilisant des expressions régulières (comme Django en avant) est une idée vraiment, vraiment mauvaise idée.


@slacy: Ce n'est pas analyser HTML, il comptait le nombre de balises ouvertes. C'est un problème beaucoup plus simple.



2
votes

Ceci servira votre exigence.Un facile à utiliser HTML Parser et Bad Markup Corrector

http://www.crummy.com/software/Beautifulsoup/


1 commentaires

J'ai d'abord regardé ici avant de poser la question. Ce n'est pas mauvais, mais c'est à moi de compter des personnages de contenu et de tronquer au bon point, même si cela fait un bon travail de fixation du balisage une fois que cela est fait.



7
votes

Je ne pense pas que vous ayez besoin d'un analyseur à part entière - il vous suffit de goûter la chaîne d'entrée dans l'une des suivantes:

  • Texte
  • Tag Ouvrir
  • Fermer Tag
  • Étiquette auto-fermeture
  • entité de caractère

    Une fois que vous avez un flux de jetons comme celui-ci, il est facile d'utiliser une pile pour garder une trace de ce que les étiquettes ont besoin de fermeture. En fait, je rencontrais ce problème il y a un moment et j'ai écrit une petite bibliothèque pour le faire:

    https://github.com/eentzel/htmltrunatate.py

    C'est bien fonctionné pour moi et gère la plupart des caisses de coin, y compris une balise pénétrée arbitrairement imbriquée, de compter des entités de caractère comme un seul Caractère, retourner une erreur sur le balisage mal formé, etc.

    Il produira: xxx

    sur votre exemple. Cela pourrait peut-être être changé, mais c'est difficile dans le cas général - et si vous essayez de tronquer 10 caractères, mais la balise

    n'est pas fermée pour une autre, par exemple, 300 caractères. ?


2 commentaires

C'est exactement ce que j'ai travaillé et m'a écrit. La seule différence pratique entre le vôtre et la mienne était que j'ai permis de ne pas tronquer que sur les emplacements inter-mots.


J'avais besoin de cela et j'ai mis en œuvre des pauses inter-mots mis en œuvre. C'est très simple, le diff vers l'original est comme 5 lignes - Github.com /enkore/typeflow/blob/master/htmltruncate.py autour de la ligne cinquante



3
votes

Vous pouvez le faire en une seule ligne avec BeauXoup (en supposant que vous souhaitez tronquer à un certain nombre de caractères source, pas à un certain nombre de caractères de contenu):

from BeautifulSoup import BeautifulSoup

def truncate_html(html, length): 
    return unicode(BeautifulSoup(html[:length]))


0 commentaires

4
votes

J'ai trouvé la réponse par SLAY très utile et je l'avoue si j'avais la réputation, mais il y avait une chose supplémentaire à noter. Dans mon environnement, j'avais installé HTML5Lib ainsi que BeautifulSoup4. Beautifulsoup a utilisé l'analyseur HTML5LIB et cela a permis d'envelopper mon extrait HTML dans des balises HTML et de corps qui n'est pas ce que je voulais. XXX

Pour résoudre ces problèmes, j'ai dit à Beauchsoup d'utiliser l'analyseur Python: xxx


1 commentaires

Je veux juste laisser les lecteurs savoir: une réponse géniale et très robuste. Fonctionne pour `truncate_html ("

123456789

12