web-dev-qa-db-ja.com

BeautifulSoupは、目に見えるウェブページのテキストをつかむ

基本的に、BeautifulSoupを使用して、Webページ上のvisible textを厳密に取得します。たとえば、 このWebページ は私のテストケースです。そして、私は主に本文テキスト(記事)を取得し、おそらくいくつかのタブ名さえあちこちで取得したいです。私はこれで提案を試みました SO question これは多くの<script>タグと、望まないhtmlコメントを返します。関数 findAll() に必要な引数がわからないため、Webページに表示されるテキストを取得するだけです。

それでは、スクリプト、コメント、CSSなどを除くすべての表示テキストをどのように見つける必要がありますか?

111
user233864

これを試して:

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

@jbochiからの承認された回答は私には機能しません。 str()関数呼び出しは、BeautifulSoup要素内の非ASCII文字をエンコードできないため、例外を発生させます。サンプルWebページを表示テキストにフィルターするより簡潔な方法を次に示します。

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

Beautiful Soupを使用してレンダリングされたコンテンツを取得することは完全に尊重しますが、ページ上のレンダリングされたコンテンツを取得するための理想的なパッケージではない場合があります。

レンダリングされたコンテンツ、または一般的なブラウザで表示されるコンテンツを取得するために、同様の問題がありました。特に、以下のような単純な例を使用するために、おそらく非定型的なケースが多くありました。この場合、非表示タグはスタイルタグにネストされており、チェックした多くのブラウザーでは表示されません。クラスタグ設定表示をなしに定義するなど、他のバリエーションもあります。次に、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>

上記のソリューションの1つは次のとおりです。

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を除去しません そしてここ Pythonを使用してHTMLをプレーンテキストにレンダリングします

Html2textとnltk.clean_htmlの両方のソリューションを試しましたが、タイミングの結果に驚いたため、後世への答えが必要だと考えました。もちろん、速度はデータの内容に大きく依存します...

@Helgeからの1つの答えは、あらゆるものの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
10
Paul

パフォーマンスに関心がある場合は、もう1つのより効率的な方法があります。

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を返すため、複数のループを経由せずに、親のタグ名を直接確認できます。

2
Polor Beer

BeautifulSoupを使用して最も簡単な方法で少ないコードで文字列を取得するだけで、空の行やがらくたはありません。

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

for i in soup.stripped_strings:
    print repr(i)
2
Diego Suarez

一方、何らかの理由で誰かが不正な形式のhtmlの可視部分(たとえば、Webページのセグメントまたは行だけがある場合)を表示しようとしている場合、一般的には美しいスープを使用することをお勧めします。 <>タグの間のコンテンツを削除します。

import re   ## only use with malformed html - this is not efficient
def display_visible_html_using_re(text):             
    return(re.sub("(\<.*?\>)", "",text))
1
kyrenia

タイトルは<nyt_headline>タグ内にあり、<h1>タグとID「article」を持つ<div>タグ内にネストされています。

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

動作するはずです。

記事の本文は<nyt_text>タグ内にあり、これはID「articleBody」を持つ<div>タグ内にネストされています。 <nyt_text>要素内では、テキスト自体が<p>タグ内に含まれています。画像はこれらの<p>タグ内にありません。構文を試すのは難しいですが、実際のスクレイプはこのようなものになると思います。

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