Вопрос-Ответ

Strip HTML from strings in Python

Удаление HTML из строк в Python
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.example">some text</a>', он напечатает только "некоторый текст", '<b>hello</b>' печатает "привет" и т.д. Как бы это сделать?

Переведено автоматически
Ответ 1

Я всегда использовал эту функцию для удаления HTML-тегов, поскольку для этого требуется только стандартный файл 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()
Ответ 2

Если вам нужно удалить HTML-теги для обработки текста, подойдет простое регулярное выражение. Не используйте это, если вы хотите очистить созданный пользователем HTML для предотвращения XSS-атак. Это небезопасный способ удалить все <script> теги или трекинги<img>. Следующее регулярное выражение довольно надежно удалит большинство тегов HTML:

import re

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

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

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

Ответ 3

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

from bs4 import BeautifulSoup

html_str = '''
<td><a href="http://www.fakewebsite.example">Please can you strip me?</a>
<br/><a href="http://www.fakewebsite.example">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"), чтобы вывод был воспроизводимым.

Ответ 4

Короткая версия!

import re, html
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 = html.escape(no_tags)

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

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

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

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

Django's 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.

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

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

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

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

python