Pergunta

Eu gostaria de extrair o texto de um arquivo HTML usando Python. Eu quero essencialmente a mesma saída que eu receberia se copiasse o texto de um navegador e o colasse no bloco de notas.

Eu gostaria de algo mais robusto do que usar expressões regulares que podem falhar no HTML mal formado. Já vi muitas pessoas recomendarem sopa bonita, mas tive alguns problemas em usá -la. Por um lado, ele pegou texto indesejado, como a fonte JavaScript. Além disso, não interpretou as entidades HTML. Por exemplo, eu esperaria que 'na fonte HTML fosse convertida em um apóstrofo no texto, como se eu coloquei o conteúdo do navegador no bloco de notas.

Atualizar html2text parece promissor. Ele lida com entidades HTML corretamente e ignora o JavaScript. No entanto, ele não produz exatamente texto simples; Produz remarcar que teria que ser transformado em texto simples. Ele vem sem exemplos ou documentação, mas o código parece limpo.


Perguntas relacionadas:

Foi útil?

Solução

html2text é um programa Python que faz um bom trabalho nisso.

Outras dicas

A melhor peça de código que encontrei para extrair texto sem obter JavaScript ou não as coisas queriam:

import urllib
from bs4 import BeautifulSoup

url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"
html = urllib.urlopen(url).read()
soup = BeautifulSoup(html)

# kill all script and style elements
for script in soup(["script", "style"]):
    script.extract()    # rip it out

# get text
text = soup.get_text()

# break into lines and remove leading and trailing space on each
lines = (line.strip() for line in text.splitlines())
# break multi-headlines into a line each
chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
# drop blank lines
text = '\n'.join(chunk for chunk in chunks if chunk)

print(text)

Você só tem que instalar o BeautifulSoup antes:

pip install beautifulsoup4

NOTA: NTLK não suporta mais clean_html função

Resposta original abaixo e uma alternativa nas seções de comentários.


Usar Nltk

Eu perdi minhas 4-5 horas corrigindo os problemas com o HTML2TEXT. Felizmente, eu poderia encontrar o NLTK.
Funciona magicamente.

import nltk   
from urllib import urlopen

url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"    
html = urlopen(url).read()    
raw = nltk.clean_html(html)  
print(raw)

Me vi enfrentando o mesmo problema hoje. Escrevi um analisador HTML muito simples para retirar o conteúdo de todas as marcas, retornando o texto restante com apenas um mínimo de formatação.

from HTMLParser import HTMLParser
from re import sub
from sys import stderr
from traceback import print_exc

class _DeHTMLParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.__text = []

    def handle_data(self, data):
        text = data.strip()
        if len(text) > 0:
            text = sub('[ \t\r\n]+', ' ', text)
            self.__text.append(text + ' ')

    def handle_starttag(self, tag, attrs):
        if tag == 'p':
            self.__text.append('\n\n')
        elif tag == 'br':
            self.__text.append('\n')

    def handle_startendtag(self, tag, attrs):
        if tag == 'br':
            self.__text.append('\n\n')

    def text(self):
        return ''.join(self.__text).strip()


def dehtml(text):
    try:
        parser = _DeHTMLParser()
        parser.feed(text)
        parser.close()
        return parser.text()
    except:
        print_exc(file=stderr)
        return text


def main():
    text = r'''
        <html>
            <body>
                <b>Project:</b> DeHTML<br>
                <b>Description</b>:<br>
                This small script is intended to allow conversion from HTML markup to 
                plain text.
            </body>
        </html>
    '''
    print(dehtml(text))


if __name__ == '__main__':
    main()

Aqui está uma versão da resposta de Xperroni que é um pouco mais completa. Ele pula seções de script e estilo e traduz entidades Charrefs (por exemplo, ') e HTML (por exemplo, &).

Ele também inclui um conversor inverso trivial de texto simples para HTML.

"""
HTML <-> text conversions.
"""
from HTMLParser import HTMLParser, HTMLParseError
from htmlentitydefs import name2codepoint
import re

class _HTMLToText(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self._buf = []
        self.hide_output = False

    def handle_starttag(self, tag, attrs):
        if tag in ('p', 'br') and not self.hide_output:
            self._buf.append('\n')
        elif tag in ('script', 'style'):
            self.hide_output = True

    def handle_startendtag(self, tag, attrs):
        if tag == 'br':
            self._buf.append('\n')

    def handle_endtag(self, tag):
        if tag == 'p':
            self._buf.append('\n')
        elif tag in ('script', 'style'):
            self.hide_output = False

    def handle_data(self, text):
        if text and not self.hide_output:
            self._buf.append(re.sub(r'\s+', ' ', text))

    def handle_entityref(self, name):
        if name in name2codepoint and not self.hide_output:
            c = unichr(name2codepoint[name])
            self._buf.append(c)

    def handle_charref(self, name):
        if not self.hide_output:
            n = int(name[1:], 16) if name.startswith('x') else int(name)
            self._buf.append(unichr(n))

    def get_text(self):
        return re.sub(r' +', ' ', ''.join(self._buf))

def html_to_text(html):
    """
    Given a piece of HTML, return the plain text it contains.
    This handles entities and char refs, but not javascript and stylesheets.
    """
    parser = _HTMLToText()
    try:
        parser.feed(html)
        parser.close()
    except HTMLParseError:
        pass
    return parser.get_text()

def text_to_html(text):
    """
    Convert the given text to html, wrapping what looks like URLs with <a> tags,
    converting newlines to <br> tags and converting confusing chars into html
    entities.
    """
    def f(mo):
        t = mo.group()
        if len(t) == 1:
            return {'&':'&amp;', "'":'&#39;', '"':'&quot;', '<':'&lt;', '>':'&gt;'}.get(t)
        return '<a href="%s">%s</a>' % (t, t)
    return re.sub(r'https?://[^] ()"\';]+|[&\'"<>]', f, text)

Você também pode usar o método HTML2Text na biblioteca Stripograma.

from stripogram import html2text
text = html2text(your_html_string)

Para instalar o Stripogram Run sudo easy_install stripograma

Eu sei que já existem muitas respostas, mas mais elegante e Pitônico Solução que encontrei é descrita, em parte, aqui.

from bs4 import BeautifulSoup

text = ''.join(BeautifulSoup(some_html_string, "html.parser").findAll(text=True))

Atualizar

Com base no comentário de Fraser, aqui está a solução mais elegante:

from bs4 import BeautifulSoup

clean_text = ''.join(BeautifulSoup(some_html_string, "html.parser").stripped_strings)

Há biblioteca de padrões para mineração de dados.

http://www.clips.ua.ac.be/pages/pattern-web

Você pode até decidir quais tags manter:

s = URL('http://www.clips.ua.ac.be').download()
s = plaintext(s, keep={'h1':[], 'h2':[], 'strong':[], 'a':['href']})
print s

Pyparsing faz um ótimo trabalho. O wiki de Pyparsing foi morto, então aqui está outro local onde há exemplos do uso de Pyparsing (link de exemplo). Uma razão para investir um pouco de tempo com Pyparsing é que ele também escreveu um Manual de O'Reilly de O'Reilly muito bem organizado que também é barato.

Dito isto, eu uso muito o BeautifulSoup e não é tão difícil lidar com os problemas das entidades, você pode convertê -las antes de correr o BeautifulSoup.

Boa sorte

Esta não é exatamente uma solução Python, mas converterá o texto JavaScript gerado em texto, o que eu acho importante (por exemplo, google.com). Os links do navegador (não lynx) possuem um mecanismo JavaScript e converterá a fonte em texto com a opção -dump.

Então você poderia fazer algo como:

fname = os.tmpnam()
fname.write(html_source)
proc = subprocess.Popen(['links', '-dump', fname], 
                        stdout=subprocess.PIPE,
                        stderr=open('/dev/null','w'))
text = proc.stdout.read()

Em vez do módulo HTMLPARSER, consulte o htmllib. Ele tem uma interface semelhante, mas faz mais do trabalho para você. (É bastante antigo, por isso não ajuda muita Para fazer isso de maneira confiável para html malformado.) De qualquer forma, aqui está algo simples que imprime o texto simples no console

from htmllib import HTMLParser, HTMLParseError
from formatter import AbstractFormatter, DumbWriter
p = HTMLParser(AbstractFormatter(DumbWriter()))
try: p.feed('hello<br>there'); p.close() #calling close is not usually needed, but let's play it safe
except HTMLParseError: print ':(' #the html is badly malformed (or you found a bug)

Se você precisar de mais velocidade e menos precisão, poderá usar o LXML bruto.

import lxml.html as lh
from lxml.html.clean import clean_html

def lxml_to_text(html):
    doc = lh.fromstring(html)
    doc = clean_html(doc)
    return doc.text_content()

instalar html2text usando

pip install html2text

então,

>>> import html2text
>>>
>>> h = html2text.HTML2Text()
>>> # Ignore converting links from HTML
>>> h.ignore_links = True
>>> print h.handle("<p>Hello, <a href='http://earth.google.com/'>world</a>!")
Hello, world!

Linda sopa converte entidades HTML. Provavelmente é a sua melhor aposta, considerando que o HTML geralmente é buggy e cheio de problemas de codificação Unicode e HTML. Este é o código que eu uso para converter HTML em texto bruto:

import BeautifulSoup
def getsoup(data, to_unicode=False):
    data = data.replace("&nbsp;", " ")
    # Fixes for bad markup I've seen in the wild.  Remove if not applicable.
    masssage_bad_comments = [
        (re.compile('<!-([^-])'), lambda match: '<!--' + match.group(1)),
        (re.compile('<!WWWAnswer T[=\w\d\s]*>'), lambda match: '<!--' + match.group(0) + '-->'),
    ]
    myNewMassage = copy.copy(BeautifulSoup.BeautifulSoup.MARKUP_MASSAGE)
    myNewMassage.extend(masssage_bad_comments)
    return BeautifulSoup.BeautifulSoup(data, markupMassage=myNewMassage,
        convertEntities=BeautifulSoup.BeautifulSoup.ALL_ENTITIES 
                    if to_unicode else None)

remove_html = lambda c: getsoup(c, to_unicode=True).getText(separator=u' ') if c else ""

Eu recomendo um pacote Python chamado Goose-Extractor Goose tentará extrair as seguintes informações:

Texto principal de um artigo Imagem principal do artigo qualquer filme do YouTube/Vimeo incorporado no artigo Meta Descrição Meta Tags

Mais :https://pypi.python.org/pypi/goose-extractor/

Outra opção é executar o HTML por meio de um navegador da Web baseado em texto e despejá -lo. Por exemplo (usando lynx):

lynx -dump html_to_convert.html > converted_html.txt

Isso pode ser feito dentro de um script python da seguinte maneira:

import subprocess

with open('converted_html.txt', 'w') as outputFile:
    subprocess.call(['lynx', '-dump', 'html_to_convert.html'], stdout=testFile)

Não fornecerá exatamente apenas o texto do arquivo HTML, mas, dependendo do seu caso de uso, pode ser preferível à saída do HTML2Text.

Outra solução não-python: Libre Office:

soffice --headless --invisible --convert-to txt input1.html

A razão pela qual prefiro essa a outras alternativas é que todo parágrafo HTML é convertido em uma única linha de texto (sem quebras de linha), que era o que eu estava procurando. Outros métodos requerem pós-processamento. O Lynx produz uma boa saída, mas não exatamente o que eu estava procurando. Além disso, o escritório da Libre pode ser usado para converter de todos os tipos de formatos ...

Alguém já tentou bleach.clean(html,tags=[],strip=True) com água sanitária? Está funcionando para mim.

Eu sei que já existem muitas respostas aqui, mas acho jornal3k Também merece uma menção. Recentemente, eu precisava concluir uma tarefa semelhante de extrair o texto dos artigos na web e essa biblioteca fez um excelente trabalho ao conseguir isso até agora em meus testes. Ele ignora o texto encontrado nos itens do menu e nas barras laterais, bem como em qualquer JavaScript que apareça na página como solicitações de OP.

from newspaper import Article

article = Article(url)
article.download()
article.parse()
article.text

Se você já possui os arquivos HTML baixados, pode fazer algo assim:

article = Article('')
article.set_html(html)
article.parse()
article.text

Ele ainda possui alguns recursos de PNL para resumir os tópicos de artigos:

article.nlp()
article.summary

Eu tive bons resultados com Apache Tika. Seu objetivo é a extração de metadados e texto do conteúdo, portanto, o analisador subjacente é ajustado de acordo com a caixa.

Tika pode ser executado como um servidor, é trivial para executar / implantar em um contêiner do Docker e a partir daí pode ser acessado via Python Bindings.

de uma maneira simples

import re

html_text = open('html_file.html').read()
text_filtered = re.sub(r'<(.*?)>', '', html_text)

Este código encontra todas as partes do html_text iniciado com '<' e terminando com '>' e substituem tudo encontrado por uma corda vazia

@A resposta de Peyotil usando o BeautifulSoup e eliminando o estilo e o conteúdo do script não funcionou para mim. Eu tentei usando decompose ao invés de extract Mas ainda não funcionou. Então eu criei o meu próprio que também formata o texto usando o <p> tags e substitui <a> Tags com o link href. Também lida com os links dentro do texto. Disponível em esta essência com um documento de teste incorporado.

from bs4 import BeautifulSoup, NavigableString

def html_to_text(html):
    "Creates a formatted text email message as a string from a rendered html template (page)"
    soup = BeautifulSoup(html, 'html.parser')
    # Ignore anything in head
    body, text = soup.body, []
    for element in body.descendants:
        # We use type and not isinstance since comments, cdata, etc are subclasses that we don't want
        if type(element) == NavigableString:
            # We use the assumption that other tags can't be inside a script or style
            if element.parent.name in ('script', 'style'):
                continue

            # remove any multiple and leading/trailing whitespace
            string = ' '.join(element.string.split())
            if string:
                if element.parent.name == 'a':
                    a_tag = element.parent
                    # replace link text with the link
                    string = a_tag['href']
                    # concatenate with any non-empty immediately previous string
                    if (    type(a_tag.previous_sibling) == NavigableString and
                            a_tag.previous_sibling.string.strip() ):
                        text[-1] = text[-1] + ' ' + string
                        continue
                elif element.previous_sibling and element.previous_sibling.name == 'a':
                    text[-1] = text[-1] + ' ' + string
                    continue
                elif element.parent.name == 'p':
                    # Add extra paragraph formatting newline
                    string = '\n' + string
                text += [string]
    doc = '\n'.join(text)
    return doc

No Python 3.x, você pode fazê -lo de uma maneira muito fácil, importando pacotes de 'imaplib' e 'email'. Embora este seja uma postagem mais antiga, mas talvez minha resposta possa ajudar os novos cantos nesta postagem.

status, data = self.imap.fetch(num, '(RFC822)')
email_msg = email.message_from_bytes(data[0][1]) 
#email.message_from_string(data[0][1])

#If message is multi part we only want the text version of the body, this walks the message and gets the body.

if email_msg.is_multipart():
    for part in email_msg.walk():       
        if part.get_content_type() == "text/plain":
            body = part.get_payload(decode=True) #to control automatic email-style MIME decoding (e.g., Base64, uuencode, quoted-printable)
            body = body.decode()
        elif part.get_content_type() == "text/html":
            continue

Agora você pode imprimir a variável do corpo e ela estará no formato de texto simples :) Se for bom o suficiente para você, seria bom selecioná -lo como resposta aceita.

Melhor trabalhado para mim é as inscrições.

https://github.com/weblyzard/inscriptis

import urllib.request
from inscriptis import get_text

url = "http://www.informationscience.ch"
html = urllib.request.urlopen(url).read().decode('utf-8')

text = get_text(html)
print(text)

Os resultados são realmente bons

Você pode extrair apenas texto do HTML com o BeautifulSoup

url = "https://www.geeksforgeeks.org/extracting-email-addresses-using-regular-expressions-python/"
con = urlopen(url).read()
soup = BeautifulSoup(con,'html.parser')
texts = soup.get_text()
print(texts)

Enquanto muitas pessoas mencionadas usando Regex para retirar as tags HTML, há muitas desvantagens.

por exemplo:

<p>hello&nbsp;world</p>I love you

Deve ser analisado para:

Hello world
I love you

Aqui está um trecho que eu inventei, você pode cusomizá -lo às suas necessidades específicas, e funciona como um charme

import re
import html
def html2text(htm):
    ret = html.unescape(htm)
    ret = ret.translate({
        8209: ord('-'),
        8220: ord('"'),
        8221: ord('"'),
        160: ord(' '),
    })
    ret = re.sub(r"\s", " ", ret, flags = re.MULTILINE)
    ret = re.sub("<br>|<br />|</p>|</div>|</h\d>", "\n", ret, flags = re.IGNORECASE)
    ret = re.sub('<.*?>', ' ', ret, flags=re.DOTALL)
    ret = re.sub(r"  +", " ", ret)
    return ret

Aqui está o código que eu uso regularmente.

from bs4 import BeautifulSoup
import urllib.request


def processText(webpage):

    # EMPTY LIST TO STORE PROCESSED TEXT
    proc_text = []

    try:
        news_open = urllib.request.urlopen(webpage.group())
        news_soup = BeautifulSoup(news_open, "lxml")
        news_para = news_soup.find_all("p", text = True)

        for item in news_para:
            # SPLIT WORDS, JOIN WORDS TO REMOVE EXTRA SPACES
            para_text = (' ').join((item.text).split())

            # COMBINE LINES/PARAGRAPHS INTO A LIST
            proc_text.append(para_text)

    except urllib.error.HTTPError:
        pass

    return proc_text

Espero que isso ajude.

O Comentário do LibreOffice Writer tem mérito, pois o aplicativo pode empregar macros Python. Parece oferecer vários benefícios tanto para responder a essa pergunta quanto para promover a base macro do LibreOffice. Se essa resolução for uma implementação única, em vez de ser usada como parte de um programa de produção maior, abrir o HTML no escritor e salvar a página como texto parece resolver os problemas discutidos aqui.

Perl Way (desculpe, mãe, nunca farei isso em produção).

import re

def html2text(html):
    res = re.sub('<.*?>', ' ', html, flags=re.DOTALL | re.MULTILINE)
    res = re.sub('\n+', '\n', res)
    res = re.sub('\r+', '', res)
    res = re.sub('[\t ]+', ' ', res)
    res = re.sub('\t+', '\t', res)
    res = re.sub('(\n )+', '\n ', res)
    return res

Estou alcançando algo assim.

>>> import requests
>>> url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"
>>> res = requests.get(url)
>>> text = res.text
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top