!!!Beautiful Soup (HTML XML解析) [Python] *http://www.crummy.com/software/BeautifulSoup/ *Document **http://www.crummy.com/software/BeautifulSoup/documentation.html **http://tdoc.info/beautifulsoup/ 日本語 !!!インストール !!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 *以下の様なパーサーがあります **Python 標準ライブラリのパーサー **lxml HTMLパーサー **lxml XMLパーサー **html5lib !!!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("data") *URLを指定して解析 import urllib2 from BeautifulSoup import BeautifulSoup soup = BeautifulSoup(urllib2.urlopen('http://xxxxx.com')) !!!オブジェクト *Beautiful Soup は複雑なHTML文書を、Python オブジェクトのツリーに変換する *以下の4種類のオブジェクトを扱うだけでよい !!Tag *XML、HTMLのタグに一致する 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('

') css_soup.p['class'] # ["body", "strikeout"] *XMLでは、複数値として扱わない xml_soup = BeautifulSoup('

', 'xml') xml_soup.p['class'] # u'body strikeout' !!NavigableString *文字列は、Beautiful Soup で、NavigableStringを利用する *Python のUnicode文字列とほぼ同じ *treeをナビゲートしたり検索したりする機能がサポートされている tag.string # u'Extremely bold' type(tag.string) # *unicode() で、Unicode 文字列に変換できる unicode_string = unicode(tag.string) unicode_string # u'Extremely bold' type(unicode_string) # !!BeautifulSoup *文書全体を表す *Tagオブジェクトとして文書を扱う *.name は、特別に、"[document]"となる !!!コメントと特殊な文字列 !!コメント *Commentオブジェクトは、NavigableStringの特殊型 markup = "" soup = BeautifulSoup(markup) comment = soup.b.string type(comment) # !!!treeのナビゲート !!下がる !タグ名を使ったナビゲート soup.head soup.title soup.find_all('a') !.contents と .children head_tag.contents [The Dormouse's story] for child in title_tag.children: print(child) !.descendants *.contents および .children は、タグの直接の子供しか考慮していない *ネストした子供を順次取り出す for child in head_tag.descendants: print(child) # The Dormouse's story # 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 # The Dormouse's story title_tag.parent # The Dormouse's story !.paretns link = soup.a link # Elsie 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 # text2 sibling_soup.c.previous_sibling # text1 !.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) # [The Dormouse's story] soup.find('title') # The Dormouse's story ::CSSクラスで検索 soup.find("b", { "class" : "lime" }) # Lime !find_parents() と find_parent() !find_next_siblings() と find_next_sibling() *あるオブジェクトのnextSiblingメンバー変数を辿り、指定したTagあるいはNavigableTextを集めてきます。 paraText = soup.find(text='This is paragraph ') paraText.findNextSiblings('b') # [one] 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("Foo") soup.a.append("Bar") !!BeautifulSoup.new_string() と .new_tag() !new_string() soup = BeautifulSoup("") tag = soup.b tag.append("Hello") new_string = soup.new_string(" there") tag.append(new_string) tag # Hello there. tag.contents # [u'Hello', u' there'] !new_tag() soup = BeautifulSoup("") original_tag = soup.b new_tag = soup.new_tag("a", href="http://www.example.com") original_tag.append(new_tag) original_tag # new_tag.string = "Link text." original_tag # Link text. !!insert() markup = 'I linked to example.com' soup = BeautifulSoup(markup) tag = soup.a tag.insert(1, "but did not endorse ") tag # I linked to but did not endorse example.com tag.contents # [u'I linked to ', u'but did not endorse', example.com] !!insert_before() と insert_after() !!clear() markup = 'I linked to example.com' soup = BeautifulSoup(markup) tag = soup.a tag.clear() tag # !!extract() *タグもしくは文字列をツリーから削除 markup = 'I linked to example.com' soup = BeautifulSoup(markup) a_tag = soup.a i_tag = soup.i.extract() a_tag # I linked to i_tag # example.com !!decompose() *タグをツリーから取り除く markup = 'I linked to example.com' soup = BeautifulSoup(markup) a_tag = soup.a soup.i.decompose() a_tag # I linked to !!replace_with() *タグおよび文字列をツリーから取り除き、別のタグおよび文字列に置き換える !!wrap() *要素をタグでラップする soup = BeautifulSoup("

I wish I was bold.

") soup.p.string.wrap(soup.new_tag("b")) # I wish I was bold. !!unwrap() *タグをはがす markup = 'I linked to example.com' soup = BeautifulSoup(markup) a_tag = soup.a a_tag.i.unwrap() a_tag # I linked to example.com !!!出力 !!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']