]>
<< テクニカルエンジニア(ネットワーク) | main | KernelNewbies.org >>
ちょっとHTMLをパースする必要があったので、BeautifulSoupを使ってみました。参考にさせていただいたサイトはこちら。
あかさかランチにっき: BeautifulSoupによるスクレイピングの練習
あかさかランチにっき: 続・BeautifulSoupのスクレイピングの練習
Perl使いのPythonちゃん: BeautifulSoupでHTML解析
Perl使いのPythonちゃん: PythonでGoogleの表示順位を取得
>>> from BeautifulSoup import BeautifulSoup
>>> import urllib2
>>> url = 'http://www.crummy.com/software/BeautifulSoup/documentation.html'
>>> html = urllib2.urlopen(url).read()
>>> soup = BeautifulSoup(html)
>>> soup('h1')
[<h1>Beautiful Soup Documentation</h1>]
特定のタグを抽出したいときには、上のリンク先の例にあるようにfindAll()を使うこともできる。抽出した結果には抽出タグ自身も含まれている。これを取り除くにはrenderContents()を使う。
>>> for s in soup('h1'):
... s.renderContents()
...
'Beautiful Soup Documentation'
それぞれのタグについて、条件をつけてさらに絞りこみたいときにはディクショナリが使える。アンカータグでクラスが "l" のものを絞りこみ、リンク先URLとアンカーテキストからなるタプルのリストを返すには次のように書く。 (Googleの検索結果表示画面で、外部リンク先へのアンカータグのクラスは "l"。したがって、Googleの検索結果をsoupに入れておけば、URLとタイトルの組からなるリストができる)
[(_a.get('href'), _a.renderContents()) for _a in soup('a', {'class' : 'l'})]
renderContents()は、子要素のタグも含めて文字列にする。アンカータグ内で b タグなんかが使われていると、「***<b>***</b>」のような文字列が返ってきてしまう。
BeautifulSoupによってHTMLが木構造に分解されたとき、それぞれの要素は自分の子要素(タグで囲まれた部分)をcontentsというリストとして持っているようだ。
>>> ex = BeautifulSoup('<a>This is <b>Example.</b></a>')
>>> for e in ex('a'):
... print e.contents
...
[u'This is ', <b>Example.</b>]
ここで、contentsの長さが1のとき、つまりタグが入れ子を含まないとき、タグが囲む文字列を"string"という識別子で取得できる。タグが複数の子要素を持つとき、stringはNoneとなっている。
>>> ex = BeautifulSoup('<a>This is <b>Example.</b></a><a>text</a>')
>>> for e in ex('a'):
... print e.string
...
None
text
これを使って、HTMLのタグを取り除く関数が書ける。
import re, urllib2
from BeautifulSoup import BeautifulSoup
def getPlainText(soup):
text = ''.join([ s.string if s.string else getPlainText(s) for s in soup ])
ctug = re.compile('<!--.*?-->',re.DOTALL)
text = ctug.sub('', x)
return text
url = 'http://www.crummy.com/software/BeautifulSoup/documentation.html'
html = urllib2.urlopen(url).read()
soup = BeautifulSoup(html)
print getPlainText(soup)
改行のみの行を取り除きたいときは、joinの前でfilter()を使うと簡単。ctugは「<!-- -->」のようなコメントを取り除くためのもの。re.DOTALLは正規表現の「. (ドット)」で改行も含めてマッチさせるのに必要。しかし、これだとscriptタグで囲まれた部分(javascriptとか)は取り除かれずに返ってきてしまう。document.write()をどう扱うべきか、、、。
''.join(filter(lambda(x): x != '\n' ,[ s.string if s.string else getPlainText(s) for s in soup ]))
http://www.panopticon.jp/mt/mt-tb.cgi/97
コメントする