Question

En fait, je veux utiliser BeautifulSoup pour saisir strictement le texte visible sur une page Web. Par exemple, cette page est mon cas de test. Et je veux surtout juste obtenir le corps du texte (article) et peut-être même quelques noms d'onglets ici et là. J'ai essayé la suggestion dans cette question SO renvoie beaucoup de balises <script> et commentaires html que je ne veux pas. Je ne peux pas comprendre les arguments dont j'ai besoin pour la fonction findAll() afin d'obtenir que les textes visibles sur une page Web.

Alors, comment dois-je trouver tout le texte visible à l'exception des scripts, des commentaires, etc css.

Était-ce utile?

La solution

Essayez ceci:

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

Autres conseils

La réponse approuvée de @jbochi ne fonctionne pas pour moi. L'appel de la fonction str () déclenche une exception, car il ne peut pas coder les caractères non-ascii dans l'élément BeautifulSoup. Voici une façon plus succincte pour filtrer la page Web par exemple au texte visible.

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

Je respecte tout à fait à l'aide Belle soupe pour obtenir le contenu rendu, mais il ne peut pas être le package idéal pour l'acquisition du contenu rendu sur une page.

J'ai eu un problème similaire pour obtenir le contenu rendu, ou le contenu visible dans un navigateur typique. En particulier, j'ai eu de nombreux cas, peut-être atypiques pour travailler avec un tel exemple simple ci-dessous. Dans ce cas, l'étiquette non affichable est imbriquée dans une balise de style et n'est pas visible dans de nombreux navigateurs que j'ai vérifié. D'autres variantes existent telles que la définition d'un écran de réglage de balise de classe sans pareil. Ensuite, en utilisant cette classe pour la 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>

Une solution ci-dessus est affiché:

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

Cette solution a certainement des applications dans de nombreux cas et fait le travail très bien en général, mais dans le code HTML affiché ci-dessus, il conserve le texte qui n'est pas rendu. Après la recherche SO une solution de couple est venu ici BeautifulSoup get_text ne bande pas tous les tags et JavaScript et ici HTML Rendus en texte brut en utilisant python

J'ai essayé ces deux solutions: html2text et nltk.clean_html et a été surpris par le moment les résultats obtenus jusqu'à pensaient qu'ils justifiaient une réponse pour la postérité. Bien sûr, les vitesses dépendent fortement du contenu des données ...

Une réponse ici de @Helge était sur l'utilisation NLTK de toutes choses.

import nltk

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

Il a vraiment bien pour retourner une chaîne en HTML rendu. Ce module de NLTK était plus rapide que même html2text, mais peut-être html2text est plus robuste.

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

Utilisation BeautifulSoup la meilleure façon avec moins de code pour obtenir exactement les cordes, sans lignes et merde vides.

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

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

Si vous vous souciez de la performance, voici une autre façon plus 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 est un itérateur, et il retourne NavigableString de sorte que vous pouvez vérifier le nom de l'étiquette du parent directement, sans passer par de multiples boucles.

Le titre est à l'intérieur d'une balise <nyt_headline>, qui est imbriqué dans une balise <h1> et une étiquette de <div> avec id « article ».

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

Si le travail.

Le corps de l'article est à l'intérieur d'une balise <nyt_text>, qui est imbriquée dans une balise <div> avec id « ArticleBody ». A l'intérieur de l'élément de <nyt_text>, le texte lui-même est contenu dans les balises <p>. Les images ne sont pas dans ces balises <p>. Il est difficile pour moi d'expérimenter avec la syntaxe, mais je me attends un coup de racloir de travail pour ressembler à quelque chose comme ça.

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

Alors, je suggère complètement en utilisant belle soupe en général, si quelqu'un cherche à afficher les parties visibles d'un html malformé (par exemple où vous avez juste un segment ou une ligne d'une page Web) pour tout-raison, le suit supprimera le contenu entre les balises de < et >:

import re   ## only use with malformed html - this is not efficient
def display_visible_html_using_re(text):             
    return(re.sub("(\<.*?\>)", "",text))
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top