example python html text html-content-extraction

python - beautifulsoup example



Extraer texto de un archivo HTML usando Python (29)

Me gustaría extraer el texto de un archivo HTML usando Python. Quiero esencialmente la misma salida que obtendría si copiara el texto desde un navegador y lo pegara en el bloc de notas.

Me gustaría algo más robusto que usar expresiones regulares que puedan fallar en HTML mal formado. He visto a muchas personas recomendar Beautiful Soup, pero he tenido algunos problemas al usarlo. Por un lado, recogió texto no deseado, como fuente de JavaScript. Además, no interpretó las entidades HTML. Por ejemplo, yo esperaría que & # 39; en la fuente HTML para convertirla en un apóstrofe en el texto, como si hubiera pegado el contenido del navegador en el bloc de notas.

Actualizar html2text parece prometedor. Maneja las entidades HTML correctamente e ignora JavaScript. Sin embargo, no produce exactamente texto plano; produce una rebaja que luego tendría que convertirse en texto plano. Viene sin ejemplos o documentación, pero el código se ve limpio.

Preguntas relacionadas:


¿Alguien ha probado bleach.clean(html,tags=[],strip=True) con bleach ? esta funcionando para mi


Aquí está el código que utilizo de forma regular.

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 eso ayude.


Aquí hay una versión de la respuesta de xperroni que es un poco más completa. Omite las secciones de estilo y script y traduce las charrefs (por ejemplo, & # 39;) y las entidades HTML (por ejemplo, & amp;).

También incluye un convertidor inverso trivial de texto plano a 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)


El comentario del escritor de LibreOffice tiene mérito ya que la aplicación puede emplear macros de Python. Parece ofrecer múltiples beneficios tanto para responder esta pregunta como para promover la base macro de LibreOffice. Si esta resolución es una implementación única, en lugar de ser utilizada como parte de un programa de producción mayor, abrir el HTML en el escritor y guardar la página como texto parece resolver los problemas que se analizan aquí.


En Python 3.x puedes hacerlo de una manera muy sencilla importando paquetes ''imaplib'' y ''correo electrónico''. Aunque esta es una publicación más antigua, pero tal vez mi respuesta pueda ayudar a los recién llegados en esta publicación.

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

Ahora puede imprimir la variable del cuerpo y estará en formato de texto simple :) Si es lo suficientemente bueno para usted, sería bueno seleccionarlo como respuesta aceptada.


En lugar del módulo HTMLParser, echa un vistazo a htmllib. Tiene una interfaz similar, pero hace más del trabajo por ti. (Es bastante antiguo, por lo que no es de mucha ayuda para deshacerse de javascript y css. Podría crear una clase derivada, pero agregue métodos con nombres como start_script y end_style (consulte la documentación de Python para obtener más información), pero es difícil para hacer esto de manera confiable para html malformado.) De todos modos, aquí hay algo simple que imprime el texto sin formato en la consola

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)


Esto no es exactamente una solución de Python, pero convertirá el texto que Javascript generaría en texto, lo que creo que es importante (por ejemplo, google.com). Los enlaces del navegador (no Lynx) tienen un motor de Javascript y convertirán la fuente al texto con la opción -dump.

Así que podrías hacer 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()


Hay una biblioteca de patrones para la minería de datos.

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

Incluso puedes decidir qué etiquetas guardar:

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


He tenido buenos resultados con Apache Tika . Su propósito es la extracción de metadatos y texto del contenido, por lo tanto, el analizador subyacente se sintoniza en consecuencia fuera de la caja.

Tika se puede ejecutar como un server , es trivial de ejecutar / implementar en un contenedor Docker, y desde allí se puede acceder a través de enlaces Python .


Hermosa sopa convierte las entidades html. Probablemente sea su mejor apuesta considerando que HTML a menudo está lleno de errores y está lleno de problemas de codificación html y Unicode. Este es el código que utilizo para convertir html a texto sin formato:

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


La mejor pieza de código que encontré para extraer texto sin obtener javascript o cosas no deseadas:

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)

Solo tienes que instalar BeautifulSoup antes:

pip install beautifulsoup4


La respuesta de @ PeYoTIL usando BeautifulSoup y eliminando el estilo y el contenido del script no me funcionó. Lo intenté usando decompose lugar de extract pero todavía no funcionó. Así que creé el mío que también formatea el texto usando las etiquetas <p> y reemplaza las etiquetas <a> con el enlace href. También hace frente a los enlaces dentro del texto. Disponible en esta esencia con un documento de prueba integrado.

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


Lo estoy logrando algo así.

>>> import requests >>> url = "http://news.bbc.co.uk/2/hi/health/2284783.stm" >>> res = requests.get(url) >>> text = res.text


Lo mejor que he trabajado para mí son las inscripciones.

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)

Los resultados son realmente buenos.


Me encontré enfrentando el mismo problema hoy. Escribí un analizador HTML muy simple para eliminar el contenido entrante de todas las marcas, devolviendo el texto restante con un mínimo de formato.

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


Otra opción es ejecutar el html a través de un navegador web basado en texto y volcarlo. Por ejemplo (usando Lynx):

lynx -dump html_to_convert.html > converted_html.txt

Esto se puede hacer dentro de un script de python de la siguiente manera:

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

No le dará exactamente el texto del archivo HTML, pero dependiendo de su caso de uso puede ser preferible a la salida de html2text.


Otra solución que no es python: Libre Office:

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

La razón por la que prefiero esta opción por sobre otras alternativas es que cada párrafo HTML se convierte en una sola línea de texto (sin saltos de línea), que es lo que estaba buscando. Otros métodos requieren post-procesamiento. Lynx produce una buena salida, pero no es exactamente lo que estaba buscando. Además, Libre Office se puede utilizar para convertir desde todo tipo de formatos ...


Perl manera (lo siento mamá, nunca lo haré en producción).

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


PyParsing hace un gran trabajo. La wiki de PyParsing fue eliminada, así que aquí hay otra ubicación donde hay ejemplos del uso de PyParsing ( enlace de ejemplo ). Una de las razones para invertir un poco de tiempo en la reproducción es que también ha escrito un breve manual de O''Reilly Short Cut muy bien organizado que también es económico.

Habiendo dicho eso, uso BeautifulSoup mucho y no es tan difícil lidiar con los problemas de las entidades, puede convertirlos antes de ejecutar BeautifulSoup.

Buena suerte


Recomiendo un paquete de Python llamado goose-extractor Goose intentará extraer la siguiente información:

Texto principal de un artículo Imagen principal del artículo Cualquier película de Youtube / Vimeo incrustada en el artículo Meta descripción Etiquetas meta

Más: https://pypi.python.org/pypi/goose-extractor/


Sé que ya hay muchas respuestas aquí, pero creo que newspaper3k también merece una mención. Hace poco tuve que completar una tarea similar para extraer el texto de los artículos en la web y esta biblioteca ha hecho un excelente trabajo para lograr esto hasta ahora en mis pruebas. Ignora el texto que se encuentra en los elementos del menú y las barras laterales, así como cualquier JavaScript que aparezca en la página cuando el OP lo solicite.

from newspaper import Article article = Article(url) article.download() article.parse() article.text

Si ya tiene los archivos HTML descargados, puede hacer algo como esto:

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

Incluso tiene algunas características de PNL para resumir los temas de los artículos:

article.nlp() article.summary


Sé que ya hay muchas respuestas, pero la solución más elegante y pitónica que he encontrado se describe, en parte, here .

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

Actualizar

Basado en el comentario de Fraser, aquí hay una solución más elegante:

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


Si necesita más velocidad y menos precisión, entonces podría usar lxml en 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()


También puede utilizar el método html2text en la biblioteca de stripogram.

from stripogram import html2text text = html2text(your_html_string)

Para instalar stripogram ejecuta sudo easy_install stripogram


de una manera simple

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

este código encuentra todas las partes del texto html que comenzaron con ''<'' y terminaron con ''>'' y reemplazan todas las encontradas por una cadena vacía


instale html2text usando

pip instalar html2text

entonces,

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


solo puedes extraer texto de HTML con 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)


html2text es un programa de Python que hace un buen trabajo en esto.


NOTA: NTLK ya no admite la función clean_html

Respuesta original a continuación, y una alternativa en las secciones de comentarios.

Utilizar NLTK

Perdí mis 4-5 horas arreglando los problemas con html2text. Por suerte pude encontrarme con NLTK.
Funciona mágicamente.

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)