目次
Beautiful Soup (HTML XML解析)
[Python]
インストール
PIPからインストール
# pip install BeautifulSoup
ダウンロードして解凍
piroto@eisai /cygdrive/c/Users/piroto/Downloads $ tar -xvf beautifulsoup4-4.1.2.tar.gz
インストール
$ cd beautifulsoup4-4.1.2 $ python setup.py install
パーサーのインストール
- Beautiful Soup は Pythonの標準ライブラリに含まれているHTML パーサーをサポートしています。
- その他にもサードパーティー製のパーサーもサポートしています。
- その一つが、lxmlパーサーで、以下の様にインストールできます。
$ apt-get install python-lxml $ easy_install lxml $ pip install lxml
Import
from BeautifulSoup import BeautifulSoup # For processing HTML from BeautifulSoup import BeautifulStoneSoup # For processing XML import BeautifulSoup # To get everything
No module named BeautifulSoup エラーとなる場合
from bs4 import BeautifulSoup # To get everything
解析
- 文字列およびファイルハンドルによる文書解析
soup = BeautifulSoup(open("index.html")) soup = BeautifulSoup("<html>data</html>")
- URLを指定して解析
import urllib2 from BeautifulSoup import BeautifulSoup soup = BeautifulSoup(urllib2.urlopen('http://xxxxx.com'))
オブジェクト
- Beautiful Soup は複雑なHTML文書を、Python オブジェクトのツリーに変換する
- 以下の4種類のオブジェクトを扱うだけでよい
Tag
tag = soup.b
Name
- すべてのタグは、.name でアクセスできる名前を持つ
- タグの名称を変更すると、内容も変更される
tag.name
Attributes
- タグはいくつかの属性をもつ。
- 以下の様にアクセス可能
tag['class']
- .attrs で直接アクセスできる
- 追加、削除、変更ができる
tag['class'] = 'verybold' tag['id'] = 1 del tag['class'] del tag['id']
複数値属性
- HTML4では、いくつかの属性で、複数の値を持つことができる
- もっとも知られているのが、class
- Beautiful Soupでは、リストとして扱う
css_soup = BeautifulSoup('<p class="body strikeout"></p>') css_soup.p['class'] # ["body", "strikeout"]
- XMLでは、複数値として扱わない
xml_soup = BeautifulSoup('<p class="body strikeout"></p>', 'xml') xml_soup.p['class'] # u'body strikeout'
NavigableString
- 文字列は、Beautiful Soup で、NavigableStringを利用する
- Python のUnicode文字列とほぼ同じ
- treeをナビゲートしたり検索したりする機能がサポートされている
tag.string # u'Extremely bold' type(tag.string) # <class 'bs4.element.NavigableString'>
- unicode() で、Unicode 文字列に変換できる
unicode_string = unicode(tag.string) unicode_string # u'Extremely bold' type(unicode_string) # <type 'unicode'>
BeautifulSoup
- 文書全体を表す
- Tagオブジェクトとして文書を扱う
- .name は、特別に、"[document]"となる
コメントと特殊な文字列
コメント
- Commentオブジェクトは、NavigableStringの特殊型
markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>" soup = BeautifulSoup(markup) comment = soup.b.string type(comment) # <class 'bs4.element.Comment'>
treeのナビゲート
下がる
タグ名を使ったナビゲート
soup.head soup.title soup.find_all('a')
.contents と .children
head_tag.contents [<title>The Dormouse's story</title>]
for child in title_tag.children: print(child)
.descendants
- .contents および .children は、タグの直接の子供しか考慮していない
- ネストした子供を順次取り出す
for child in head_tag.descendants: print(child) # <title>The Dormouse's story</title> # The Dormouse's story
.string
title_tag.string # u'The Dormouse's story'
.strings と stripped_strings
- strings
for string in soup.strings: print(repr(string)) # u"The Dormouse's story" # u'\n\n' # u"The Dormouse's story" # u'\n\n'
- stripped_strings
for string in soup.stripped_strings: print(repr(string)) # u"The Dormouse's story" # u"The Dormouse's story"
上がる
.parent
title_tag = soup.title title_tag # <title>The Dormouse's story</title> title_tag.parent # <head><title>The Dormouse's story</title></head>
.paretns
link = soup.a link # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a> for parent in link.parents: if parent is None: print(parent) else: print(parent.name) # p # body # html # [document] # None
横に移動
.next_sibling と .prebious_sibling
sibling_soup.b.next_sibling # <c>text2</c> sibling_soup.c.previous_sibling # <b>text1</b>
.next_siblings と .prebious_siblings
for sibling in soup.a.next_siblings: print(repr(sibling)) for sibling in soup.find(id="link3").previous_siblings: print(repr(sibling))
後ろ向き前向き
.next_element と .previous_element
.next_elements と .previous_elements
ツリーの検索
string
soup.find_all('b')
正規表現
import re for tag in soup.find_all(re.compile("^b")): print(tag.name) # body # b
リスト
soup.find_all(["a", "b"])
関数
findAll()
- タグ名を渡す
soup.findAll('b')
- 正規表現を使う
import re tagsStartingWithB = soup.findAll(re.compile('^b'))
- リストまたは辞書を渡す(結果は同一だが後者が高速)
soup.findAll(['title', 'p']) oup.findAll({'title' : True, 'p' : True})
- Trueを渡すとすべてのタグを返す
allTags = soup.findAll(True)
- 一つの引数をもち真偽値を返す呼び出し可能オブジェクトを渡すとフィルタできる
soup.findAll(lambda tag: len(tag.attrs) == 2)
find()
- 結果が一つしかないと分かっている場合など、最後までスキャンする必要はない利用
find(name, attrs, recursive, string, **kwargs)
- 以下はほぼ同等
soup.find_all('title', limit=1) # [<title>The Dormouse's story</title>] soup.find('title') # <title>The Dormouse's story</title>
- CSSクラスで検索
soup.find("b", { "class" : "lime" }) # <b class="lime">Lime</b>
find_parents() と find_parent()
find_next_siblings() と find_next_sibling()
- あるオブジェクトのnextSiblingメンバー変数を辿り、指定したTagあるいはNavigableTextを集めてきます。
paraText = soup.find(text='This is paragraph ') paraText.findNextSiblings('b') # [<b>one</b>] paraText.findNextSibling(text = lambda(text): len(text) == 1) # u'.'
find_previous_siblings() と find_previous_sibling()
find_all_next() と find_next()
find_all_previous() と find_previous()
CSS selectors
タグ
soup.select("title")
配下のタグ
soup.select("body a")
直下のタグ
soup.select("head > title")
CSS class
soup.select(".sister")
ID
soup.select("#link1") soup.select("a#link2")
属性
soup.select('a[href]')
属性値
soup.select('a[href="http://example.com/elsie"]')
ツリーの操作
タグ名と属性の変更
tag.name = "blockquote" tag['class'] = 'verybold' tag['id'] = 1 del tag['class'] del tag['id']
.stringの変更
tag.string = "New link text."
append()
soup = BeautifulSoup("<a>Foo</a>") soup.a.append("Bar")
BeautifulSoup.new_string() と .new_tag()
new_string()
soup = BeautifulSoup("<b></b>") tag = soup.b tag.append("Hello") new_string = soup.new_string(" there") tag.append(new_string) tag # <b>Hello there.</b> tag.contents # [u'Hello', u' there']
new_tag()
soup = BeautifulSoup("<b></b>") original_tag = soup.b new_tag = soup.new_tag("a", href="http://www.example.com") original_tag.append(new_tag) original_tag # <b><a href="http://www.example.com"></a></b>
new_tag.string = "Link text." original_tag # <b><a href="http://www.example.com">Link text.</a></b>
insert()
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup) tag = soup.a tag.insert(1, "but did not endorse ") tag # <a href="http://example.com/">I linked to but did not endorse <i>example.com</i></a> tag.contents # [u'I linked to ', u'but did not endorse', <i>example.com</i>]
insert_before() と insert_after()
clear()
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup) tag = soup.a tag.clear() tag # <a href="http://example.com/"></a>
extract()
- タグもしくは文字列をツリーから削除
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup) a_tag = soup.a i_tag = soup.i.extract() a_tag # <a href="http://example.com/">I linked to</a> i_tag # <i>example.com</i>
decompose()
- タグをツリーから取り除く
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup) a_tag = soup.a soup.i.decompose() a_tag # <a href="http://example.com/">I linked to</a>
replace_with()
- タグおよび文字列をツリーから取り除き、別のタグおよび文字列に置き換える
wrap()
- 要素をタグでラップする
soup = BeautifulSoup("<p>I wish I was bold.</p>") soup.p.string.wrap(soup.new_tag("b")) # <b>I wish I was bold.</b>
unwrap()
- タグをはがす
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup) a_tag = soup.a a_tag.i.unwrap() a_tag # <a href="http://example.com/">I linked to example.com</a>
出力
Pretty-printing
prettify()
Non-pretty printing
unicode() もしくは str()を使う
XML
from BeautifulSoup import BeautifulStoneSoup def get_translation(itemid): ''' ItemId から、訳語を取得する ''' url = r'http://btonic.est.co.jp/NetDic/NetDicV09.asmx/GetDicItemLite?Dic=EJdict&Item={0}&Loc=&Prof=XHTML' url = url.format(itemid) f = urllib2.urlopen(url) soup = BeautifulStoneSoup(f.read()) #print soup.prettify() try: ret = soup.body('div')[1].text except: ret = '' return ret
例
ページを解析し指定タグのテキストのみ取り出す
soup = BeautifulSoup(urllib2.urlopen(url)) for p in soup('p'): print p.text
タグの文字列を取得する
soup = BeautifulSoup(urllib2.urlopen(url)) ''.join(soup.findAll(text=True))
必要なタグのみ解析する
tags = SoupStrainer(['title','img','h1','h2','h3','h4','h5','h6','p','meta','link']) soup = BeautifulSoup(urllib2.urlopen(url), parseOnlyThese=tags)
タグに囲まれた文字列値がある場合出力
soup = BeautifulSoup(urllib2.urlopen(url)) if hasattr(soup, "title") and hasattr(soup.title, "string"): print soup.title.string
Image タグから リンクを抜き出す
soup = BeautifulSoup(urllib2.urlopen(url)) for tag in soup.findAll(): if tag.name == 'img': print tag['src']
YAGI Hiroto (piroto@a-net.email.ne.jp)
twitter http://twitter.com/pppiroto
Copyright© 矢木 浩人 All Rights Reserved.