BeautifulSoup Grab Visible Page Web Text

fondamentalement, je veux utiliser BeautifulSoup pour saisir strictement le texte visible sur une page web. Par exemple, cette page web est mon cas d'essai. Et je veux principalement obtenir le corps du texte (article) et peut-être même quelques noms d'onglet ici et là. J'ai essayé la suggestion dans ce alors question qui renvoie beaucoup de tags <script> et des commentaires html que je ne veux pas. Je n'arrive pas à comprendre les arguments dont j'ai besoin pour fonction findAll() pour obtenir les textes visibles sur une page web.

alors, comment puis-je trouver tout le texte visible à l'exclusion des scripts, commentaires, css etc.?

96
demandé sur Community 2009-12-20 20:55:58

8 réponses

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))
166
répondu jbochi 2018-02-05 08:24:30

la réponse approuvée de @jbochi ne fonctionne pas pour moi. L'appel de fonction str () soulève une exception car il ne peut pas encoder les caractères non-ascii dans L'élément BeautifulSoup. Voici une façon plus succincte de filtrer la page Web de l'exemple vers du 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()
28
répondu nmgeek 2013-11-04 00:35:55
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'))
27
répondu bumpkin 2014-07-26 07:32:55

je respecte complètement l'utilisation de la belle soupe pour obtenir le contenu rendu, mais il ne peut pas être le paquet idéal pour acquérir le contenu rendu sur une page.

j'ai eu un problème similaire pour obtenir rendu le contenu, ou le contenu visible dans un navigateur typique. En particulier, j'ai eu de nombreux cas peut-être atypiques à travailler avec un exemple si simple ci-dessous. Dans ce cas, l'étiquette non affichable est imbriquée dans une étiquette de style, et n'est pas visible dans de nombreux navigateurs que j'ai vérifiés. D'autres variantes existent telles que la définition d'un affichage d'étiquette de classe à zéro. Puis utiliser 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 des solutions proposées ci-dessus est:

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 assez bien généralement mais dans le html affiché ci-dessus il conserve le texte qui n'est pas rendu. Après avoir cherché ainsi un couple de solutions est venu ici BeautifulSoup get_text ne se déshabille pas toutes les balises et JavaScript et ici rendu HTML à texte en clair en utilisant Python

j'ai essayé ces deux solutions: html2text et nltk.clean_html et a été surpris par les résultats du timing donc pensé 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 d'utiliser nltk de toutes choses.

import nltk

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

Il fonctionnait vraiment bien pour retourner une chaîne de caractères Avec rendu html. Ce module nltk était plus rapide que même html2text, bien que html2text soit peut-être plus robuste.

betterHTML = html.decode(errors='ignore')
%timeit html2text.html2text(betterHTML)
%3.09 ms per loop
9
répondu Paul 2017-05-23 12:02:47

utilisant BeautifulSoup de la façon la plus facile avec moins de code pour obtenir les cordes, sans lignes vides et la merde.

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

for i in soup.stripped_strings:
    print repr(i)
2
répondu Diego Suarez 2017-05-01 03:50:58

le titre est à l'intérieur d'une étiquette <nyt_headline> , qui est imbriquée dans une étiquette <h1> et une étiquette <div> avec id"article".

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

devrait marcher.

le corps de l'article est à l'intérieur d'une étiquette <nyt_text> , qui est imbriquée dans une étiquette <div> avec id"articleBody". À l'intérieur de l'élément <nyt_text> , le texte lui-même est contenu dans les balises <p> . Les Images ne sont pas dans ces étiquettes <p> . C'est difficile pour moi d'expérimenter avec la syntaxe, mais je m'attends à un travail gratter à ressembler à quelque chose comme ça.

text = soup.findAll('nyt_text', limit=1)[0]
text.findAll('p')
1
répondu Ewan Todd 2009-12-20 18:40:54

alors que, je suggérerais complètement l'utilisation de beautiful-soup 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 quelque raison que ce soit, le suivant va supprimer le contenu entre < et > tags:

import re   ## only use with malformed html - this is not efficient
def display_visible_html_using_re(text):             
    return(re.sub("(\<.*?\>)", "",text))
1
répondu kyrenia 2015-05-03 20:39:31

si vous vous souciez de la performance, voici un autre moyen 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 renvoie NavigableString de sorte que vous pouvez vérifier le nom de la balise du parent directement, sans passer par plusieurs boucles.

1
répondu Polor Beer 2017-06-18 03:26:18