tutorial python text beautifulsoup html-content-extraction

tutorial - install beautifulsoup python 3



BeautifulSoup Grab Visible página web de texto (8)

El título está dentro de una etiqueta <nyt_headline> , que está anidada dentro de una etiqueta <h1> y una etiqueta <div> con un "artículo".

soup.findAll(''nyt_headline'', limit=1)

Deberia trabajar.

El cuerpo del artículo está dentro de una etiqueta <nyt_text> , que está anidada dentro de una etiqueta <div> con id "articleBody". Dentro del elemento <nyt_text> , el texto en sí está contenido dentro de las etiquetas <p> . Las imágenes no están dentro de esas etiquetas <p> . Es difícil para mí experimentar con la sintaxis, pero espero que un rasguño funcione de esta manera.

text = soup.findAll(''nyt_text'', limit=1)[0] text.findAll(''p'')

Básicamente, quiero usar BeautifulSoup para captar estrictamente el texto visible en una página web. Por ejemplo, esta página web es mi caso de prueba. Y principalmente quiero obtener el texto del cuerpo (artículo) y tal vez incluso algunos nombres de pestañas aquí y allá. He intentado la sugerencia en esta pregunta SO que devuelve muchas etiquetas <script> y comentarios html que no quiero. No puedo entender los argumentos que necesito para la función findAll() para obtener los textos visibles en una página web.

Entonces, ¿cómo debería encontrar todo el texto visible excluyendo scripts, comentarios, css, etc.?


La respuesta aprobada de @jbochi no funciona para mí. La llamada a la función str () genera una excepción porque no puede codificar los caracteres que no son ascii en el elemento BeautifulSoup. Aquí hay una manera más sucinta de filtrar la página web de ejemplo al texto visible.

html = open(''21storm.html'').read() soup = BeautifulSoup(html) [s.extract() for s in soup([''style'', ''script'', ''[document]'', ''head'', ''title''])] visible_text = soup.getText()


Prueba esto:

from bs4 import BeautifulSoup from bs4.element import Comment import urllib.request def tag_visible(element): if element.parent.name in [''style'', ''script'', ''head'', ''title'', ''meta'', ''[document]'']: return False if isinstance(element, Comment): return False return True def text_from_html(body): soup = BeautifulSoup(body, ''html.parser'') texts = soup.findAll(text=True) visible_texts = filter(tag_visible, texts) return u" ".join(t.strip() for t in visible_texts) html = urllib.request.urlopen(''http://www.nytimes.com/2009/12/21/us/21storm.html'').read() print(text_from_html(html))


Respeto completamente usar Beautiful Soup para obtener contenido renderizado, pero puede no ser el paquete ideal para adquirir el contenido presentado en una página.

Tuve un problema similar para obtener contenido renderizado o el contenido visible en un navegador típico. En particular, tuve muchos casos quizás atípicos para trabajar con un ejemplo tan simple a continuación. En este caso, la etiqueta no visualizable está anidada en una etiqueta de estilo, y no está visible en muchos navegadores que he verificado. Existen otras variaciones, como definir una pantalla de configuración de etiqueta de clase en none. Luego usando esta clase para el div.

<html> <title> Title here</title> <body> lots of text here <p> <br> <h1> even headings </h1> <style type="text/css"> <div > this will not be visible </div> </style> </body> </html>

Una solución publicada arriba es:

html = Utilities.ReadFile(''simple.html'') soup = BeautifulSoup.BeautifulSoup(html) texts = soup.findAll(text=True) visible_texts = filter(visible, texts) print(visible_texts) [u''/n'', u''/n'', u''/n/n lots of text here '', u'' '', u''/n'', u'' even headings '', u''/n'', u'' this will not be visible '', u''/n'', u''/n'']

Esta solución ciertamente tiene aplicaciones en muchos casos y generalmente funciona bastante bien, pero en el html publicado anteriormente conserva el texto que no se procesa. Después de buscar SO aparecieron un par de soluciones aquí BeautifulSoup get_text no quita todas las etiquetas y JavaScript y aquí Rendered HTML a texto plano usando Python

Probé ambas soluciones: html2text y nltk.clean_html y me sorprendieron los resultados de tiempo, así que pensé que justificaban una respuesta para la posteridad. Por supuesto, las velocidades dependen en gran medida del contenido de los datos ...

Una respuesta aquí de @Helge fue sobre usar nltk de todas las cosas.

import nltk %timeit nltk.clean_html(html) was returning 153 us per loop

Funcionó muy bien devolver una cadena con html renderizado. Este módulo nltk fue más rápido que incluso html2text, aunque quizás html2text sea más robusto.

betterHTML = html.decode(errors=''ignore'') %timeit html2text.html2text(betterHTML) %3.09 ms per loop


Si bien, sugeriría completamente usar beautiful-soup en general, si alguien está buscando mostrar las partes visibles de un html malformado (por ejemplo, donde tiene solo un segmento o línea de una página web) por cualquier motivo, el siguiente eliminará el contenido entre las etiquetas < y > :

import re ## only use with malformed html - this is not efficient def display_visible_html_using_re(text): return(re.sub("(/<.*?/>)", "",text))


Si te preocupa el rendimiento, esta es otra forma más eficiente:

import re INVISIBLE_ELEMS = (''style'', ''script'', ''head'', ''title'') RE_SPACES = re.compile(r''/s{3,}'') def visible_texts(soup): """ get visible text from a document """ text = '' ''.join([ s for s in soup.strings if s.parent.name not in INVISIBLE_ELEMS ]) # collapse multiple spaces to two spaces. return RE_SPACES.sub('' '', text)

soup.strings es un iterador, y devuelve NavigableString para que pueda verificar directamente el nombre de la etiqueta padre, sin pasar por múltiples bucles.


Usando BeautifulSoup de la manera más fácil con menos código para obtener las cadenas, sin líneas vacías y basura.

tag = <Parent_Tag_that_contains_the_data> soup = BeautifulSoup(tag, ''html.parser'') for i in soup.stripped_strings: print repr(i)


import urllib from bs4 import BeautifulSoup url = "https://www.yahoo.com" 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.encode(''utf-8''))