Стріпніть HTML з рядків у Python


269
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>'надрукує "привіт" тощо. Як би ви зробили це?


16
Важливим питанням є поводження з HTML-сутностями (наприклад &amp;). Ви можете 1) видалити їх разом із тегами (часто небажаними та непотрібними, оскільки вони еквівалентні звичайному тексту), 2) залишити їх незмінними (підходяще рішення, якщо відрізаний текст повернеться назад у контекст HTML) або 3 ) розшифруйте їх до простого тексту (якщо зрізаний текст переходить у базу даних чи інший не HTML-контекст, або якщо ваш веб-фреймворк автоматично виконує HTML-текст для вас).
Søren Løvborg

2
для @ SørenLøvborg точка 2): stackoverflow.com/questions/753052/…
Роберт

2
Тут найвища відповідь, яка використовувалася проектом "Джанго" до березня 2014 року, виявилася небезпечною проти сценаріїв міжміських сайтів. Рекомендую використовувати Bleach.clean (), стриптиги Markupsafe або СТРЕНТ-теги Django.
rescdsk

Відповіді:


419

Я завжди використовував цю функцію для зчитування тегів 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()

3
Два роки + пізніше зіткнулися з тим же питанням, і це набагато більш елегантне рішення. Єдиною зміною, яку я вніс, було повернення self.fed як списку, а не приєднання до нього, щоб я міг переглядати вміст елемента.
направлення

47
Зауважте, що ця смужка HTML-сутностей (наприклад &amp;), а також тегів.
Søren Løvborg

30
@surya Я впевнений, що ти це бачив
tkone

8
Дякую за чудову відповідь. Одне, що слід зазначити тим, хто використовує новіші версії Python (3.2+), - це те, що вам потрібно буде викликати функцію батьківського класу __init__. Дивіться тут: stackoverflow.com/questions/11061058/… .
псевдорамбл

10
Щоб зберегти html-сутності (перетворені в unicode), я додав два рядки: parser = HTMLParser()і html = parser.unescape(html)на початок функції strip_tags.
James Doepp - pihentagyu

156

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

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

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

Якщо нетег <з'являється в html (наприклад, 2 < 3), він &...все одно повинен бути записаний як послідовність виходу, так що ^<може бути непотрібним.


10
Це майже точно так, як це робить Django's strip_tags .
Блу

10
Зауважте, що це залишає HTML-об'єкти (наприклад &amp;) незмінними у висновку.
Søren Løvborg

35
Ще можна обдурити цей метод чимось так: <script <script>> alert ("Привіт!") <</script> / script>

19
НЕ РОБИТИ ЦЕ ШЛЯХ! Як каже @Julio Garcia, це НЕ БЕЗПЕЧНО!
rescdsk

18
Люди, не плутайте зачистку HTML та санітарію HTML. Так, при зламаному або зловмисному введенні ця відповідь може дати вихід з HTML-тегами. Це все ще ідеально правильний підхід до зняття тегів HTML. Однак , видалення тегів HTML не є коректною заміною правильної дезінфекції HTML. Правило не є складним: кожного разу, коли ви вставляєте рядок із звичайним текстом у вихід HTML, вам слід завжди уникати HTML (використовуючи cgi.escape(s, True)), навіть якщо ви "знаєте", що він не містить HTML (наприклад, тому що ви зняли вміст HTML) . Однак це не те, про що запитували ОП.
Søren Løvborg

76

Ви можете використовувати 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)

Доцільно чітко вказати аналізатор , наприклад, як 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)

Джерело Regex: MarkupSafe . Їх версія також обробляє HTML-об'єкти, а ця швидка - ні.

Чому я не можу просто зняти теги і залишити їх?

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

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

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 та інші утиліти html з останньої версії хороші, але я вважаю їх менш зручними, ніж MarkupSafe. Вони досить автономні, ви можете скопіювати все, що вам потрібно, з цього файлу .

Якщо вам потрібно зняти майже всі теги, бібліотека Bleach - це добре. Ви можете дозволити йому виконувати правила типу "мої користувачі можуть робити курсив, але вони не можуть створювати рамки кадрів".

Зрозумійте властивості знімача тегів! Виконайте на ньому нечіткі тести! Ось код, який я використовував для дослідження для цієї відповіді.

ovish Примітка - саме питання стосується друку на консоль, але це найкращий результат Google для "python strip html from string", тому саме ця відповідь на 99% стосується Інтернету.


Мій приклад "альтернативний останній рядок" код не обробляє html-сутності - наскільки це погано?
rescdsk

Я розбираю лише невеликий фрагмент html без спеціальних тегів, і ваша коротка версія виконує цю роботу дуже добре. Дякую, що поділились!
tbolender

31

Мені знадобився спосіб викреслити теги та розшифрувати HTML-об’єкти до простого тексту. Наступне рішення засноване на відповіді Eloff (яку я не міг використати, оскільки він знімає сутності).

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, вам слід завжди уникати HTML (використовуючи cgi.escape(s, True)), навіть якщо ви "знаєте", що він не містить HTML (наприклад, тому що ви зняли вміст HTML) .

(Однак ОП запитав про друк результату на консолі, і в цьому випадку не потрібен 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 . Це безкоштовно!

Ласкаво просимо! :)


2
Цікаво, чому цю відповідь просто оскаржували. Це простий спосіб вирішити проблему, не маючи жодної губки. Просто чистий пітон, і він працює, як показано за посиланнями.
Медейрос

2
Напевно, люди віддають перевагу вусам, щоб забезпечити їм безпеку. Я перевірив ваш код і передав, і я завжди віддаю перевагу маленькому коду, який я розумію, ніж використання lib і припускаючи, що це нормально, поки не з’явиться помилка. Для мене це те, що я шукав і знову дякую. Щодо низових голосів, не забувайте про це. Люди повинні дбати про якість, а не про голоси. Останнім часом ТА стала місцем, де всі хочуть балів, а не знань.
Джиммі Кейн

2
Проблема з цим рішенням - обробка помилок. Наприклад, якщо ви подаєте <b class="o'>x</b>в якості функції вводу вихід x. Але насправді цей ввід недійсний. Я думаю, що тому люди віддають перевагу лайкам.
laltin

1
Він працює і з цим входом. Щойно перевірений. Просто зрозумійте, що всередині цих бібліотек ви знайдете подібний код. Це я не дуже пітонічний. Схоже на C або Java-код. Я думаю, що це ефективно та легко переноситься на іншу мову.
Медейрос

1
Простий, пітонічний і, здається, працює так само добре чи краще, ніж будь-який з інших обговорюваних методів. Цілком можливо, що це не спрацює з деяким неправильно сформованим HTML, але немає цього подолання.
денсон

16

Якщо вам потрібно зберегти сутність HTML (тобто &amp;), я додав у відповідь Eloff метод " handle_entityref" .

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", тоді як ви, мабуть, хочете, щоб це був "здрастуй світ". ' '.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тому, можливо, вам доведеться спочатку передати це рядок
Сузана,

1
@Suzana Добрий момент. Схоже, це робиться автоматичним каскадом strдля рядкових операцій, таких як +та індексація []. Так чи інакше, додали акторський склад.
Робін Дінзе

9

Рішення на базі lxml.html (lxml - це рідна бібліотека і тому набагато швидше, ніж будь-яке чисте рішення python).

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)

1
Я отримав AttributeError: 'HtmlElement' об’єкта не має атрибута 'strip'
виникає

7

Пакет Beautiful Soup робить це негайно для вас.

from bs4 import BeautifulSoup

soup = BeautifulSoup(html)
text = soup.get_text()
print(text)

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 зараз є хороші вказівки, ось інструкція з регексу
Джейсон Кун

5
В lxml:lxml.html.fromstring(s).text_content()
Bluu

1
Приклад Bluu з lxml розшифровує HTML-об'єкти (наприклад &amp;) до тексту.
Søren Løvborg

1

Я відповів Eloff успішно для 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.

Ще раз, дякую Томасу К за виправлення та оригінальний код Елоффа, наданий вище!


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
Чи додавання рядків створює нову копію рядка?
Джеремі L

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 іншого джерела.
kasperd

Ви маєте рацію у випадку відсутності втечі, але питання полягали у тому, щоб позбавити HTML із заданої рядки, щоб не уникнути заданого рядка. Якщо попередні відповіді створюють новий html зі своїми рішеннями в результаті видалення деяких html, то використання цих рішень небезпечно.
Falk Nisius

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

Адаптація пітона 3 відповіді søren-løvborg

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()

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()

1

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

def strip_html (текст):
    частини = []                                                                      
    парсер = HTMLParser ()                                                           
    parser.handle_data = parts.append                                               
    parser.feed (текст)                                                               
    повернути ''. приєднатися (частини)

0

Я аналізую ритми Github і виявляю, що наступне дійсно добре працює:

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 правильно.


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

Це добре працює для мене, але його можна, звичайно, покращити ...


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

Але це не дасть повного результату, якщо текст містить <> символи всередині нього.


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()

-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-об’єкти до простого тексту, але, очевидно, насправді не знімає жодних тегів, що було початковим питанням. (Крім того, другий блок спробу-окрім цього потрібно знеструмити, щоб код навіть зробив стільки ж).
Søren Løvborg
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.