naritoブログ

このブログはPython/DjangoとBootstrap4で作成されました
Githubのソースコード

Python、BeautifulSoup+requestsの基本

約742日前 2016年6月12日16:15
プログラミング関連
Requests BeautifulSoup Python
https://torina.top/detail/264/
こちらの、BeautifulSoupとRequestsバージョンです。


Seleniumでのブラウザの自動化と違い、プログラム上から直接HTTPリクエストを行う為、高速です。
それぞれのインストールは、以下のようにしましょう。
pip install requests
pip install beautifulsoup4



基本的な使い方として、例えば、このブログのタイトルを抽出したい場合は以下のようになります。
find_all(タグ名)とすることで、要素の一覧が取得できます。
from bs4 import BeautifulSoup
import requests

res = requests.get("https://torina.top/")
soup = BeautifulSoup(res.text)
h2s = soup.find_all("h2")  # 全ての<h2>...</h2>を取得
for h2 in h2s:
    print(h2.text)  # .textでテキストを取得


結果
Django、よく使うフィルタ
Python、Seleniumの基本
Pythonで、進捗バーを自作する
Djangoで、404ページを作る
Djangoでシンプルなアクセスカウンターもどき
Django、templateからよくincludeするhtml
Django、ブログに使えそうなModel
Django、管理画面へのリンク
Python、マルチスレッドを使い簡易チャット
ホームページ作成に役立つサイト(非デザイナー向け)



find_allは全て取得ですが、findによる見つかったものを1つ取得するメソッドもあります。
引数は柔軟で、例えばidによる検索では、id=id名のようなキーワード引数で指定します。
header = soup.find(id="header")


classによる検索はclass_=クラス名とします。
h2s = soup.find_all(class_="panel-title")



h2タグの中のpタグの中のspanタグ、というアクセスには単純に.タグ名でアクセスできます。具体的には、findを引数なしで呼び出す際はこちらを使えるでしょう。
target = soup.h2.p.span.text



以下は、hrefに「css」が含まれているものを抽出します。このような、ある属性にある文字列が含まれた要素を取得する、というケースは多いでしょう。
このre.compileという書き方は、他でも使えます。classに"item"の文字が含まれている物を抽出、等です。
また、取得した要素の属性は、['href'], ['class']のように辞書として格納されています。
import re
from bs4 import BeautifulSoup
import requests


res = requests.get("https://torina.top/")
soup = BeautifulSoup(res.text)
all_css = soup.find_all(href=re.compile("css"))
for css in all_css:
    print(css)
    print(css["href"])


結果
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet"/>
/static/bootstrap/css/bootstrap.min.css
<link href="/static/google-code-prettify/mypre.css" rel="stylesheet"/>
/static/google-code-prettify/mypre.css
<link href="/static/blog/css/blog.css" rel="stylesheet"/>
/static/blog/css/blog.css



data-urlのような、html5からの属性を元に絞り込みたい場合ですが、hrefやclassと違いキーワード引数として指定ができません。
そのため、少し冗長ですが、以下のように指定する必要があります。
twitter = soup.find(attrs={"data-text": "Django、よく使うフィルタ"})
print(twitter)
print(twitter["data-url"])



結果
<a class="twitter-share-button" data-text="Django、よく使うフィルタ" data-url="http://torina.top/detail/265" href="https://twitter.com/share">Tweet</a>
http://torina.top/detail/265



findとfind_all以外によく使うものとして、CSSセレクタでの検索があります。
CSSセレクタでの指定をするにはselectメソッドを使います。1つだけ欲しいならば、select_oneメソッドです。
h2s = soup.select("h2")  # 複数
h2s = soup.select_one("h2")  # 一つ


よく使われるCSSセレクタをほとんどサポートしています。
soup.select("body div")  # body以下のdiv全て
soup.select("body > div")  # body直下のdiv
soup.select("div.row")  # <div class="row" だけ
soup.select('div[class="spam ham"]')  # <div class="spam ham>"だけ。class="spam"やclass="ham"だけの要素は取得されず
oup.select('.red, .blue')  # class="red"、又はclass="blue"のもの。カンマで区切る



.textと.stringの違いも見ておきます。
from bs4 import BeautifulSoup


html = """
<td>some text</td>
<td></td>
<td><p>more text</p></td>
<td>even <p>more text</p></td>
"""
soup = BeautifulSoup(html, 'html.parser')
tds = soup.find_all('td')

print('-----以下string-----')
for td in tds:
    print(td.string)

print('-----以下text-----')
for td in tds:
    print(td.text)



結果。基本的にtextを使うほうが混乱は少ないです。
-----以下string-----
some text
None
more text
None
-----以下text-----
some text

more text
even more text




requestsについて、もう少し見ておきましょう。
.textではなく.contentを使うと、バイナリデータのレスポンス本文にアクセスができます。
res = requests.get("https://torina.top/")
res.content


.json()を使うことで、jsonデータをデコードできます。WebAPI利用の際にはお世話になるでしょう。
res.json()


https://torina.top/detail/161/
Web上のリソースをダウンロードする時なんかには、サーバーからの生のソケットレスポンスの全てを取得する.rawも使えます。
res = requests.get(url, stream=True)
with open(file_name, 'wb') as file:
    shutil.copyfileobj(res.raw, file)


HTTPヘッダーを変更したい場合、以下のようなことができます。
以下はユーザエージェントの設定です。
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
response = requests.get(url, headers=headers)



ログインしていないと見せないページがある場合、つまりセッションが絡む場合はsessionメソッドを利用することになります。
import requests

session = requests.session()
res = session.get('https://torina.top')



raise_for_status()は、200番台以外のレスポンス時に例外を送出してくれます。
import requests

session = requests.session()
res = session.get('http://httpbin.org/status/404')
res.raise_for_status()


ドキュメント
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
http://docs.python-requests.org/en/master/