3

Web ページから「コンテンツ」を抽出するためにBeautiful Soupを使用しています。私は何人かの人々が以前にこの質問をしたことを知っています、そして彼らは皆美しいスープを指摘されました、そしてそれが私がそれを始めた方法です.

ほとんどのコンテンツを正常に取得できましたが、コンテンツの一部であるタグでいくつかの問題に直面しています。(私は基本的な戦略から始めています: ノードに x-chars を超えるものがある場合、それはコンテンツです)。例として、次の html コードを見てみましょう。

<div id="abc">
    some long text goes <a href="/"> here </a> and hopefully it 
    will get picked up by the parser as content
</div>

results = soup.findAll(text=lambda(x): len(x) > 20)

上記のコードを使用して長いテキストを取得すると、タグで中断されます (識別されたテキストは「そして願わくば..」から始まります)。そこで、次のようにタグをプレーンテキストに置き換えようとしました:

anchors = soup.findAll('a')

for a in anchors:
  a.replaceWith('plain text')

Beautiful Soup は文字列を NavigableString として挿入し、len(x) > 20 で findAll を使用すると同じ問題が発生するため、上記は機能しません。最初に正規表現を使用して html をプレーン テキストとして解析し、すべてクリアします。不要なタグを削除してから、Beautiful Soup を呼び出します。しかし、同じコンテンツを 2 回処理することは避けたいと思います。これらのページを解析して、特定のリンク (Facebook の共有と非常によく似ています) のコンテンツのスニペットを表示できるようにしています。速くなると思います。

私の質問: Beautiful Soup を使用して「タグをクリア」し、「プレーン テキスト」に置き換える方法はありますか。そうでない場合、そうするための最良の方法は何ですか?

ご提案いただきありがとうございます。

更新: Alex のコードは、サンプルの例で非常にうまく機能しました。また、さまざまなエッジケースを試しましたが、すべて正常に機能しました(以下の変更を加えて)。そこで、実際の Web サイトで試してみたところ、困惑する問題に遭遇しました。

import urllib
from BeautifulSoup import BeautifulSoup

page = urllib.urlopen('http://www.engadget.com/2010/01/12/kingston-ssdnow-v-dips-to-30gb-size-lower-price/')

anchors = soup.findAll('a')
i = 0
for a in anchors:
    print str(i) + ":" + str(a)
    for a in anchors:
        if (a.string is None): a.string = ''
        if (a.previousSibling is None and a.nextSibling is None):
            a.previousSibling = a.string
        elif (a.previousSibling is None and a.nextSibling is not None):
            a.nextSibling.replaceWith(a.string + a.nextSibling)
        elif (a.previousSibling is not None and a.nextSibling is None):
            a.previousSibling.replaceWith(a.previousSibling + a.string)
        else:
            a.previousSibling.replaceWith(a.previousSibling + a.string + a.nextSibling)
            a.nextSibling.extract()
    i = i+1

上記のコードを実行すると、次のエラーが発生します。

0:<a href="http://www.switched.com/category/ces-2010">Stay up to date with 
Switched's CES 2010 coverage</a>
Traceback (most recent call last):
  File "parselink.py", line 44, in <module>
  a.previousSibling.replaceWith(a.previousSibling + a.string + a.nextSibling)
 TypeError: unsupported operand type(s) for +: 'Tag' and 'NavigableString'

HTMLコードを見ると、「最新の状態に保つ..」には以前の兄弟がありません(アレックスのコードを見るまで、以前の兄弟がどのように機能したかわかりませんでした。テストに基づいて、「テキスト」を探しているように見えますですから、前の兄弟がいない場合、a.previousSibling is None と a;nextSibling is None の if ロジックを通過していないことに驚いています。

私が間違っていることを教えてください。

-エコグニウム

4

2 に答える 2

4

特定の例で機能するアプローチは次のとおりです。

from BeautifulSoup import BeautifulSoup

ht = '''
<div id="abc">
    some long text goes <a href="/"> here </a> and hopefully it 
    will get picked up by the parser as content
</div>
'''
soup = BeautifulSoup(ht)

anchors = soup.findAll('a')
for a in anchors:
  a.previousSibling.replaceWith(a.previousSibling + a.string)

results = soup.findAll(text=lambda(x): len(x) > 20)

print results

放出する

$ python bs.py
[u'\n    some long text goes  here ', u' and hopefully it \n    will get picked up by the parser as content\n']

もちろん、もう少し注意を払う必要があります。つまり、 が存在しないa.string場合、または存在する場合a.previousSiblingNoneどうなるでしょうか?ifそのようなまれなケースに対応するには、適切なステートメントが必要です。しかし、この一般的な考え方が役立つことを願っています。(実際には、次の兄弟が文字列の場合はマージすることできます-それがヒューリスティックでどのように機能するかはわかりませんが、たとえば、中央に5文字の文字列を含む2つの9文字の文字列があるとします、おそらく「23文字の文字列」としてロットを取得したいですか?私はあなたのヒューリスティックの動機を理解していないのでわかりません)。len(x) > 20<a>

タグのほかに、または、おそらくand/orなどの他の<a>タグも削除したいと思うと思います...? これも、ヒューリスティックの背後にある実際のアイデアが何であるかに依存すると思います!<b><strong><p><br>

于 2010-01-14T02:49:02.093 に答える
1

ドキュメント内のタグを平坦化しようとすると、タグのコンテンツ全体がその親ノードにプルアップされます (すべてのサブ段落、リスト、div、およびスパンを含むpタグのコンテンツを削減したかったのです)。などの内部ですが、スタイルフォントタグ、およびいくつかの恐ろしい単語からhtmlへのジェネレーターの残骸を取り除きます) 、ビューティフルスープ自体を扱うのはかなり複雑であることがわかりました。受け付けないなし引数として。いくつかのワイルドな再帰実験の後、最終的に、次の方法で BeautifulSoup を使用してドキュメントを処理する前または処理した後に、正規表現を使用することにしました。

import re
def flatten_tags(s, tags):
   pattern = re.compile(r"<(( )*|/?)(%s)(([^<>]*=\\\".*\\\")*|[^<>]*)/?>"%(isinstance(tags, basestring) and tags or "|".join(tags)))
   return pattern.sub("", s)

tags引数は、フラット化する単一のタグまたはタグのリストです。

于 2010-07-02T17:05:58.237 に答える