Убрать HTML из строк в Python

271
from mechanize import Browser
br = Browser()
br.open('http://somewebpage')
html = br.response().readlines()
for line in html:
  print line

При печати строки в файле HTML я пытаюсь найти способ показать только содержимое каждого элемента HTML, а не само форматирование. Если он найдет '<a href="whatever.com">some text</a>', он напечатает только «некоторый текст», '<b>hello</b>'напечатает «привет» и т. Д. Как можно поступить так?

directedition
источник
16
Важным соображением является то, как обрабатывать HTML-объекты (например &amp;). Вы можете либо 1) удалить их вместе с тегами (часто нежелательно и ненужно, поскольку они эквивалентны простому тексту), 2) оставить их без изменений (подходящее решение, если выделенный текст возвращается обратно в контекст HTML), или 3 ) расшифруйте их в виде обычного текста (если выделенный текст попадает в базу данных или в какой-либо другой контекст, отличный от HTML, или если ваша веб-платформа автоматически выполняет HTML-экранирование текста для вас).
Сёрен Лёвборг
2
для @ SørenLøvborg, пункт 2): stackoverflow.com/questions/753052/…
Роберт
2
Верхний ответ здесь, который использовался проектом Django до марта 2014 года, оказался небезопасным против межсайтовых скриптов - посмотрите эту ссылку для примера, который делает это. Я рекомендую использовать Bleach.clean (), стрипты Markupsafe или последние Django strip_tags.
rescdsk

Ответы:

419

Я всегда использовал эту функцию для удаления тегов HTML, так как для этого требуется только stthon-библиотека Python:

Для Python 3:

from io import StringIO
from html.parser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        super().__init__()
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.text = StringIO()
    def handle_data(self, d):
        self.text.write(d)
    def get_data(self):
        return self.text.getvalue()

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

Для Python 2:

from HTMLParser import HTMLParser
from StringIO import StringIO

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.text = StringIO()
    def handle_data(self, d):
        self.text.write(d)
    def get_data(self):
        return self.text.getvalue()

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
Оливье Ле Флох
источник
3
Два года спустя столкнулись с той же проблемой, и это гораздо более элегантное решение. Единственным изменением, которое я сделал, было возвращение self.fed в виде списка, а не присоединение к нему, чтобы я мог просматривать содержимое элемента.
постановление
47
Обратите внимание, что это удаляет объекты HTML (например &amp;), а также теги.
Сёрен Лёвборг
30
@Surya Я уверен, что вы видели это
tkone
8
Спасибо за отличный ответ. Для тех из вас, кто использует более новые версии Python (3.2+), следует отметить, что вам необходимо вызвать __init__функцию родительского класса . Смотрите здесь: stackoverflow.com/questions/11061058/… .
псевдорабль
10
Чтобы сохранить html-сущности (преобразованные в юникод), я добавил две строки: parser = HTMLParser()и html = parser.unescape(html)в начало функции strip_tags.
Джеймс Доупп - pihentagyu
157

Я не особо задумывался о случаях, которые он пропустит, но вы можете сделать простое регулярное выражение:

re.sub('<[^<]+?>', '', text)

Для тех, кто не понимает регулярное выражение, он ищет строку <...>, где внутреннее содержимое состоит из одного или нескольких ( +) символов, которые не являются <. В ?означает , что он будет соответствовать наименьшую строку , она может найти. Например, учитывая <p>Hello</p>, он будет соответствовать <'p>и </p>отдельно с ?. Без этого он будет соответствовать всей строке <..Hello..>.

Если <в html появляется не тег (например 2 < 3), он должен быть записан как escape-последовательность, &...так что это ^<может быть ненужным.

mmmdreg
источник
10
Это почти точно, как это делает Django strip_tags .
Bluu
10
Обратите внимание, что это оставляет объекты HTML (например &amp;) неизменными в выводе.
Сёрен Левборг
36
Этот метод все еще можно обмануть с помощью чего-то вроде этого: <script <script >> alert ("Hi!") << / script> / script>
19
НЕ ДЕЛАЙТЕ ЭТОГО СПОСОБА! Как говорит @Julio Garcia, это НЕ БЕЗОПАСНО!
13
18
Люди, не путайте зачистку HTML и дезинфекцию HTML. Да, при неправильном или злонамеренном вводе этот ответ может привести к выводу с тегами HTML. Это все еще совершенно правильный подход для удаления тегов HTML. Однако удаление тегов HTML не является допустимой заменой для надлежащей очистки HTML. Правило несложное: всякий раз, когда вы вставляете текстовую строку в вывод HTML, вы всегда должны избегать ее с помощью HTML cgi.escape(s, True), даже если вы «знаете», что она не содержит HTML (например, потому что вы удалили содержимое HTML) , Однако это не то, о чем спрашивал ОП.
Сорен Левборг
77

Вы можете использовать get_text()функцию BeautifulSoup .

from bs4 import BeautifulSoup

html_str = '''
<td><a href="http://www.fakewebsite.com">Please can you strip me?</a>
<br/><a href="http://www.fakewebsite.com">I am waiting....</a>
</td>
'''
soup = BeautifulSoup(html_str)

print(soup.get_text()) 
#or via attribute of Soup Object: print(soup.text)

Желательно явно указать синтаксический анализатор , например as BeautifulSoup(html_str, features="html.parser"), чтобы вывод был воспроизводимым.

Амина Нураини
источник
32

Укороченная версия!

import re, cgi
tag_re = re.compile(r'(<!--.*?-->|<[^>]*>)')

# Remove well-formed tags, fixing mistakes by legitimate users
no_tags = tag_re.sub('', user_input)

# Clean up anything else by escaping
ready_for_web = cgi.escape(no_tags)

Источник регулярных выражений: MarkupSafe . Их версия также обрабатывает сущности HTML, а эта быстрая - нет.

Почему я не могу просто удалить метки и оставить это?

Одно дело удерживать людей от <i>italicizing</i>вещей, не оставляя iпарящих вокруг. Но это другой способ принять произвольный вклад и сделать его совершенно безвредным. Большинство методов на этой странице оставят такие вещи, как незакрытые комментарии ( <!--) и угловые скобки, которые не являются частью тегов (blah <<<><blah нетронутыми ). Версия HTMLParser может даже оставлять полные теги, если они находятся внутри закрытого комментария.

Что делать, если ваш шаблон {{ firstname }} {{ lastname }}? firstname = '<a'и lastname = 'href="http://evil.com/">'будет пропущен всеми стриптизерами тегов на этой странице (кроме @Medeiros!), потому что они сами по себе не являются полными тегами. Удалять обычные HTML-теги недостаточно.

Django strip_tags, улучшенная (см. Следующий заголовок) версия главного ответа на этот вопрос, выдает следующее предупреждение:

Абсолютно НЕТ гарантии, что полученная строка безопасна для HTML. Поэтому НИКОГДА не помечайте как безопасный результат strip_tagsвызова, не экранируя его первым, например, с помощью escape().

Следуй их советам!

Чтобы удалить теги с HTMLParser, вы должны запустить его несколько раз.

Легко обойти главный ответ на этот вопрос.

Посмотрите на эту строку ( источник и обсуждение ):

<img<!-- --> src=x onerror=alert(1);//><!-- -->

Когда HTMLParser видит его впервые, он не может сказать, что <img...>это тег. Он выглядит разбитым, поэтому HTMLParser не избавится от него. Это только вынимает <!-- comments -->, оставляя вас с

<img src=x onerror=alert(1);//>

Эта проблема была раскрыта проекту Django в марте 2014 года. Их старый strip_tagsбыл по сути тем же, что и главный ответ на этот вопрос. Их новая версия в основном запускает его в цикле, пока повторный запуск не изменит строку:

# _strip_once runs HTMLParser once, pulling out just the text of all the nodes.

def strip_tags(value):
    """Returns the given HTML with all tags stripped."""
    # Note: in typical case this loop executes _strip_once once. Loop condition
    # is redundant, but helps to reduce number of executions of _strip_once.
    while '<' in value and '>' in value:
        new_value = _strip_once(value)
        if len(new_value) >= len(value):
            # _strip_once was not able to detect more tags
            break
        value = new_value
    return value

Конечно, ничто из этого не является проблемой, если вы всегда избегаете результата strip_tags().

Обновление 19 марта 2015 : в версиях Django до 1.4.20, 1.6.11, 1.7.7 и 1.8c1 произошла ошибка. Эти версии могут ввести бесконечный цикл в функцию strip_tags (). Фиксированная версия воспроизводится выше. Подробнее здесь .

Хорошие вещи, чтобы скопировать или использовать

Мой пример кода не обрабатывает сущности HTML, как это делают упакованные версии Django и MarkupSafe.

Мой пример кода взят из превосходной библиотеки MarkupSafe для предотвращения межсайтовых скриптов. Это удобно и быстро (с ускорением C до его родной версии Python). Он включен в Google App Engine и используется Jinja2 (2.7 и выше) , Mako, Pylons и другими. Он легко работает с шаблонами Django из Django 1.7.

Django strip_tags и другие html-утилиты из последней версии хороши, но я считаю их менее удобными, чем MarkupSafe. Они довольно автономны, вы можете скопировать то, что вам нужно из этого файла .

Если вам нужно удалить практически все теги, библиотека Bleach подойдет. Вы можете заставить его применять такие правила, как «мои пользователи могут выделять курсивом, но не могут создавать фреймы».

Поймите свойства вашего стриптизерши! Запустите пушистые тесты! Вот код, который я использовал для исследования этого ответа.

зову примечание - сам вопрос касается печати на консоль, но это лучший результат Google для "python strip html from string", поэтому этот ответ на 99% относится к сети.

rescdsk
источник
Мой пример кода «альтернативная последняя строка» не обрабатывает html-сущности - насколько это плохо?
rescdsk
Я анализирую только небольшой фрагмент HTML без специальных тегов, и ваша короткая версия отлично справляется со своей задачей. Спасибо, что поделился!
tbolender
31

Мне нужен был способ вырезать теги и декодировать HTML-объекты в обычный текст. Следующее решение основано на ответе Элоффа (который я не смог использовать, потому что он удаляет сущности).

from HTMLParser import HTMLParser
import htmlentitydefs

class HTMLTextExtractor(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def handle_charref(self, number):
        codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
        self.result.append(unichr(codepoint))

    def handle_entityref(self, name):
        codepoint = htmlentitydefs.name2codepoint[name]
        self.result.append(unichr(codepoint))

    def get_text(self):
        return u''.join(self.result)

def html_to_text(html):
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

Быстрый тест:

html = u'<a href="#">Demo <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>'
print repr(html_to_text(html))

Результат:

u'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

Обработка ошибок:

  • Неверная структура HTML может вызвать HTMLParseError .
  • Недопустимые именованные сущности HTML (например &#apos;, допустимые в XML и XHTML, но не в обычном HTML) вызовут ValueErrorисключение.
  • Числовые сущности HTML, указывающие кодовые точки вне диапазона Unicode, приемлемого для Python (например, в некоторых системах символы вне базовой многоязычной плоскости ), вызовут ValueErrorисключение.

Примечание по безопасности: Не путайте разметку HTML (преобразование HTML в простой текст) с очисткой HTML (преобразование обычного текста в HTML). Этот ответ удалит HTML и расшифрует сущности в простой текст, что не делает результат безопасным для использования в контексте HTML.

Пример: &lt;script&gt;alert("Hello");&lt;/script&gt;будет преобразован в <script>alert("Hello");</script>, что является 100% правильным поведением, но явно недостаточно, если полученный простой текст вставляется как есть на HTML-страницу.

Правило несложное: каждый раз, когда вы вставляете текстовую строку в вывод HTML, вы всегда должны избегать его (используяcgi.escape(s, True) , даже если вы «знаете», что она не содержит HTML (например, потому что вы удалили содержимое HTML) ,

(Однако OP спросил о выводе результата на консоль, и в этом случае экранирование HTML не требуется.)

Версия Python 3.4+: (с doctest!)

import html.parser

class HTMLTextExtractor(html.parser.HTMLParser):
    def __init__(self):
        super(HTMLTextExtractor, self).__init__()
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def get_text(self):
        return ''.join(self.result)

def html_to_text(html):
    """Converts HTML to plain text (stripping tags and converting entities).
    >>> html_to_text('<a href="#">Demo<!--...--> <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>')
    'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

    "Plain text" doesn't mean result can safely be used as-is in HTML.
    >>> html_to_text('&lt;script&gt;alert("Hello");&lt;/script&gt;')
    '<script>alert("Hello");</script>'

    Always use html.escape to sanitize text before using in an HTML context!

    HTMLParser will do its best to make sense of invalid HTML.
    >>> html_to_text('x < y &lt z <!--b')
    'x < y < z '

    Unrecognized named entities are included as-is. '&apos;' is recognized,
    despite being XML only.
    >>> html_to_text('&nosuchentity; &apos; ')
    "&nosuchentity; ' "
    """
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

Обратите внимание, что HTMLParser улучшился в Python 3 (что означает меньше кода и лучшую обработку ошибок).

Сёрен Лёвборг
источник
18

Есть простой способ сделать это:

def remove_html_markup(s):
    tag = False
    quote = False
    out = ""

    for c in s:
            if c == '<' and not quote:
                tag = True
            elif c == '>' and not quote:
                tag = False
            elif (c == '"' or c == "'") and tag:
                quote = not quote
            elif not tag:
                out = out + c

    return out

Идея объясняется здесь: http://youtu.be/2tu9LTDujbw

Вы можете увидеть это работает здесь: http://youtu.be/HPkNPcYed9M?t=35s

PS - Если вы заинтересованы в классе (об умной отладке с python), я дам вам ссылку: http://www.udacity.com/overview/Course/cs259/CourseRev/1 . Это бесплатно!

Пожалуйста! :)

Medeiros
источник
2
Интересно, почему этот ответ только что был отвергнут? Это простой способ решить проблему без какой-либо библиотеки. Просто чистый питон и он работает как показано по ссылкам.
Медейрос
2
Вероятно, люди предпочитают библиотек, чтобы обеспечить им безопасность. Я проверил ваш код и прошел, и я всегда предпочитаю небольшой код, который я понимаю, чем использование lib и предполагаю, что все в порядке, пока не появится ошибка. Для меня это то, что я искал, и еще раз спасибо. Относительно отрицательных голосов, не входите в это мышление. Люди здесь должны заботиться о качестве, а не о голосах. В последнее время ТАК стало местом, где всем нужны очки, а не знания.
Джимми Кейн
2
Проблема с этим решением - обработка ошибок. Например, если вы даете в <b class="o'>x</b>качестве входных данных функции выхода x. Но на самом деле этот ввод недействителен. Я думаю, именно поэтому люди предпочитают библиотек.
Лалтин
1
Это работает с этим входом тоже. Только что протестировано. Просто поймите, что внутри этих библиотек вы найдете похожий код. Это не очень питонично, я знаю. Похоже на C или Java-код. Я думаю, что это эффективно и может быть легко перенесено на другой язык.
Медейрос,
1
Простой, Pythonic и, кажется, работает так же хорошо или лучше, чем любой из других обсуждаемых методов. Возможно, он не будет работать для некоторых плохо сформированных HTML, но это не преодолеть.
Denson
16

Если вам нужно сохранить сущности HTML (то есть &amp;), я добавил метод «handle_entityref» к ответу Eloff .

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def handle_entityref(self, name):
        self.fed.append('&%s;' % name)
    def get_data(self):
        return ''.join(self.fed)

def html_to_text(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
Роберт
источник
13

Если вы хотите удалить все теги HTML, я нашел самый простой способ - использовать BeautifulSoup:

from bs4 import BeautifulSoup  # Or from BeautifulSoup import BeautifulSoup

def stripHtmlTags(htmlTxt):
    if htmlTxt is None:
            return None
        else:
            return ''.join(BeautifulSoup(htmlTxt).findAll(text=True)) 

Я попробовал код принятого ответа, но получал «RuntimeError: превышена максимальная глубина рекурсии», чего не произошло с вышеуказанным блоком кода.

Василис
источник
1
Я только что попробовал ваш метод, потому что он кажется чище, он работал, ну вроде ... он не удалял теги ввода!
kustomrtr
Я считаю , что простое применение BeautifulSoup имеет проблемы с пробелами: ''.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True)). Здесь вывод "helloworld", в то время как вы, вероятно, хотите, чтобы это был "hello world". ' '.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))не помогает, поскольку это становится "он мир llo".
Финн Оруп Нильсен
@kustomrtr, извини, мое невежество, что я могу поставить в аргумент самообладания? NameError: имя 'self' не определено
Ian_De_Oliveira
@Ian_De_Oliveira Вы можете удалить его, я предполагал, что это внутри класса, но это не нужно. Я также отредактировал ответ, чтобы удалить его
Василис
@Ian_De_Oliveira Вы можете удалить его, я предполагал, что это внутри класса, но это не нужно. Я также отредактировал ответ, чтобы удалить его
Василис
10

Вот простое решение, которое удаляет HTML-теги и декодирует HTML-объекты на основе удивительно быстрой lxmlбиблиотеки:

from lxml import html

def strip_html(s):
    return str(html.fromstring(s).text_content())

strip_html('Ein <a href="">sch&ouml;ner</a> Text.')  # Output: Ein schöner Text.
Робин Динс
источник
3
По состоянию на 2020 год это был самый быстрый и лучший способ для удаления содержимого HTML. Плюс бонус обработки декодирования. Отлично подходит для определения языка!
dfabiano
text_content()возвращается, lxml.etree._ElementUnicodeResultтак что вам, возможно, придется сначала привести его к последовательности
Suzana
1
@ Сузана Хорошая мысль. Кажется, он автоматически приводится к strстроковым операциям, таким как +и индексация []. В любом случае добавлен актерский акт для хорошей меры.
Робин Динс
9

Lxml.html основанное решение (LXML является родной библиотеки и , следовательно , гораздо быстрее , чем любой чистый раствор питона).

from lxml import html
from lxml.html.clean import clean_html

tree = html.fromstring("""<span class="item-summary">
                            Detailed answers to any questions you might have
                        </span>""")

print(clean_html(tree).strip())

# >>> Detailed answers to any questions you might have

Также смотрите http://lxml.de/lxmlhtml.html#cleaning-up-html, что именно делает lxml.cleaner.

Если вам требуется больше контроля над тем, что именно очищается перед преобразованием в текст, тогда вы можете явно использовать lxml Cleaner , передавая нужные параметры в конструкторе, например:

cleaner = Cleaner(page_structure=True,
                  meta=True,
                  embedded=True,
                  links=True,
                  style=True,
                  processing_instructions=True,
                  inline_style=True,
                  scripts=True,
                  javascript=True,
                  comments=True,
                  frames=True,
                  forms=True,
                  annoying_tags=True,
                  remove_unknown_tags=True,
                  safe_attrs_only=True,
                  safe_attrs=frozenset(['src','color', 'href', 'title', 'class', 'name', 'id']),
                  remove_tags=('span', 'font', 'div')
                  )
sanitized_html = cleaner.clean_html(unsafe_html)
ccpizza
источник
1
Я получил AttributeError: у объекта 'HtmlElement' нет атрибута 'strip'
aris
7

Пакет Beautiful Soup сделает это немедленно для вас.

from bs4 import BeautifulSoup

soup = BeautifulSoup(html)
text = soup.get_text()
print(text)
runawaykid
источник
3
Из очереди отзывов: Могу ли я попросить вас добавить еще немного контекста вокруг вашего ответа. Ответы только на код трудно понять. Это поможет вам и будущим читателям, если вы сможете добавить больше информации в свой пост.
help-info.de
2

Вот мое решение для Python 3.

import html
import re

def html_to_txt(html_text):
    ## unescape html
    txt = html.unescape(html_text)
    tags = re.findall("<[^>]+>",txt)
    print("found tags: ")
    print(tags)
    for tag in tags:
        txt=txt.replace(tag,'')
    return txt

Не уверен, что это идеально, но решил мой вариант использования и кажется простым.

Джон Лоутценхизер
источник
2

Вы можете использовать другой HTML-парсер ( например, lxml или Beautiful Soup ) - тот, который предлагает функции для извлечения только текста. Или, вы можете запустить регулярное выражение в строке строки, которая удаляет теги. Смотрите Python документы для получения дополнительной информации.

Джейсон Кун
источник
1
Ссылка AMK мертва. Есть альтернатива?
2
На веб-сайте Python есть хорошие практические рекомендации, вот регулярные инструкции: docs.python.org/howto/regex
Джейсон Кун
5
В lxml:lxml.html.fromstring(s).text_content()
Bluu
1
Пример Bluu с lxml декодирует HTML-объекты (например &amp;) в текст.
Сёрен Лёвборг
1

Я успешно использовал ответ Элоффа для Python 3.1 [большое спасибо!].

Я обновился до Python 3.2.3 и столкнулся с ошибками.

Решение, предоставленное здесь благодаря респонденту Томасу К, заключается в том, чтобы вставить super().__init__()следующий код:

def __init__(self):
    self.reset()
    self.fed = []

... чтобы это выглядело так:

def __init__(self):
    super().__init__()
    self.reset()
    self.fed = []

... и это будет работать для Python 3.2.3.

Еще раз спасибо Thomas K за исправление и за оригинальный код Eloff, представленный выше!

MilesNielsen
источник
1

Вы можете написать свою собственную функцию:

def StripTags(text):
     finished = 0
     while not finished:
         finished = 1
         start = text.find("<")
         if start >= 0:
             stop = text[start:].find(">")
             if stop >= 0:
                 text = text[:start] + text[start+stop+1:]
                 finished = 0
     return text
Юда Правира
источник
1
Создает ли добавление к строкам новую копию строки?
Джереми Л
1
@Nerdling - Да, это так, что может привести к довольно впечатляющей неэффективности часто используемых функций (или, если уж на то пошло, нечасто используемых функций, которые воздействуют на большие фрагменты текста.) Подробнее см. На этой странице. : D
Джереми Санделл
Проверяет ли он строки в кавычках? Нет.
Джимми Кейн
1

Все решения с HTML-парсером можно взломать, если они запускаются только один раз:

html_to_text('<<b>script>alert("hacked")<</b>/script>

результаты в:

<script>alert("hacked")</script>

что вы намереваетесь предотвратить. если вы используете HTML-парсер, считайте теги до замены нуля:

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
        self.containstags = False

    def handle_starttag(self, tag, attrs):
       self.containstags = True

    def handle_data(self, d):
        self.fed.append(d)

    def has_tags(self):
        return self.containstags

    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    must_filtered = True
    while ( must_filtered ):
        s = MLStripper()
        s.feed(html)
        html = s.get_data()
        must_filtered = s.has_tags()
    return html
Фальк Нисиус
источник
1
Если вы вызываете вызываемую функцию html_to_textи встраиваете выводимый из этой функции текст в html без экранирования этого текста, то это отсутствие экранирования, которое является уязвимостью безопасности, а не html_to_textфункцией. html_to_textФункция никогда не обещал вам выход будет текст. А вставка текста в html без экранирования является потенциальной уязвимостью безопасности, независимо от того, получили ли вы текст из html_to_text какого-либо другого источника.
Касперд
Вы правы в том случае, если вам не удалось убежать, но вопросы заключались в том, чтобы убрать html из заданной строки, чтобы не избежать заданной строки. Если предыдущие ответы создают новый html со своими решениями в результате удаления некоторого html, то использование этих решений опасно.
Фальк Нисиус
1

Это быстрое решение, которое может быть еще более оптимизировано, но оно будет работать нормально. Этот код заменит все непустые теги на «» и удалит все HTML-теги из заданного входного текста. Вы можете запустить его, используя ./file.py входной вывод

    #!/usr/bin/python
import sys

def replace(strng,replaceText):
    rpl = 0
    while rpl > -1:
        rpl = strng.find(replaceText)
        if rpl != -1:
            strng = strng[0:rpl] + strng[rpl + len(replaceText):]
    return strng


lessThanPos = -1
count = 0
listOf = []

try:
    #write File
    writeto = open(sys.argv[2],'w')

    #read file and store it in list
    f = open(sys.argv[1],'r')
    for readLine in f.readlines():
        listOf.append(readLine)         
    f.close()

    #remove all tags  
    for line in listOf:
        count = 0;  
        lessThanPos = -1  
        lineTemp =  line

            for char in lineTemp:

            if char == "<":
                lessThanPos = count
            if char == ">":
                if lessThanPos > -1:
                    if line[lessThanPos:count + 1] != '<>':
                        lineTemp = replace(lineTemp,line[lessThanPos:count + 1])
                        lessThanPos = -1
            count = count + 1
        lineTemp = lineTemp.replace("&lt","<")
        lineTemp = lineTemp.replace("&gt",">")                  
        writeto.write(lineTemp)  
    writeto.close() 
    print "Write To --- >" , sys.argv[2]
except:
    print "Help: invalid arguments or exception"
    print "Usage : ",sys.argv[0]," inputfile outputfile"
Киран Мохан
источник
1

Адаптация ответа Сёрена-Левборга на Python 3

from html.parser import HTMLParser
from html.entities import html5

class HTMLTextExtractor(HTMLParser):
    """ Adaption of http://stackoverflow.com/a/7778368/196732 """
    def __init__(self):
        super().__init__()
        self.result = []

    def handle_data(self, d):
        self.result.append(d)

    def handle_charref(self, number):
        codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
        self.result.append(unichr(codepoint))

    def handle_entityref(self, name):
        if name in html5:
            self.result.append(unichr(html5[name]))

    def get_text(self):
        return u''.join(self.result)

def html_to_text(html):
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()
CpILL
источник
1

Для одного проекта мне нужно было раздеть HTML, а также CSS и JS Таким образом, я сделал вариант ответа Eloffs:

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.fed = []
        self.css = False
    def handle_starttag(self, tag, attrs):
        if tag == "style" or tag=="script":
            self.css = True
    def handle_endtag(self, tag):
        if tag=="style" or tag=="script":
            self.css=False
    def handle_data(self, d):
        if not self.css:
            self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
mousetail
источник
1

Вот решение, аналогичное принятому в настоящее время ответу ( https://stackoverflow.com/a/925630/95989 ), за исключением того, что оно использует внутренний HTMLParserкласс напрямую (т.е. не имеет подклассов), тем самым делая его значительно более кратким:

def strip_html (text):
    части = []                                                                      
    parser = HTMLParser ()                                                           
    parser.handle_data = parts.append                                               
    parser.feed (текст)                                                               
    return '' .join (части)
Ричард
источник
0

Я разбираю Github readmes и обнаружил, что следующее действительно хорошо работает:

import re
import lxml.html

def strip_markdown(x):
    links_sub = re.sub(r'\[(.+)\]\([^\)]+\)', r'\1', x)
    bold_sub = re.sub(r'\*\*([^*]+)\*\*', r'\1', links_sub)
    emph_sub = re.sub(r'\*([^*]+)\*', r'\1', bold_sub)
    return emph_sub

def strip_html(x):
    return lxml.html.fromstring(x).text_content() if x else ''

А потом

readme = """<img src="https://raw.githubusercontent.com/kootenpv/sky/master/resources/skylogo.png" />

            sky is a web scraping framework, implemented with the latest python versions in mind (3.4+). 
            It uses the asynchronous `asyncio` framework, as well as many popular modules 
            and extensions.

            Most importantly, it aims for **next generation** web crawling where machine intelligence 
            is used to speed up the development/maintainance/reliability of crawling.

            It mainly does this by considering the user to be interested in content 
            from *domains*, not just a collection of *single pages*
            ([templating approach](#templating-approach))."""

strip_markdown(strip_html(readme))

Удаляет все уценки и HTML правильно.

PascalVKooten
источник
0

Используя BeautifulSoup, html2text или код из @Eloff, в большинстве случаев остаются некоторые элементы html, код javascript ...

Таким образом, вы можете использовать комбинацию этих библиотек и удалить форматирование уценки (Python 3):

import re
import html2text
from bs4 import BeautifulSoup
def html2Text(html):
    def removeMarkdown(text):
        for current in ["^[ #*]{2,30}", "^[ ]{0,30}\d\\\.", "^[ ]{0,30}\d\."]:
            markdown = re.compile(current, flags=re.MULTILINE)
            text = markdown.sub(" ", text)
        return text
    def removeAngular(text):
        angular = re.compile("[{][|].{2,40}[|][}]|[{][*].{2,40}[*][}]|[{][{].{2,40}[}][}]|\[\[.{2,40}\]\]")
        text = angular.sub(" ", text)
        return text
    h = html2text.HTML2Text()
    h.images_to_alt = True
    h.ignore_links = True
    h.ignore_emphasis = False
    h.skip_internal_links = True
    text = h.handle(html)
    soup = BeautifulSoup(text, "html.parser")
    text = soup.text
    text = removeAngular(text)
    text = removeMarkdown(text)
    return text

Это хорошо работает для меня, но может быть улучшено, конечно ...

hayj
источник
0

Простой код! Это удалит все виды тегов и содержимого внутри него.

def rm(s):
    start=False
    end=False
    s=' '+s
    for i in range(len(s)-1):
        if i<len(s):
            if start!=False:
                if s[i]=='>':
                    end=i
                    s=s[:start]+s[end+1:]
                    start=end=False
            else:
                if s[i]=='<':
                    start=i
    if s.count('<')>0:
        self.rm(s)
    else:
        s=s.replace('&nbsp;', ' ')
        return s

Но он не даст полного результата, если текст содержит символы <> .

Vanjith
источник
0
# This is a regex solution.
import re
def removeHtml(html):
  if not html: return html
  # Remove comments first
  innerText = re.compile('<!--[\s\S]*?-->').sub('',html)
  while innerText.find('>')>=0: # Loop through nested Tags
    text = re.compile('<[^<>]+?>').sub('',innerText)
    if text == innerText:
      break
    innerText = text

  return innerText.strip()
dabingsou
источник
-2

Этот метод работает для меня безупречно и не требует дополнительных установок:

import re
import htmlentitydefs

def convertentity(m):
    if m.group(1)=='#':
        try:
            return unichr(int(m.group(2)))
        except ValueError:
            return '&#%s;' % m.group(2)
        try:
            return htmlentitydefs.entitydefs[m.group(2)]
        except KeyError:
            return '&%s;' % m.group(2)

def converthtml(s):
    return re.sub(r'&(#?)(.+?);',convertentity,s)

html =  converthtml(html)
html.replace("&nbsp;", " ") ## Get rid of the remnants of certain formatting(subscript,superscript,etc).
Джон
источник
3
Это декодирует сущности HTML в простой текст, но, очевидно, фактически не удаляет какие-либо теги, что было первоначальным вопросом. (Кроме того, второй блок try-кроме должен быть де-отступ для кода, чтобы сделать так же).
Сёрен Лёвборг