塩見周子の徒然日記

自分のことを塩見周子と思い込んでいるオタクです

Pythonでスクレイピング 1-2/BeautifulSoup

1-2

Pythonスクレイピング(HTMLやXMLから情報を抽出)をするときの便利なライブラリにBeautifulSoup(綺麗なスープ!!!!???????!?!?!!??!!??!?!)がある。
※「データ抽出」のみの機能であり、ダウンロードの機能はないので、そこはurllibを使って自分で組む必要がある。

BeautifulSoup使い方その1(HTMLの文字列からデータを取得する)

ア:HTMLから情報を抽出(基本ver)

from bs4 import BeautifulSoup

#解析したいHTMLの例
html = '''
<html><body>
  <h1>スクレイピングとは?</h1>
  <p>Webページを解析すること。</p>
  <p>任意の情報を抽出すること。</p>
</body><html>
'''

#HTMLの解析。第一引数にhtml(上の文章)、第二引数に文を解析するもの(パーサー)を入れる。今回はHTMLなのでhtml.parserを入れる
soup = BeautifulSoup(html, 'html.parser')

#HTMLと同じようにアクセスできる。例えば、h1は<html><body><h1>にある要素なので、それをドットでつないでアクセス
h1 = soup.html.body.h1
p1 = soup.html.body.p
p2 = p1.next_sibling.next_sibling 
#pタグは二つあり、後ろの方(「任意の情報を抽出すること。」の一文)にアクセスするには「p1の次」というアクセスの仕方をする。1つ目のnext_siblingはp1直後の改行、スペースを得る。

#出力
print('h1 = ' + h1.string)
print('p = ' + p1.string)
print('p = ' + p2.string)

ア':find()を使って、任意のidで探す(アのように、ルートから一つずつ辿るのは面倒なので)

from bs4 import BeautifulSoup

html = '''
<html><body>
  <h1 id = 'title'>スクレイピングとは?</h1>
  <p id = 'body'>ページから任意のデータを抽出すること。</p>
</body></html>
'''

soup = BeautifulSoup(html, 'html.parser')

title = soup.find(id = 'title')
body = soup.find(id = 'body')

print('#title=' + title.string)
print('#body=' + body.string)

ア'':find_all()を使って複数要素を取得

from bs4 import BeautifulSoup

#ulは順序のないリストを指す。a hrefはリンクを指すタグ。
html = '''
<html><body>
    <ul>
        <li><a href='http://uta.pw'>uta</a></li>
        <li><a href='http://oto.chu.jp'>oto</a></li>
    </ul>
</body></html>
'''

soup = BeautifulSoup(html, 'html.parser')

#flnd_all()メソッドでaタグ(リンク)を全て取り出す
links = soup.find_all('a')

#リンク一覧を表示。href属性(リンクの部分)はattrs['href']のようなattrsプロパティで取得できる。textはstringから。
for a in links:
    href = a.attrs['href']
    text = a.string
    print(text, '>', href)

BeautifulSoup使い方その2(urlopen()を使ってリンク先を持ってくる)

郵便番号検索APIにアクセスして、郵便番号の「県」「市」「町」を表示させる

from bs4 import BeautifulSoup
import urllib.request as req

#ここの数字の部分を変えれば任意の住所が検索できる
url = 'http://api.aoikujira.com/zip/xml/1500042'

#urlopenでデータ取得
res = req.urlopen(url)

#データ解析(なんでXML読んでんのにパーサーがhtmlなのかは分からん)
soup = BeautifulSoup(res, 'html.parser')

#soup(XMLとして読んだ情報)の中から、find('ken')なら<ken>東京都</ken>となっているところを文字列として取り出す
ken = soup.find('ken').string
shi = soup.find('shi').string
cho = soup.find('cho').string
print(ken,shi,cho)

BeautifulSoupの使い方その3(CSSセレクタを使う)

CSS(Cascading Style Sheets:要はHTMLみたいなのでできた文章のレイアウトを指定するヤツ)を指定して、その要素を抽出するみたいなことを行う。

from bs4 import BeautifulSoup

#divはその部分をくくってひとまとめのグループにする意味。ulは順序のないリスト。
html = '''
<html><body>
<div id='meigen'>
    <h1>トルストイの名言</h1>
    <ul class='items'>
        <li>汝の心に教えよ、心に学ぶな。</li>
        <li>謙虚な人は誰からも好かれる。</li>
        <li>強い人々は、いつも気取らない。</li>
    </ul>
</div>
</body></html>
'''

soup = BeautifulSoup(html, 'html.parser')

#select_one(セレクタ)でCSSセレクタで要素を一つ取り出す。div#meigen > h1はHTMLの木構造をイメージ。「meigenっていう名前のついたdivタグの中にあるh1」みたいな
h1 = soup.select_one('div#meigen > h1').string
print('h1 =', h1)

#リスト部分を取得。select(セレクタ)でCSSセレクタで複数要素取り出し、リスト型で返す。
li_list = soup.select('div#meigen > ul.items > li')
for li in li_list:
    print('li =', li.string)

まとめ

Yahoo!ファイナンスの為替情報(USD/JPY)をスクレイピングしてみる。

from bs4 import BeautifulSoup
import urllib.request as req

url = 'https://stocks.finance.yahoo.co.jp/stocks/detail/?code=USDJPY=X'
res = req.urlopen(url)

soup = BeautifulSoup(res, 'html.parser')

#なぜかわからないがここのstoksprice、stocks(株式)じゃなくてstoksになってる むずむずするわね
price = soup.select_one('.stoksPrice').string
print('usd/jpy=', price)

f:id:saguh:20190917021731p:plain
実行結果(+soupの中身)はこんな感じ。