BeautifulSoup Grab Видимый текст веб-страницы - PullRequest
108 голосов
/ 20 декабря 2009

По сути, я хочу использовать BeautifulSoup для строгого захвата видимого текста на веб-странице. Например, эта веб-страница - мой тестовый пример. И я в основном хочу просто получить основной текст (статью) и, возможно, даже несколько названий вкладок здесь и там. Я попробовал предложение в этом ТАК вопрос , который возвращает много <script> тегов и HTML-комментариев, которые я не хочу. Я не могу определить аргументы, которые мне нужны для функции findAll(), чтобы просто получить видимые тексты на веб-странице.

Итак, как мне найти весь видимый текст, кроме скриптов, комментариев, CSS и т. Д.? * 10101 *

Ответы [ 8 ]

193 голосов
/ 31 декабря 2009

Попробуйте это:

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))
32 голосов
/ 04 ноября 2013

Одобренный ответ от @jbochi у меня не работает. Вызов функции str () вызывает исключение, поскольку он не может кодировать не-ascii символы в элементе BeautifulSoup. Вот более краткий способ фильтрации веб-страницы примера по видимому тексту.

html = open('21storm.html').read()
soup = BeautifulSoup(html)
[s.extract() for s in soup(['style', 'script', '[document]', 'head', 'title'])]
visible_text = soup.getText()
29 голосов
/ 26 июля 2014
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'))
10 голосов
/ 05 ноября 2013

Я полностью уважаю использование Beautiful Soup для получения визуализированного контента, но это может быть не идеальный пакет для получения визуализированного контента на странице.

У меня была похожая проблема с получением визуализированного контента или видимого контента в обычном браузере. В частности, у меня было много, возможно, нетипичных случаев для работы с таким простым примером ниже. В этом случае не отображаемый тег вложен в тег стиля и не виден во многих браузерах, которые я проверял. Существуют и другие варианты, такие как определение отображения тега класса для none. Затем, используя этот класс для 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>

Одно из опубликованных выше решений:

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

Это решение, безусловно, имеет приложения во многих случаях и в целом выполняет работу достаточно хорошо, но в HTML-коде, размещенном выше, оно сохраняет текст, который не отображается. После поиска SO было найдено несколько решений BeautifulSoup get_text не удаляет все теги и JavaScript , а здесь Отображает HTML в простой текст, используя Python

Я испробовал оба эти решения: html2text и nltk.clean_html и был удивлен результатами синхронизации, так что я подумал, что они гарантируют ответ для потомков. Конечно, скорости сильно зависят от содержимого данных ...

Один ответ здесь @Helge был об использовании nltk всех вещей.

import nltk

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

Очень хорошо работало, чтобы вернуть строку с визуализированным html. Этот модуль nltk был быстрее, чем даже html2text, хотя, возможно, html2text более устойчив.

betterHTML = html.decode(errors='ignore')
%timeit html2text.html2text(betterHTML)
%3.09 ms per loop
2 голосов
/ 01 мая 2017

Использование BeautifulSoup - самый простой способ с меньшим количеством кода, чтобы просто получить строки, без пустых строк и дерьма.

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

for i in soup.stripped_strings:
    print repr(i)
1 голос
/ 18 июня 2017

Если вы заботитесь о производительности, вот еще один более эффективный способ:

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 является итератором, и он возвращает NavigableString, так что вы можете проверить имя тега родителя напрямую, без прохождения нескольких циклов.

1 голос
/ 03 мая 2015

Хотя я бы полностью предложил использовать Beautiful-Soup в общем, если кто-то хочет показать видимые части искаженного HTML (например, если у вас есть только сегмент или строка веб-страницы) по любой причине, следующее удалит содержимое между тегами < и >:

import re   ## only use with malformed html - this is not efficient
def display_visible_html_using_re(text):             
    return(re.sub("(\<.*?\>)", "",text))
1 голос
/ 20 декабря 2009

Заголовок находится внутри тега <nyt_headline>, который вложен в тег <h1> и тег <div> с идентификатором "article".

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

Должно работать.

Тело статьи находится внутри тега <nyt_text>, который вложен в тег <div> с идентификатором "articleBody". Внутри элемента <nyt_text> сам текст содержится в тегах <p>. Изображения не входят в эти теги <p>. Мне сложно экспериментировать с синтаксисом, но я ожидаю, что рабочий скребок будет выглядеть примерно так.

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