Усечение HTML в Python - PullRequest
       10

Усечение HTML в Python

6 голосов
/ 11 февраля 2011

Существует ли инструмент на чистом Python, который может взять HTML-код и обрезать его как можно ближе к заданной длине, но убедитесь, что полученный фрагмент правильно сформирован? Например, учитывая этот HTML:

<h1>This is a header</h1>
<p>This is a paragraph</p>

это не будет производить:

<h1>This is a hea

но:

<h1>This is a header</h1>

или хотя бы:

<h1>This is a hea</h1>

Я не могу найти тот, который работает, хотя я нашел тот, который опирается на pullparser, который устарел и мертв.

Ответы [ 8 ]

6 голосов
/ 07 марта 2011

Не думаю, что вам нужен полноценный парсер - вам нужно только разбить входную строку на одну из следующих строк:

  • текст
  • открытый тег
  • закрывающий тег
  • самозакрывающийся тег
  • символьная сущность

Если у вас есть подобный поток токенов, можно легко использовать стек для храненияотслеживать, какие теги нужно закрыть.Я на самом деле столкнулся с этой проблемой некоторое время назад и написал для этого небольшую библиотеку:

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

Она хорошо работает для меня и хорошо обрабатывает большинство угловых случаев, в том числе произвольновложенная разметка, считая символьные объекты как один символ, возвращая ошибку при искаженной разметке и т. д.

В вашем примере будет:

<h1>This is a hea</h1>

.Возможно, это можно изменить, но в общем случае это сложно - что, если вы пытаетесь усечь до 10 символов, но тег <h1> не закрыт для другого, скажем, 300 символов?

6 голосов
/ 13 февраля 2011

Если вы используете DJANGO lib, вы можете просто:

from django.utils import text, html

    class class_name():


        def trim_string(self, stringf, limit, offset = 0):
            return stringf[offset:limit]

        def trim_html_words(self, html, limit, offset = 0):
            return text.truncate_html_words(html, limit)


        def remove_html(self, htmls, tag, limit = 'all', offset = 0):
            return html.strip_tags(htmls)

В любом случае вот код из truncate_html_words из django:

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
3 голосов
/ 09 февраля 2012

Я нашел ответ от рабства очень полезным и одобрил бы его, если бы у меня была репутация, - однако было кое-что еще отметить.В моем окружении у меня был установлен html5lib, а также BeautifulSoup4.BeautifulSoup использовал синтаксический анализатор html5lib, в результате чего мой html-фрагмент был обернут в теги html и body, а это не то, что я хотел.

>>> truncate_html("<p>sdfsdaf</p>", 4)
u'<html><head></head><body><p>s</p></body></html>'

Для решения этих проблем я сказал BeautifulSoup использовать синтаксический анализатор python:

from bs4 import BeautifulSoup
def truncate_html(html, length): 
    return unicode(BeautifulSoup(html[:length], "html.parser"))

>>> truncate_html("<p>sdfsdaf</p>", 4)
u'<p>s</p>'
3 голосов
/ 08 декабря 2011

Вы можете сделать это в одной строке с BeautifulSoup (при условии, что вы хотите усечь определенное количество исходных символов, а не количество символов содержимого):

from BeautifulSoup import BeautifulSoup

def truncate_html(html, length): 
    return unicode(BeautifulSoup(html[:length]))
2 голосов
/ 13 февраля 2011

Это будет соответствовать вашим требованиям. Простой в использовании анализатор HTML и корректор неверной разметки

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

0 голосов
/ 11 февраля 2011

Посмотрите на HTML Tidy , чтобы очистить / переформатировать / переопределить HTML.

0 голосов
/ 11 февраля 2011

Я бы рекомендовал сначала полностью проанализировать HTML, а затем обрезать. Отличный парсер HTML для python - lxml . После анализа и усечения вы можете распечатать его обратно в формате HTML.

0 голосов
/ 11 февраля 2011

Вначале я хотел использовать синтаксический анализатор XML (возможно, синтаксический анализатор python ), а затем, вероятно, посчитать текстовые символы в каждом элементе xml.Я бы проигнорировал количество символов тегов, чтобы сделать его более последовательным, а также более простым, но любой из них должен быть возможен.

...