BeautifulSoup Grab Visible Webpage Text
В принципе, я хочу использовать BeautifulSoup, чтобы строго фиксировать видимый текст на веб-странице. Например, эта веб-страница - это мой тестовый пример. И я в основном хочу просто получить текстовый текст (статья) и, возможно, даже несколько названий вкладок здесь и там. Я пробовал предложение в этом вопросе SO, который возвращает много тегов <script>
и html комментариев, которые я не хочу. Я не могу определить аргументы, которые мне нужны для функции findAll()
, чтобы просто получить видимые тексты на веб-странице.
Итак, как мне найти все видимые тексты, исключая скрипты, комментарии, css и т.д.?
Ответы
Ответ 1
Попробуйте следующее:
from bs4 import BeautifulSoup
from bs4.element import Comment
import urllib
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.urlopen('http://www.nytimes.com/2009/12/21/us/21storm.html').read()
print(text_from_html(html))
Ответ 2
Утвержденный ответ от @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()
Ответ 3
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'))
Ответ 4
Я полностью уважаю использование 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
Ответ 5
Использование BeautifulSoup проще всего с меньшим количеством кода, чтобы просто получить строки, без пустых строк и дерьма.
tag = <Parent_Tag_that_contains_the_data>
soup = BeautifulSoup(tag, 'html.parser')
for i in soup.stripped_strings:
print repr(i)
Ответ 6
Хотя я бы полностью предложил использовать красивый суп в целом, если кто-то хочет отображать видимые части искаженного html (например, где у вас есть только сегмент или строка веб-страницы) по любой причине, следующее будет удалять контент между тегами <
и >
:
import re ## only use with malformed html - this is not efficient
def display_visible_html_using_re(text):
return(re.sub("(\<.*?\>)", "",text))
Ответ 7
Название находится внутри тега <nyt_headline>
, который вложен внутри тега <h1>
и тега <div>
с идентификатором "статья".
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')
Ответ 8
Если вы заботитесь о производительности, еще один более эффективный способ:
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
, чтобы вы могли напрямую проверить имя родительского тега, не пройдя несколько циклов.