Tira HTML das strings em Python

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

Ao imprimir uma linha em um arquivo HTML, estou tentando encontrar uma maneira de mostrar apenas o conteúdo de cada elemento HTML e não a formatação em si. Se encontrar '<a href="whatever.com">some text</a>', ele imprimirá apenas 'algum texto', '<b>hello</b>'imprimirá 'olá', etc. Como alguém faria isso?

direção
fonte
16
Uma consideração importante é como lidar com entidades HTML (por exemplo &amp;). Você pode 1) removê-los junto com as tags (geralmente indesejáveis ​​e desnecessárias, pois são equivalentes ao texto sem formatação), 2) deixá-los inalterados (uma solução adequada se o texto retirado estiver voltando ao contexto HTML) ou 3 ) decodifique-os para texto sem formatação (se o texto removido estiver entrando em um banco de dados ou em outro contexto não HTML, ou se sua estrutura da Web executar automaticamente a saída de texto HTML para você).
Søren Løvborg 15/10
2
para @ SørenLøvborg point 2): stackoverflow.com/questions/753052/…
Robert
2
A resposta principal aqui, que foi usada pelo projeto Django até março de 2014, foi considerada insegura contra scripts entre sites - veja esse link como exemplo. Eu recomendo usar Bleach.clean (), striptags do Markupsafe ou strip_tags do Djent RECENT.
#Re

Respostas:

419

Eu sempre usei essa função para remover tags HTML, pois requer apenas o stdlib do Python:

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

Para 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()
Olivier Le Floch
fonte
3
Mais de dois anos depois, enfrentando o mesmo problema, e essa é uma solução muito mais elegante. A única alteração que fiz foi retornar self.fed como uma lista, em vez de ingressar nela, para que eu pudesse percorrer o conteúdo do elemento.
directedition
47
Observe que isso retira entidades HTML (por exemplo &amp;), além de tags.
Søren Løvborg
30
@surya Eu estou certo que você viu esse
tkone
8
Obrigado pela ótima resposta. Uma coisa a ser observada para quem usa versões mais recentes do Python (3.2+) é que precisará chamar a __init__função da classe pai . Veja aqui: stackoverflow.com/questions/11061058/… .
Pseudoramble
10
Para manter as entidades html (convertidas em unicode), adicionei duas linhas: parser = HTMLParser()e html = parser.unescape(html)no início da função strip_tags.
James Doepp - pihentagyu
157

Não pensei muito nos casos que ele perderá, mas você pode fazer uma simples expressão regular:

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

Para aqueles que não entendem regex, isso procura por uma string <...>, onde o conteúdo interno é composto de um ou mais +caracteres ( ) que não são a <. O ?meio que corresponderá à menor string que encontrar. Por exemplo <p>Hello</p>, ele corresponderá <'p>e </p>separadamente com o ?. Sem ele, ele corresponderá a toda a cadeia <..Hello..>.

Se não-tag <aparecer em html (por exemplo 2 < 3), deve ser escrito como uma sequência de escape de &...qualquer maneira, para que ^<possa ser desnecessário.

mmmdreg
fonte
10
Isto é quase exatamente como strip_tags do Django faz.
Bluu 30/03
10
Observe que isso deixa as entidades HTML (por exemplo &amp;) inalteradas na saída.
Søren Løvborg 15/10/11
36
Ainda é possível enganar esse método com algo como isto: <script <script>> alert ("Oi!") <</script> / script>
19
NÃO O FAZ ASSIM! Como @Julio Garcia diz, NÃO É SEGURO!
rescdsk
18
Pessoas, não confunda a remoção de HTML e a limpeza de HTML. Sim, para entradas quebradas ou mal-intencionadas, esta resposta pode produzir uma saída com tags HTML. Ainda é uma abordagem perfeitamente válida para remover as tags HTML. No entanto , remover tags HTML não é uma substituição válida para higienização HTML adequada. A regra não é difícil: sempre que você inserir uma sequência de texto sem formatação na saída HTML, você deve sempre escapá-la (usando cgi.escape(s, True)), mesmo que "saiba" que ela não contém HTML (por exemplo, porque você retirou o conteúdo HTML) . No entanto, não foi sobre isso que o OP perguntou.
Søren Løvborg 9/11
76

Você pode usar o get_text()recurso 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)

É aconselhável especificar explicitamente o analisador , por exemplo BeautifulSoup(html_str, features="html.parser"), para que a saída seja reproduzível.

Aminah Nuraini
fonte
32

Versão curta!

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)

Fonte Regex: MarkupSafe . A versão deles também lida com entidades HTML, enquanto essa rápida não.

Por que não posso simplesmente retirar as tags e deixá-las?

Uma coisa é manter as pessoas longe das <i>italicizing</i>coisas, sem deixar is flutuando. Mas é outra forma de receber informações arbitrárias e torná-las completamente inofensivas. A maioria das técnicas desta página deixará intactos comentários não fechados ( <!--) e colchetes angulares que não fazem parte das tags ( blah <<<><blah). A versão HTMLParser pode até deixar tags completas, se elas estiverem em um comentário não fechado.

E se o seu modelo for {{ firstname }} {{ lastname }}? firstname = '<a'e lastname = 'href="http://evil.com/">'será liberado por todos os removedores de tags desta página (exceto @ Medeiros!), porque eles não são tags completos por si próprios. Retirar tags HTML normais não é suficiente.

O Django strip_tags, uma versão melhorada (veja o próximo cabeçalho) da resposta principal a esta pergunta, dá o seguinte aviso:

Absolutamente nenhuma garantia é fornecida sobre a sequência resultante ser HTML seguro. Portanto, NUNCA marque com segurança o resultado de uma strip_tagschamada sem escapar primeiro, por exemplo, com escape().

Siga os conselhos deles!

Para remover tags com o HTMLParser, você deve executá-lo várias vezes.

É fácil contornar a resposta principal para esta pergunta.

Veja esta string ( fonte e discussão ):

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

A primeira vez que o HTMLParser o vê, não é possível dizer que <img...>é uma tag. Parece quebrado, então o HTMLParser não se livra dele. Só tira o <!-- comments -->, deixando você com

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

Esse problema foi divulgado ao projeto Django em março de 2014. O antigo strip_tagsera basicamente o mesmo que a principal resposta a essa pergunta. A nova versão deles basicamente roda em um loop até que a execução novamente não mude a string:

# _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

Obviamente, nada disso é um problema se você sempre escapar do resultado strip_tags().

Atualização 19 de março de 2015 : Houve um bug nas versões do Django anteriores à 1.4.20, 1.6.11, 1.7.7 e 1.8c1. Essas versões podem inserir um loop infinito na função strip_tags (). A versão fixa é reproduzida acima. Mais detalhes aqui .

Coisas boas para copiar ou usar

Meu código de exemplo não lida com entidades HTML - as versões empacotadas do Django e MarkupSafe.

Meu código de exemplo é extraído da excelente biblioteca MarkupSafe para prevenção de scripts entre sites. É conveniente e rápido (com acelerações em C para sua versão nativa do Python). Ele está incluído no Google App Engine e é usado por Jinja2 (2.7 e superior) , Mako, Pylons e muito mais. Ele funciona facilmente com modelos do Django do Django 1.7.

O strip_tags do Django e outros utilitários html de uma versão recente são bons, mas eu os acho menos convenientes que o MarkupSafe. Eles são bem independentes, você pode copiar o que precisa desse arquivo .

Se você precisar remover quase todas as tags, a biblioteca Bleach é boa. Você pode fazer cumprir regras como "meus usuários podem colocar itálico nas coisas, mas eles não podem criar iframes".

Entenda as propriedades do seu removedor de etiquetas! Execute testes de fuzz nele! Aqui está o código que eu usei para fazer a pesquisa para esta resposta.

observação tímida - A pergunta em si é sobre a impressão no console, mas este é o principal resultado do Google para "python strip html from string"; é por isso que essa resposta é 99% sobre a web.

rescdsk
fonte
Meu código de exemplo "última linha alternativa" não lida com entidades html - quão ruim é isso?
rescdsk
Estou analisando apenas um pequeno pedaço de html sem tags especiais, e sua versão curta faz o trabalho muito bem. Obrigado por compartilhar!
tbolender
31

Eu precisava de uma maneira de remover tags e decodificar entidades HTML em texto sem formatação. A solução a seguir é baseada na resposta de Eloff (que eu não poderia usar porque retira entidades).

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

Um teste rápido:

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

Resultado:

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

Manipulação de erros:

  • Estrutura HTML inválida pode causar um HTMLParseError .
  • Entidades HTML nomeadas inválidas (como &#apos;válidas em XML e XHTML, mas não em HTML simples) causarão uma ValueErrorexceção.
  • Entidades HTML numéricas que especificam pontos de código fora do intervalo Unicode aceitável pelo Python (como, em alguns sistemas, caracteres fora do Plano Multilíngue Básico ) causarão uma ValueErrorexceção.

Nota de segurança: Não confunda a remoção de HTML (convertendo HTML em texto sem formatação) com higienização HTML (convertendo texto sem formatação em HTML). Esta resposta removerá o HTML e decodificará as entidades em texto sem formatação - o que não torna o resultado seguro para uso em um contexto HTML.

Exemplo: &lt;script&gt;alert("Hello");&lt;/script&gt;será convertido para <script>alert("Hello");</script>, que é um comportamento 100% correto, mas obviamente não será suficiente se o texto simples resultante for inserido como está em uma página HTML.

A regra não é difícil: sempre que você inserir uma sequência de texto sem formatação na saída HTML, você deve sempre escapá-la (usando cgi.escape(s, True)), mesmo que "saiba" que ela não contém HTML (por exemplo, porque você retirou o conteúdo HTML) .

(No entanto, o OP perguntou sobre a impressão do resultado no console, caso em que nenhum escape de HTML é necessário.)

Versão do Python 3.4+: (com 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()

Observe que o HTMLParser foi aprimorado no Python 3 (o que significa menos código e melhor tratamento de erros).

Søren Løvborg
fonte
18

Existe uma maneira simples de fazer isso:

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

A ideia é explicada aqui: http://youtu.be/2tu9LTDujbw

Você pode vê-lo funcionando aqui: http://youtu.be/HPkNPcYed9M?t=35s

PS - Se você estiver interessado na classe (sobre depuração inteligente com python), eu lhe dou um link: http://www.udacity.com/overview/Course/cs259/CourseRev/1 . É grátis!

De nada! :)

Medeiros
fonte
2
Eu me pergunto por que essa resposta acabou de ser votada. É uma maneira simples de resolver o problema sem nenhuma lib. Apenas python puro e funciona como mostrado pelos links.
Medeiros
2
Provavelmente, as pessoas preferem libs para lhes dar segurança. Eu testei seu código e passei, e eu sempre prefiro um código pequeno que eu entenda do que usar uma lib e supondo que está tudo bem até que um bug apareça. Para mim, era isso que eu estava procurando e, novamente, obrigado. Em relação aos votos negativos, não entre nessa mentalidade. As pessoas aqui devem se preocupar com a qualidade e não com os votos. Ultimamente, o SO se tornou um lugar onde todos querem os pontos e não o conhecimento.
Jimmy Kane
2
O problema com esta solução é o tratamento de erros. Por exemplo, se você der <b class="o'>x</b>como saída a função de entrada x. Mas, na verdade, essa entrada é inválida. Eu acho que é por isso que as pessoas preferem libs.
Laltin 13/09/2013
1
Também funciona com essa entrada. Apenas testado. Apenas perceba que dentro dessas bibliotecas você encontrará código semelhante. Não é muito pitonico, eu sei. Parece código C ou Java. Eu acho que é eficiente e pode ser portado facilmente para outro idioma.
Medeiros
1
Simples, pitonico e parece funcionar tão bem ou melhor que qualquer um dos outros métodos discutidos. É possível que não funcione para HTML mal formado, mas não há como superá-lo.
denson
16

Se você precisar preservar entidades HTML (ou seja &amp;), adicionei o método "handle_entityref" à resposta de 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()
Robert
fonte
13

Se você deseja remover todas as tags HTML, a maneira mais fácil que encontrei é usar o 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)) 

Tentei o código da resposta aceita, mas estava recebendo "RuntimeError: profundidade máxima de recursão excedida", o que não aconteceu com o bloco de código acima.

Vasilis
fonte
1
Eu apenas tentei o seu método porque parece mais limpo, funcionou, meio que ... não tira as tags de entrada!
kustomrtr
Acho que uma simples aplicação de BeautifulSoup tem um problema com espaços em branco: ''.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True)). Aqui a saída é "helloworld", enquanto você provavelmente deseja que seja "hello world". ' '.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))não ajuda, pois se torna "ele mundo".
Finn Årup Nielsen
@kustomrtr, desculpe minha ignorância, o que eu coloco no argumento pessoal? NameError: o nome 'self' não está definido
Ian_De_Oliveira
@Ian_De_Oliveira Você pode removê-lo, presumi que esteja dentro de uma classe, mas não é necessário. Eu também editou a resposta para removê-lo
Vasilis
@Ian_De_Oliveira Você pode removê-lo, presumi que esteja dentro de uma classe, mas não é necessário. Eu também editou a resposta para removê-lo
Vasilis
10

Aqui está uma solução simples que retira tags HTML e decodifica entidades HTML com base na lxmlbiblioteca incrivelmente rápida :

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.
Robin Dinse
fonte
3
A partir de 2020, essa era a maneira mais rápida e melhor de remover o conteúdo do HTML. Mais o bônus de lidar com a decodificação. Ótimo para detecção de idioma!
dfabiano 23/01
text_content()retorna lxml.etree._ElementUnicodeResultpara que você precise convertê-lo em string primeiro
Suzana
1
@Suzana Bom ponto. Parece ser convertido automaticamente strpara operações como string +e indexação []. De qualquer forma, foi adicionado um elenco para uma boa medida.
Robin Dinse em
9

Uma solução baseada em lxml.html (lxml é uma biblioteca nativa e, portanto, muito mais rápida que qualquer solução python pura).

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

Consulte também http://lxml.de/lxmlhtml.html#cleaning-up-html para saber exatamente o que o lxml.cleaner faz.

Se você precisar de mais controle sobre o que exatamente é higienizado antes de converter em texto, convém usar o Cleaner lxml explicitamente passando as opções desejadas no construtor, por exemplo:

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
fonte
1
Eu tenho AttributeError: o objeto 'HtmlElement' não tem atributo 'strip'
aris
7

O pacote Beautiful Soup faz isso imediatamente para você.

from bs4 import BeautifulSoup

soup = BeautifulSoup(html)
text = soup.get_text()
print(text)
runawaykid
fonte
3
Da fila de revisão: posso solicitar que você adicione um pouco mais de contexto à sua resposta. As respostas somente de código são difíceis de entender. Isso ajudará os solicitantes e futuros leitores, se você puder adicionar mais informações em sua postagem.
help-info.de
2

Aqui está a minha solução para 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

Não tenho certeza se é perfeito, mas resolvi meu caso de uso e parece simples.

John Loutzenhiser
fonte
2

Você pode usar um analisador HTML diferente ( como lxml ou Beautiful Soup ) - que oferece funções para extrair apenas texto. Ou, você pode executar uma regex na sua string de linha que retira as tags. Veja a documentação do Python para mais.

Jason Coon
fonte
1
o link amk está morto. Tem uma alternativa?
2
O website Python tem boas how-tos agora, aqui é o regex how-to: docs.python.org/howto/regex
Jason Coon
5
Em lxml:lxml.html.fromstring(s).text_content()
Bluu
1
O exemplo de Bluu com lxml decodifica entidades HTML (por exemplo &amp;) em texto.
Søren Løvborg 15/10
1

Eu usei a resposta de Eloff com sucesso para o Python 3.1 [muito obrigado!].

Atualizei para o Python 3.2.3 e encontrei erros.

A solução, fornecida aqui graças ao respondedor Thomas K, é inserir super().__init__()o seguinte código:

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

... para que fique assim:

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

... e funcionará para o Python 3.2.3.

Mais uma vez, obrigado a Thomas K pela correção e pelo código original de Eloff fornecido acima!

MilesNielsen
fonte
1

Você pode escrever sua própria função:

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
Yuda Prawira
fonte
1
O acréscimo às strings cria uma nova cópia da string?
Jeremy L
1
@ Nerdling - Sim, o que pode levar a algumas ineficiências impressionantes nas funções usadas com freqüência (ou, nesse caso, nas funções usadas com pouca frequência que atuam em grandes gotas de texto.) Consulte esta página para obter detalhes. : D
Jeremy Sandell
Ele testa contra seqüências de caracteres citadas? Não.
Jimmy Kane
1

As soluções com o analisador de HTML são todas quebráveis, se executadas apenas uma vez:

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

resulta em:

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

o que você pretende impedir. se você usar um analisador de HTML, conte as tags até que zero seja substituído:

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
Falk Nisius
fonte
1
Se você chamar uma função chamada html_to_texte incorporar o texto que está sendo produzido nessa função dentro de html sem escapar desse texto, é a falta de escape, que é uma vulnerabilidade de segurança, não a html_to_textfunção. A html_to_textfunção nunca prometeu que a saída seria texto. E inserir texto no html sem escapar é uma vulnerabilidade de segurança em potencial, independentemente de você ter obtido o texto html_to_text ou alguma outra fonte.
kasperd
Você está certo, no caso, de uma falta de escape, mas a questão era retirar o html de uma determinada string para não escapar de uma determinada string. Se as respostas anteriores criarem um novo html com suas soluções como resultado da remoção de alguns html, o uso dessas soluções será perigoso.
Falk Nisius
1

Essa é uma solução rápida e pode ser ainda mais otimizada, mas funcionará bem. Esse código substituirá todas as tags não vazias por "" e remove todas as tags html de um determinado texto de entrada. Você pode executá-lo usando a saída de entrada ./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"
Kiran Mohan
fonte
1

Uma adaptação python 3 da resposta de 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()
CpILL
fonte
1

Para um projeto, eu precisava remover o HTML, mas também css e js. Assim, fiz uma variação da resposta de 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()
ratoeira
fonte
1

Aqui está uma solução semelhante à resposta atualmente aceita ( https://stackoverflow.com/a/925630/95989 ), exceto que ela usa a HTMLParserclasse interna diretamente (ou seja, sem subclassificação), tornando-a significativamente mais concisa:

def strip_html (texto):
    partes = []                                                                      
    parser = HTMLParser ()                                                           
    parser.handle_data = parts.append                                               
    parser.feed (texto)                                                               
    return '' .join (peças)
Richard
fonte
0

Estou analisando os readmes do Github e acho que o seguinte realmente funciona bem:

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 ''

E depois

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

Remove todas as remarcações e html corretamente.

PascalVKooten
fonte
0

Usando BeautifulSoup, html2text ou o código de @Eloff, na maioria das vezes, permanecem alguns elementos html, código javascript ...

Portanto, você pode usar uma combinação dessas bibliotecas e excluir a formatação de markdown (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

Funciona bem para mim, mas pode ser aprimorado, é claro ...

hayj
fonte
0

Código simples! Isso removerá todos os tipos de tags e conteúdo dentro dele.

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

Mas não dará o resultado completo se o texto contiver símbolos <> .

Vanjith
fonte
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
fonte
-2

Este método funciona perfeitamente para mim e não requer instalações adicionais:

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).
John
fonte
3
Isso decodifica as entidades HTML em texto sem formatação, mas obviamente não tira as tags, o que era a pergunta original. (Além disso, o segundo bloco try-except precisa ser recuado para que o código faça o mesmo).
Søren Løvborg 15/10/11