書籍概要

Pythonクローリング&スクレイピング
―データ収集・解析のための実践開発ガイド―

著者
発売日
更新日

概要

Pythonによるクローリング・スクレイピングの入門から実践までを解説した書籍です。基本的なクローリングやAPIを活用したデータ収集,HTMLやXMLの解析から,データ取得後の分析や機械学習前の処理まで解説。データの収集・解析,活用がしっかりと基本から学べます。Webサービスの開発やデータサイエンスや機械学習分野で実用したい人はもちろん,基礎から解説しているのでPython初心者でもつまずかずに学習できます。多数のライブラリ,強力なフレームワークを活用して高効率に開発できます。

こんな方におすすめ

  • 業務や学術研究でクローラーを作りたい人
  • Pythonの一歩先の活用方法に興味のある人

サンプル

目次

1. クローリング・スクレイピングとは何か

  • 1. 本書が取り扱う領域
  • 2. Wgetによるクローリング
  • 3. Unixコマンドによるスクレイピング
  • 4. gihyo.jpのスクレイピング
  • 5. まとめ

2. Pythonではじめるクローリング・スクレイピング

  • 1. Pythonを使うメリット
  • 2. Pythonのインストールと実行
  • 3. Pythonの基礎知識
  • 4. Webページを取得する
  • 5. Webページからデータを抜き出す
  • 6. データを保存する
  • 7. Pythonによるスクレイピングの流れ
  • 8. まとめ

3. 強力なライブラリの活用

  • 1. ライブラリのインストール
  • 2. Webページを簡単に取得する
  • 3. HTMLのスクレイピング
  • 4. RSSのスクレイピング
  • 5. データベースに保存する
  • 6. クローラーとURL
  • 7. Pythonによるクローラーの作成
  • 8. まとめ

4. 実用のためのメソッド

  • 1. クローラーの分類
  • 2. クローラー作成にあたっての注意
  • 3. 繰り返しの実行を前提とした設計
  • 4. クロール先の変化に対応する
  • 5. まとめ

5. クローリング・スクレイピングの実践とデータの活用

  • 1. データセットの取得と活用
  • 2. APIによるデータの収集と活用
  • 3. 時系列データの収集と活用
  • 4. オープンデータの収集と活用
  • 5. Webページの自動操作
  • 6. JavaScriptを使ったページのスクレイピング
  • 7. 取得したデータの活用
  • 8. まとめ

6. フレームワーク Scrapy

  • 1. Scrapyの概要
  • 2. Spiderの作成と実行
  • 3. 実践的なクローリング
  • 4. 抜き出したデータの処理
  • 5. Scrapyの設定
  • 6. Scrapyの拡張
  • 7. クローリングによるデータの収集と活用
  • 8. 画像の収集と活用
  • 9. まとめ

7. クローラーの継続的な運用・管理

  • 1. クローラーをサーバーで動かす
  • 2. クローラーの定期的な実行
  • 3. クローリングとスクレイピングの分離
  • 4. クローリングの高速化・非同期化
  • 5. クラウドを活用する
  • 6. まとめ

サポート

ダウンロード

(2019年3月7日更新)

サンプルファイルのダウンロード

本書のサンプルファイルをご利用いただけます。
サンプルファイルの利用方法についてはzipファイル内のREADME.txtを参照してください。

ダウンロード
sample.zip

補足情報

(2019年10月1日更新)

Webサイトの変更等により動作しなくなった箇所の修正情報を一部掲載します。
著者のブログにていくつかフォローしている箇所もあるので,こちらもご覧ください。

P.279 broad.py

(2019年7月9日更新)

Webサイトの変更に伴い動作しなくなっていました。


import scrapy

from myproject.items import Page
from myproject.utils import get_content


class BroadSpider(scrapy.Spider):
    name = 'broad'
    # はてなブックマークの新着エントリーページ。
    start_urls = ['http://b.hatena.ne.jp/entrylist/all']

    def parse(self, response):
        """
        はてなブックマークの新着エントリーページをパースする。
        """
        # 個別のWebページへのリンクをたどる。
        for url in response.css('.entrylist-contents-title > a::attr("href")').getall():
            # parse_page() メソッドをコールバック関数として指定する。
            yield scrapy.Request(url, callback=self.parse_page)

        # page=の値が1桁である間のみ「次の20件」のリンクをたどる(最大9ページ目まで)。
        url_more = response.css('.entrylist-readmore > a::attr("href")').re_first(r'.*\?page=\d{1}$')
        if url_more:
            yield response.follow(url_more)

    def parse_page(self, response):
        """
        個別のWebページをパースする。
        """
        # utils.pyに定義したget_content()関数でタイトルと本文を抽出する。
        title, content = get_content(response.text)
        # Pageオブジェクトを作成してyieldする。
        yield Page(url=response.url, title=title, content=content)

P.273 サイトの変更に伴う入力内容の変更

(2019年2月22日更新)

サイト内容に伴い,住所,スコアの取得が動作しなくなっていました。
下記のようにそれぞれ変更してください。

変更前


>>> response.css('.rstinfo-table__address').xpath('string()').extract_first().strip()

変更後

>>> response.css('[rel="v:rating"] span::text').extract_first()

P.189 P.190 Amazon.co.jpの注文履歴

(2019年2月7日更新)

Amazon.co.jpの変更に伴い,書籍中のrobobrowserを用いたコードは動作しなくなっています。
もとのコードを参考に5.6などで解説しているSeleniumを利用してください。

P.81 SQL文のMySQL 8.0対応

(2018年10月31日更新)

MySQL 8.0以降では rank が予約語になったため,SQL文中で rank という名前を使用している箇所をバッククォートで囲う必要があります。

具体的にはP. 81のリスト3.4 save_mysql.pyの14行目を次のように変更します。

c.execute('''
    CREATE TABLE cities (
        `rank` integer,
        city text,
        population integer
    )
''')

P.136 Twitter API利用時にCallback URLを使わない方法

書籍中で紹介した当時とはAPIの認証画面で一部変更があります。引き続きCallback URLナシでも利用できるのでその方法を補足します。

Callback URLは「Sign in with
Twitter」(ユーザーにTwitterアカウントでログインしてもらう機能)を有効にする場合のみ必須です。APIによるツイートデータの収集という観点では「Sign
in with Twitter」は不要です。

従来のアプリページ (https://apps.twitter.com) では「Allow this application to be
used to Sign in with Twitter」というチェックボックスが,新しい開発者ポータル
(https://developer.twitter.com/) では「Enable Sign in with
Twitter」というチェックボックスがそれぞれのアプリのページにあるので,このチェックを外せばCallback URLは求められません。

参考: Action REQUIRED - Sign in with Twitter users must whitelist
callback URLs - Announcements - Twitter Developers
https://twittercommunity.com/t/action-required-sign-in-with-twitter-users-must-whitelist-callback-urls/105342

P.206 コラム「JSONに対してクエリを実践するjqコマンド」で利用したAPI

APIに変更がありました。下記のURL(https://map.yahooapis.jp/...)を利用してください。

なお,Yahoo! ID連携 v2は書籍中でアプリケーションIDと呼んでいた部分がClient IDに変更されています。読み替えてご利用ください。

P.228でScrapyでコンテンツを取得できない

(2018年10月2日更新)

書籍発行時とblog.scrapinghub.comの動作が変わったため,正常に動作しなくなってしまっています。
弊社Webサイトに現在のscrapinghub.comでも動作するよう改善したサンプルファイルをアップロードしたのでこちらをご確認ください。

P.240でresponse.css()などが動作しない

(2018年10月2日更新)

書籍で使用していたトピックスの記事は掲載から時間が経って消えています。代わりにP.236(6.3.4)のScrapyShellを起動する箇所で,Yahoo!ニュースのトップページに掲載されているお好みのトピックスのURLを使用して起動してください。


(scraping) $ scrapy shell https://news.yahoo.co.jp/pickup/XXXXXXX

P.192 PhantomJSの利用時の警告

(以下2018年5月8日更新)

Selenium 3.8.1以降でPhantomJSを使うと以下のWarningが表示されますが,2018-05-06時点で最新のSelenium 3.11.0でもPhantomJSは使用でき,書籍に記載のコードも問題なく動作します(ただしPhantomJSは最新版を使用して下さい)。

UserWarning: Selenium support for PhantomJS has been deprecated, please
use headless versions of Chrome or Firefox instead

なお,このWarningに従ってChromeやFirefoxのヘッドレスバージョンを使う方法は,以下の記事を参考にしてください。

SeleniumからStableになったHeadless Chrome/Firefoxを使ってみる - Qiita
https://qiita.com/orangain/items/6a166a65f5546df72a9d


(以下2017年11月9日更新)

P.275 サイトの変更に伴う入力内容の変更

サイト内容の変更に伴いコードが動作しなくなりました。
tabelog.pyの38行目(addressの部分)を下記のように変更してください。


address=response.css('.rstinfo-table__address').xpath('string()').extract_first().strip(),



(以下2017年6月26日更新)

P.162 ファイル内容の変更に伴う入力内容の変更

jgbcm_all.csvの表記ルールの変更に伴い,下記のようにdf_jgbcmの定義を変更する必要があります。

1行目の追加内容を無視しています。


>>> df_jgbcm = pd.read_csv('jgbcm_all.csv', encoding='cp932',  index_col=0, parse_dates=True, date_parser=parse_japanese_date,  na_values=['-'], header=1)

P.169とP.170 ファイル内容の変更に伴うリストの変更

jgbcm_all.csvの表記ルールの変更に伴い,下記のようにP.169のdf_jgbcmの定義を変更する必要があります。

1行目の追加内容を無視しています。


df_jgbcm = pd.read_csv( 'jgbcm_all.csv', encoding='cp932', index_col=0,  parse_dates=True, date_parser=parse_japanese_date, na_values=['-'],  header=1)


あわせて,P.170でjgbcm_all.csvを出力する個所も変更の必要があります。


plt.plot(df_jgbcm.index, df_jgbcm['1年'], label='1年国債金利')  
plt.plot(df_jgbcm.index, df_jgbcm['5年'], label='5年国債金利')  
plt.plot(df_jgbcm.index, df_jgbcm['10年'], label='10年国債金利')




(以下,2017年6月19日更新)

P.43, 2.4以降のgihyo.jp/dpにアクセスする操作が動作しない

gihyo.jp側の変更によりurllibによって「https://gihyo.jp/dp」にアクセスできなくなりました。
書籍中で案内している「https://gihyo.jp/dp」のURLをすべて,サンプルサイトの「http://sample.scraping-book.com/dp」に変更してください。


 >>> from urllib.request import urlopen
 >>> f = urlopen('http://sample.scraping-book.com/dp')



(以下,2017年5月22日更新)

P.168 P.169, macOS Sierraのフォント指定

macOS Sierraでは,matplotlibから使用できる.ttf形式の日本語フォントファイルがなくなってしまったため,以下の手順でMigMix 1Pフォントをインストールしてください。

  1. http://mix-mplus-ipa.osdn.jp/migmix/ から migmix-1p-20150712.zip をダウンロード・展開する。
  2. ZIPファイルに含まれているmigmix-1p-regular.ttfを~/Library/Fontsにコピーする。
  3. 以下のコマンドでmatplotlibのフォントのキャッシュファイルを削除する。※ P.166 脚注39のパスはLinuxのもので,macOSでは異なるので注意してください。
  4. 
    rm ~/.matplotlib/fontList.py3k.cache
    
    


(以下,2017年4月6日更新)

P.127, 5.1 データセットの取得と活用

P.127でダウンロードするWikipedia日本語版のデータセット(記事ページの最新版のダンプ)は定期的に更新され,一定以上古いものは削除されるため,書籍に記載のURLではダウンロードできなくなっています。

最新のダンプファイル一覧のページ (https://dumps.wikimedia.org/jawiki/latest/) から jawiki-latest-pages-articles1.xml-<任意の文字列>.bz2 というファイルを探し,そのURLをwgetの引数に指定してダウンロードしてください。

2017年4月6日現在では以下のURLでダウンロード可能です。

https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles1.xml-p1p168815.bz2

P.192, 5.6.1 JavaScriptを使ったページへの対応方法

PhantomJS 1.9.8を使用した場合,リスト5.25(P.201)とリスト5.26(P.203)のスクリプトが正常に動作しなくなっています。
最新のPhantomJS 2.1.1を使用すると改善します。

OS XではHomebrewで最新版(2016-12-06時点で2.1.1)がインストールされます。
古いバージョンを使用中の場合は`brew upgrade phantomjs`で最新版をインストールできます。

Ubuntuではインストール手順で`1.9.8`となっている箇所をすべて`2.1.1`に置き換えて実行すると2.1.1をインストールできます。


$ wget  
https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
$ tar xvf phantomjs-2.1.1-linux-x86_64.tar.bz2 # bz2ファイルを解凍する
$ sudo cp phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/ #  
PATHの通ったところにバイナリをコピーする
$ sudo apt-get install -y libfontconfig1 fonts-migmix

バージョンを確認すると2.1.1となります。


$ phantomjs --version
2.1.1

P.198, 5.6.2 noteのおすすめコンテンツを取得する

ページ読み込みのタイミングによっては,リスト5.24(P.198),リスト5.25(P.201),リスト5.26(P.203)のスクリプトが失敗することがあります。

この場合,`driver.get()`の後,
`driver.find_elements_by_css_selector()`または
`driver.execute_script()`の前に`time.sleep(2)`でスリープを入れると成功します。

P.200の解説にあるように,通常のWebページでは`driver.get()`の実行時にonloadイベントの発生時点までブロックされます。一方,noteのようにSingle Page Applicationの場合,onloadイベント以降もAjaxによる通信が継続していることがあります。このため,スリープを入れてAjaxの通信が完了するのを待ちます。

本来はAjaxの通信が完了するまで待つべきですが,そのような待ち方を指定できないので,ここでは仮に2秒としています。


    print('Navigating...', file=sys.stderr)
    driver.get('https://note.mu/')  # noteのトップページを開く。
    assert 'note' in driver.title  # タイトルに'note'が含まれていることを確認する。
    time.sleep(2)  # 2秒間待つ。 ← この行を追加

リスト5.24(get_note_content.py)では,ファイル冒頭に`import time`も追加してください。

サンプルファイルは修正済みです。

P.228, 6.1.2 Spiderの実行

ScrapingHub社のブログの構造の変更により,リスト6.1の`myspider.py`は投稿を1件も取得できなくなっています。

ScrapyのWebサイトに掲載されているサンプルコードも変更されています。 新しいサンプルコードにコメントを追加すると以下のようになります。(サンプルファイルに`6-1/myspider_mod.py`として同梱)



import scrapy


class BlogSpider(scrapy.Spider):
     name = 'blogspider'  # Spiderの名前。
     # クロールを開始するURLのリスト。
     start_urls = ['https://blog.scrapinghub.com']

     def parse(self, response):
         """
         ページから投稿のタイトルをすべて抜き出し,ベージャーをたどる。
         """

         # ページから投稿のタイトルをすべて抜き出す。
         for title in response.css('h2.entry-title'):
             yield {'title': title.css('a ::text').extract_first()}

         # ページャーの次のページへのリンクを取得し,次のページがあれば 
たどる。
         # 次のページもparse()メソッドで処理する。
         next_page = response.css('div.prev-post > a  
::attr(href)').extract_first()
         if next_page:
             yield scrapy.Request(response.urljoin(next_page),  
callback=self.parse)

このSpiderは「一覧のみパターン」のSpiderで,まずブログのトップページから投稿のタイトルをすべて抜き出します。
続いてページ下部にある「OLDER POST」というリンクをたどって,次のページを取得します。
そこからも同様に投稿のタイトルをすべて抜き出し,さらに次のページへと再帰的に繰り返します。
後のページまでたどって「OLDER POST」というリンクがなくなったら終了です。

リンクをたどる流れを図で表すと次のようになります。

sample.png

実行結果は書籍内のものと変わりません。

正誤表

本書の以下の部分に誤りがありました。ここに訂正するとともに,ご迷惑をおかけしたことを深くお詫び申し上げます。

(2018年5月21日最終更新)

P.80 MySQLの操作


# ユーザーscraperにデータベースscrapingを読み書き可能な権限を与える。
mysql> GRANT ALL ON scraping.* TO scraper;


# ユーザーscraperにデータベースscrapingを読み書き可能な権限を与える。
mysql> GRANT ALL ON scraping.* TO scraper@localhost;

MySQL 5.6では動作していましたが,正しくないコードのためMySQL 5.7以降で動作しませんでした。


(以下2018年4月19日更新)

P.316のcolumn AWS利用におけるセキュリティの注意点

他要素認証
要素認証

(以下,2017年6月15日更新)

P.50の脚注15(*15)

後この例では Google Chrome の〜
この例では Google Chrome の〜

(以下,2017年5月22日更新)

P.94, メソッド名の誤記


make=links_absolute()


make_links_absolute()

P.168 P.169, フォント指定の漏れ

リスト5.11 (P. 168) およびリスト5.12 (P. 169)でフォント指定が誤っていました。Osakaを追加するとEl Capitanで日本語が正常に表示されるようになります。

matplotlib.rcParams['font.sans-serif'] = 'Hiragino Kaku Gothic Pro,  
MigMix 1P'
matplotlib.rcParams['font.sans-serif'] = 'Hiragino Kaku Gothic Pro,  
Osaka, MigMix 1P'


(以下,2017年2月6日更新)

目次 「A.1.2 Vagrantとは」の対応ページ(第2刷以降修正済み)

362
361

P.68 表3.1「直下のテキストが"概要"というテキストであるh2要素」のXPath(第2刷以降修正済み)

//h2[text()="概要")]
//h2[text()="概要"]

2つ目の「)」が不要です。

P.140 脚注番号21のURL(第2刷以降修正済み)

https://dev.twitter.com/overview/api/twitter-libraries
https://dev.twitter.com/resources/twitter-libraries

P.149 ソースコード中のインストールに関するコメント


# pip install google-api-python-cliet


# pip install google-api-python-client

商品一覧