LoginSignup
192

More than 5 years have passed since last update.

PythonでJavaScriptを使ったWebサイトをスクレイピングする

Last updated at Posted at 2015-10-29

概要

JavaScriptでDOMを作ってるサイトをPythonを使ってスクレイピングしたので、手順をメモ。

大雑把には、ScrapySeleniumを組み合わせてやった。

Scrapy

Scrapyは、クローラーを実装するためのフレームワーク。

クローラーをSpiderのサブクラス、スクレイピングした情報をItemのサブクラス、スクレイピングした情報に対する処理をPipelineのサブクラス、という風にフレームワークが決めたインターフェースを満たすクラスとしてクローラーを実装する。

scrapyというコマンドが提供されてて、このコマンドを使って、作ったクローラーの一覧を見たり、クローラーを起動したりできる。

Selenium

Seleniumは、ブラウザをプログラムから制御するためのツール(でいいのかな?)。Pythonも含めたいろんな言語で使える。
よくWebサイト/アプリの自動テスト文脈でよく出てくる。
これを使えば、JavaScript実行して、動的に生成されたDOMも含めたHTMLのソースをスクレイピングできる。

元はJavaScript経由でブラウザを制御してたみたいだけど、今はブラウザに直接メッセージを送ってブラウザを制御してくれる。

ぼくの環境(OSX)で試したら、Safari、Chromeだと拡張機能みたいなのをインストールしないと使えなかった。
Firefoxだとそのまま使える。
PhantomJSを入れれば、ウィンドウ無しでスクレイピングもできるので、サーバー上で使っても大丈夫。

なんでScrapyを使うのか

Scrapyを使わなくても、Seleniumだけでもスクレイピングはできるんだけど、

  • 並行処理で複数ページをスクレイピングしてくれる(マルチスレッド?プロセス?)、
  • 何ページクロールしたかとか、エラーは何回起こったかとか、ログをいい感じにまとめてくれる、
  • クロール対象ページの重複を回避してくれる、
  • クロールの間隔とか、いろんな設定オプションを提供してくれる、
  • CSS、XPathを組み合わせて、DOMから情報を抜ける(組み合わせが結構便利!)、
  • クロール結果を、JSONとかXMLで吐き出せる、
  • いい感じのプログラム設計でクローラーを書ける(下手に自分で設計するより良いと思う)、

など、思いつくScrapyを使うメリットはこんな感じ。

最初は、フレームワークの勉強が面倒くさくて、SeleniumとPyQueryだけでクローラーを実装してたけど、ログとかエラー処理とか書いてたら、車輪の再発明感が強くなってきてやめました。

実装前にScrapyのドキュメントを、最初からSettingsのページまでと、Architecture overviewDownloader Middlewareあたりを読みました。

ScrapyとSeleniumを組み合わせて使う

元ネタはこのstack overflow

Scrapyのアーキテクチャーはこんな感じ(Scrapyのドキュメントより)。

Scrapy architecture overview

Downloader Middlewaresを、カスタマイズして、ScrapyのSpiderがSeleniumを使ってスクレイピングするように調整する。

Donwloader Middlewareの実装

Downloader Middlewareは、process_requestを実装した普通のクラスとして実装する。

selenium_middleware.py
# -*- coding: utf-8 -*-
import os.path

from urlparse import urlparse

import arrow

from scrapy.http import HtmlResponse
from selenium.webdriver import Firefox


driver = Firefox()


class SeleniumMiddleware(object):

    def process_request(self, request, spider):

        driver.get(request.url)

        return HtmlResponse(driver.current_url,
            body = driver.page_source,
            encoding = 'utf-8',
            request = request)


def close_driver():
    driver.close()

このDownload Middlewareを登録すると、Spiderが、ページをスクレイピングする前に、process_requestを呼び出してくれる。
詳しくは、こちら

HtmlResponseのインスタンスを返してるんで、それ以降のDownload Middlewareは呼び出されない。
優先順位の設定によっては、デフォルトのDonwload Middleware(robots.txtを解析処理など)は呼び出されないので注意。

上では、Firefoxを使ってるけど、PhantomJSを使いたい時は、driver変数のとこを書き換える。

Download Middlewareの登録

使いたいSpiderクラスに、SeleniumMiddlewareを登録する。

some_spider.py
# -*- coding: utf-8 -*-
import scrapy

from ..selenium_middleware import close_driver


class SomeSpider(scrapy.Spider):
    name = "some_spider"
    allowed_domains = ["somedomain"]
    start_urls = (
        'http://somedomain/',
    )
    custom_settings = {
        "DOWNLOADER_MIDDLEWARES": {
            "some_crawler.selenium_middleware.SeleniumMiddleware": 0,
        },
        "DOWNLOAD_DELAY": 0.5,
    }


    def parse(self, response):
        # クローラーの処理

    def closed(self, reason):
        close_driver()

custom_settingsDOWNLOADER_MIDDLEWARESのとこが設定。
スクレイピングに使ったFirefoxのウィンドウを消すために、closeメソッドの処理を書いてる。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
192