Skip to content
This repository has been archived by the owner on Jan 8, 2019. It is now read-only.

Latest commit

 

History

History
339 lines (215 loc) · 15.3 KB

3-2.rst

File metadata and controls

339 lines (215 loc) · 15.3 KB

3部: WEBサイトの情報をPythonを使って抽出してみよう

準備

スクリプトを作成する前に、 codes という名称の作業ディレクトリを作成しましょう。

以後の作業は、この作業ディレクトリ内で行います。

また、今回は以下のWEBサイトのページに対してスクレイピングを行いますので確認してみましょう。

WEBサイトのHTMLを表示してみよう

Requestsを使ってURLに対してリクエストを送り、WEBサイトのHTMLを取得しましょう。

RequestsでWEBサイトの情報を取得するには、 requests モジュールの get() 関数を用います。

また、 get() 関数が返すResponseオブジェクトの status_code 属性から、HTTPステータスコードが取得できます。 text 属性からはテキスト形式でHTMLが取得できます。

# show_articles_html.py

import requests

# リクエストを送るURLを書きます
url = 'http://docs.pyq.jp/_static/assets/pyq-cats/articles.html'

response = requests.get(url)
# HTTPレスポンスに適切な文字エンコーディングを設定します
response.encoding = response.apparent_encoding
# 取得したスタータスコードを表示します
print(response.status_code)
# 取得したHTMLを表示します
print(response.text)

codes ディレクトリ内に、 show_articles_html.py という名前で上記のファイルを作成してください。

すると下記のようなディレクトリ構成になります。

./
└ show_articles_html.py

では、このスクリプトを実行してみましょう。

$ python show_articles_html.py
200
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>PyQ Cats</title>
    <link rel="stylesheet" href="css/reset.css">
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ## 省略 ##
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  </body>
</html>

スクリプトを実行すると上記のようにHTTPステータスコードと指定したURLのHTMLが出力されます。

  • Requestsの注意点

    • Requests で日本語などのマルチバイト文字を含むようなHTMLを取得する場合、コンテンツの文字エンコーディングを適切に判別できずに、文字化けしてしまうことがあります。下記の一行を書くことで、文字エンコーディングを適切に設定してくれます。日本語を含むようなコンテンツを取得する時は毎回この処理を追加すると良いでしょう。
    response = requests.get(url)
    # HTTPレスポンスに適切な文字エンコーディングを設定する
    response.encoding = response.apparent_encoding
    
    • encoding 属性は、サーバーから返されるレスポンスの文字エンコーディングです。この文字エンコーディングにしたがって、コンテンツを変換してくれます。
    • apparent_encoding 属性はサーバーから返される 文字エンコーディング が不明な場合にコンテンツの中身をチェックした上で適切な文字エンコーディングを教えてくれます。これを respones.encoding にセットすることで、極力文字化けなどが起こらないようにコンテンツを取得できます。

WEB上のコンテンツをプログラムで扱う時はHTMLやHTTPなどのWEBの技術について知っておくと良いでしょう。

WEBの技術に関しては、 PyQのドキュメント WEBの技術ついて を参照してください。

Note

ここでの「オブジェクト」は、 コンピューター上に存在する「データ」と「データの使い方」をまとめた「モノ」と考えてください。

例えば、 Response オブジェクトは、HTTPステータスコードやHTTPレスポンスボディといったHTTPレスポンスの「データ」や、 「データ」であるHTTPレスポンスのデータをJSON形式で返す json() といった「データの使い方」がまとまっています。

Note

JSONとは?:JSONとはJavaScript Object Notationの略で、テキストベースのデータフォーマットです。簡潔に構造化されたデータを簡単に記述することができるため、人間が理解しやすいデータフォーマットとして扱われています

記事のURLを一覧で取得してみよう

WEBサイトのHTMLを表示する事ができたので、今度はこのHTMLの中から記事のタイトルとURL一覧を取得してみましょう

Requestsを使う事によってHTMLを取得できたので、今度はBeautifulSoupも使いHTMLの要素から取得したい要素を抽出します。

show_article_urls1.py という名前で以下のファイルを作成してください。

# show_article_urls1.py

import requests
from bs4 import BeautifulSoup

# リクエストを送るURLを書きます
url = 'http://docs.pyq.jp/_static/assets/pyq-cats/articles.html'

response = requests.get(url)
response.encoding = response.apparent_encoding
# Requestsで取得したHTMLをBeautifulSoupで扱う
soup = BeautifulSoup(response.text, 'html.parser')
# HTML内のaタグの要素を全て取得します
links = soup.find_all('a')
for link in links:
    print(link)

特定のタグの要素を検索するには、 find_all() 関数を使用します。

戻り値は bs4.element.ResultSet オブジェクトで、サンプルコードのようにfor 文と組み合わせることで、検索した結果を一件ずつ取得することができます

  • 参考: このオブジェクトはイテレータと呼ばれる、反復処理を行うためのオブジェクトです。イテレータについては こちら

上記の例では find_all() 関数の第1引数のname引数に a タグを指定して要素を取得しています。

では、このスクリプトを実行してみましょう。

python show_article_urls1.py
<a href="http://docs.pyq.jp/_static/assets/pyq-cats/articles.html">PyQ Cats</a>
<a href="http://docs.pyq.jp/_static/assets/pyq-cats/20170825.html">2017年8月25日 どこにいった?</a>
<a href="http://docs.pyq.jp/_static/assets/pyq-cats/20170715.html">2017年7月15日 ぐでーん</a>
<a href="http://docs.pyq.jp/_static/assets/pyq-cats/20170624.html">2017年6月24日 ハンモック</a>
<a href="http://docs.pyq.jp/_static/assets/pyq-cats/20170623.html">2017年6月23日 かまって欲しい</a>
<a href="http://docs.pyq.jp/_static/assets/pyq-cats/20170617.html">2017年6月17日 カーテンの向こうから</a>
<a href="http://docs.pyq.jp/_static/assets/pyq-cats/20170611.html">2017年6月11日 肉球パン</a>
<a href="http://docs.pyq.jp/_static/assets/pyq-cats/articles2.html">次のページ &gt;</a>

おや、これだと記事以外のURLが混ざっていたり、HTMLまで表示されていてわかりにくいですね。

{記事のタイトル名}:{URL} と表示されるように修正を行いましょう。

show_article_urls2.py という名前で以下のファイルを作成してください。

# show_article_urls2.py

import requests
from bs4 import BeautifulSoup

# リクエストを送るURLを書きます
url = 'http://docs.pyq.jp/_static/assets/pyq-cats/articles.html'

response = requests.get(url)
response.encoding = response.apparent_encoding
soup = BeautifulSoup(response.text, 'html.parser')
# HTMLのh2とCSSのclass属性を指定して記事の要素のみを取得します
articles = soup.find_all('h2', class_='article-title')
# 記事のタイトル名とURLを表示します
for article in articles:
    title = article.text.strip()
    link = article.a.attrs.get('href').strip()
    print('{}:{}'.format(title, link))

今度は h2 タグ とCSSのclass属性 article-title を指定してHTMLの記事の要素を取得する様に修正を行い、要素から記事のタイトル名とリンクのURLのみを出力するようにしてみました。

articles = soup.find_all('h2', class_='article-title') というコードは show_articles_html.py で出力したHTMLの要素から以下の記事タイトルの要素のみを取得しています。

<h2 class="article-title">  ← この部分をコードで指定しています。
  <a href="http://docs.pyq.jp/_static/assets/pyq-cats/20170825.html">2017年8月25日 どこにいった?</a>
</h2>

見つかった要素の属性は、 attrs 属性によって参照できます。 上記の例では、HTML内の a タグを見つけ、href の内容を表示しています。

それでは修正したスクリプトを実行してみましょう。

python show_article_urls2.py
2017年8月25日 どこにいった?:http://docs.pyq.jp/_static/assets/pyq-cats/20170825.html
2017年7月15日 ぐでーん:http://docs.pyq.jp/_static/assets/pyq-cats/20170715.html
2017年6月24日 ハンモック:http://docs.pyq.jp/_static/assets/pyq-cats/20170624.html
2017年6月23日 かまって欲しい:http://docs.pyq.jp/_static/assets/pyq-cats/20170623.html
2017年6月17日 カーテンの向こうから:http://docs.pyq.jp/_static/assets/pyq-cats/20170617.html
2017年6月11日 肉球パン:http://docs.pyq.jp/_static/assets/pyq-cats/20170611.html

無事 {記事のタイトル名}:{URL} という期待する結果で表示する事ができました!

find_all()関数の class キーワードは Python の予約語のため、class 属性値を検索するには、class_ というキーワードを使用することに注意してください。

Note

予約語とは?: プログラミング言語において字句的には識別子(変数名、関数名など)としてのルールを満たしているにもかかわらず、識別子として使えない字句要素です。

Pythonでは以下のコードで予約語の一覧を確認する事ができます。

print(__import__('keyword').kwlist)

小休止

これでRequestsとBeautifulSoupを使ったWEBスクレイピングの第一歩は終了です。 以下のような便利なプログラムを作ってみましょう。

  • WEBサイトからネコ画像をダウンロード

WEBサイトからネコ画像をダウンロード

WEBサイトから画像をダウンロードするスクリプトの作成にチャレンジしましょう。

説明

「PyQ Cats」の記事一覧ページにある記事リンクをたどると、各記事内にネコ画像があります。

記事一覧から各記事を辿って、記事内のネコ画像をダウンロードするプログラムを作成してみましょう。

Warning

連続して同じサイトにアクセスする時はアクセスする間隔を最低1秒以上は空けましょう。

アクセスする間隔を空けるのは対象となるサイトに迷惑をかけないためです。

今回の課題のコードでは以下の様に標準モジュールのtime.sleepを利用して1秒間隔を空けましょう。

import time

# 1秒プログラムを停止します。引数は停止させる秒数です
time.sleep(1)

自分のPCでスクレピングを試す時はスクレイピング、クローリングする時の注意点 をよく読んでから試すようにしましょう。

要件

  • 記事一覧は複数ページありますが、TOPページに表示される記事のみでよいです。
    • 「次のページへ」でリンクを辿る必要はありません。
  • 画像は images という名前のディレクトリに保存するようにして下さい。
    • 保存時に images というディレクトリがなければ自動的に作成するようにして下さい。
  • 保存するファイル名は、URLのファイル名をそのまま使って下さい。

作成のステップ

どこから手を付けてよいかよく分からない場合は、以下のような処理の流れで考えてみましょう。

  1. 記事一覧ページから各記事のURLを取得する
  2. 各記事から画像URLを取得する
  3. 画像URLから画像をダウンロードする

ヒント

  • URLの解析には urllib.parse モジュールの urlparse() を使います。
  • ディレクトリの存在確認には os.path モジュールの exists() を使います。
  • ディレクトリの作成には os モジュールの makedirs() を使います。
  • 画像のダウンロードには open(ファイル名, "wb") でファイルオブジェクトを取得し、 write() で保存します。
  • 記事によっては画像が複数あります。画像がいくつあっても画像URLを取得できるようにセレクタを考えましょう。

発展課題

1 画像のリンクにはalt属性があります。保存時のファイル名を {記事の日付}_{alt属性の値}.{画像の拡張子} で保存するようにしてみましょう。

  • 例: 20170624_ハンモック.jpg
  • {記事の日付} はURL等を解析して取得してみましょう。
  • {画像の拡張子} はURL等を解析して取得してみましょう。例: hoge.jpg -> 拡張子はjpg, foo.png -> 拡張子はpng

2 TOPページだけでなく、2ページ以降のネコ画像もダウンロードするようにしてみましょう。

回答

課題の回答コードは、 codes/3-2/ ディレクトリに収められています。

リンク