Domanda

In sostanza, voglio usare BeautifulSoup per afferrare strettamente il testo visibile su una pagina web. Per esempio, il sito è il mio banco di prova. E vorrei soprattutto per ottenere solo il corpo del testo (articolo) e forse anche un paio di nomi delle schede qua e là. Ho provato il suggerimento in questo domanda SO che restituisce un sacco di tag e commenti <script> html che non voglio. Io non riesco a capire gli argomenti di cui ho bisogno per la funzione findAll() al fine di ottenere solo i testi visibili su una pagina web.

Quindi, come devo trovare tutto il testo visibile escludendo gli script, i commenti, css, ecc.?

È stato utile?

Soluzione

Prova questo:

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

Altri suggerimenti

La risposta approvato dal @jbochi non funziona per me. La chiamata di funzione str () solleva un'eccezione perché non può codificare i caratteri non ASCII nell'elemento BeautifulSoup. Ecco un modo più succinto per filtrare la pagina di esempio Web per testo visibile.

html = open('21storm.html').read()
soup = BeautifulSoup(html)
[s.extract() for s in soup(['style', 'script', '[document]', 'head', 'title'])]
visible_text = soup.getText()
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'))

Sono completamente rispetto usando Beautiful Soup per ottenere contenuti reso, ma potrebbe non essere il pacchetto ideale per acquisire il contenuto visualizzato su una pagina.

Ho avuto un problema simile per ottenere contenuti reso, o il contenuto visibile in un browser tipico. In particolare, ho avuto molti casi forse atipici per lavorare con un semplice esempio come qui di seguito. In questo caso il tag non visualizzabile è annidato in un tag stile, e non è visibile in molti browser che ho controllato. Altre variazioni esistono come ad esempio la definizione di un tag di classe display di impostazione a nessuno. Quindi, utilizzando questa classe per il 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 soluzione postato sopra è:

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

Questa soluzione ha certamente applicazioni in molti casi, e fa il lavoro abbastanza bene in generale ma in html postato sopra mantiene il testo che non viene eseguito il rendering. Dopo aver cercato quindi un paio di soluzioni venuto qui BeautifulSoup get_text non elimina tutti i tag e JavaScript e qui rendering HTML in testo normale utilizzando Python

Ho provato entrambe le soluzioni: html2text e nltk.clean_html e rimasto sorpreso dalla tempistica risultati in modo pensavano garantiti una risposta per i posteri. Naturalmente, le velocità altamente dipendono dal contenuto dei dati ...

Una risposta qui dal @Helge era sull'utilizzo NLTK di tutte le cose.

import nltk

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

Ha funzionato davvero bene per restituire una stringa con rendering HTML. Questo modulo NLTK stato più veloce di anche html2text, anche se forse html2text è più robusto.

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

Utilizzando BeautifulSoup il modo più semplice con meno codice per ottenere solo le corde, senza linee vuote e merda.

tag = <Parent_Tag_that_contains_the_data>
soup = BeautifulSoup(tag, 'html.parser')

for i in soup.stripped_strings:
    print repr(i)

Se avete a cuore le prestazioni, ecco un altro modo più efficace:

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 è un iteratore, e restituisce NavigableString in modo da poter controllare il nome di tag del genitore direttamente, senza passare attraverso cicli multipli.

Il titolo è all'interno di un tag <nyt_headline>, che è annidato all'interno di un tag <h1> e un tag <div> con id "articolo".

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

Dovrebbe funzionare.

Il corpo dell'articolo è all'interno di un tag <nyt_text>, che è nidificato all'interno di un tag <div> con id "ArticleBody". All'interno dell'elemento <nyt_text>, il testo stesso è contenuto all'interno dei tag <p>. Le immagini non sono all'interno di questi tag <p>. E 'difficile per me di sperimentare con la sintassi, ma mi aspetto una raschiatura di lavoro a guardare qualcosa di simile.

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

Mentre, vorrei completamente suggerirei di usare bella zuppa, in generale, se qualcuno sta cercando di visualizzare le parti visibili di un html non valido (ad esempio, dove si ha solo un segmento o una linea di una pagina web) per qualunque-ragione, il seguito rimuoverà il contenuto tra < e > tag:

import re   ## only use with malformed html - this is not efficient
def display_visible_html_using_re(text):             
    return(re.sub("(\<.*?\>)", "",text))
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top