【Python】scrapy を使ったスクレイピング

Python

株探より、日経平均やTOPIXなど指数の値を取得する例を通して、スクレイピングの始め方をまとめました!

取得のターゲットは、株探のこちらの赤枠の部分の値になります。

出典:株探

それでは行きましょう!

以下のステップで進めていきます。

  1. プロジェクトを作成する
  2. Spiderを作成する
  3. データの取得(スクレイピング)コードを記述

STEP1:プロジェクトを作成する

プロジェクトを作成するために、Pythonのscrapyでは、まずその前に、そのプロジェクトを実行するための環境を作る必要があります。

それでは仮想環境を作っていきましょう!

ANACONDAで仮想環境を構築する

ANACONDAを開く

するとこんな画面が表示されます。

ANACONDAが未インストールの場合は、こちらが参考になりますのでご参照ください。

サイドメニューから「Environments」を選択

ここで仮想環境を作ります。

使用するライブラリのverが異なるとscrapyが使えないことがあるため、仮想環境でPythonのverとScrapyのverを合わせます。

ANACONDAを使うことで、仮想環境を構築することができ、それぞれ個別のライブラリをインストールすることができます。

デフォルトでは、「base(root)」という環境が1つだけ構築されています。

新しく仮想環境を作る

左下の「Create」をクリック

今回は、「scrapy_stock」という名前で作成します。

PackagesのPythonの欄にチェックが入っていることも確認すること!

すると、環境が作成されます。

この仮想環境はHome画面からも確認がきます。

ここに表示されているものが、この仮想環境でインストールされているライブラリです。

terminalを開く

作成した仮想環境の名前の右側にある矢印をクリックすると「Open Terminal」が出てくるので、クリックしてterminalを起動します。

pip install

terminalを開いたら、ここで必要なライブラリをpip installします。

インストールしたいライブラリをテキストファイルにまとめて、それを読み込ませて一括で実行します。

今回インストールするライブラリの一覧はこちら

ipython==7.22.0
pylint==2.7.2
autopep8==1.5.6
scrapy==2.4.1
scrapy-selenium==0.0.7
pymongo==3.11.3
pillow==8.2.0
dnspython==1.16.0
shub

このテキストファイルをお好みのディレクトリに保存し、terminalでそのディレクトリに移動します。

ここでは、workspaceディレクトリ下のscrapyディレクトリに保存するとします。

ANACONDAでterminalを開いた状態のデフォルトでは、ルートディレクトリとなっているので、ディレクトリをscrapyまで移動します。

% cd workspace

% cd scrapy

ここで、pip installを実行します。

% pip install -r requirements.txt 

すると、インストールが走ります。

ANACOINDAに戻ります。

すると、scrapy等のライブラリがインストールされていることが確認できます。

これでやっと開発環境を構築できました。

ANACONDAの仮想環境にVScodeのpathを通す(macのみ必要)

Python実行時に、ちゃんと作成した仮想環境が実行されるよう、macの場合はpathを通す必要があります。

これまでの手順より、ANACONDAでターミナルを開き、以下コマンドを実行します。

% conda info -e

これにより仮想環境のパスを確認することができます。

VScodeを開き、設定します。

設定画面を開きます。

「Search settings」の欄に「python.defaultInterpreterPath」と入力し検索します。

ここに、先程ターミナルで確認した以下のようなパスを貼り付けます。

/Users/[username]/opt/anaconda3/envs/scrapy_stock

これで準備はOKです!VScodeとANACONDAの仮想環境の連携ができました。

プロジェクトの作成

それでは本題のプロジェクトの作成をしていきます。

ANACONDAよりterminalを開きます。開き方は前述したこちらの通り。

ここでプロジェクトフォルダを作成します。

私の場合、workspaceに移動し、そこに、「stock_scraping」というプロジェクトを作成します。

% cd workspace

% mkdir stock_scraping

% cd stock_scraping

ディレクトリを作成しましたので、この中に、scrapyのstartprojectコマンドを使ってプロジェクトを作ります。

ここでは、プロジェクト名を「stock_toscrape」としてプロジェクトを作ります。

% scrapy startproject stock_toscrape

これで、以下の階層でプロジェクトができました。

workspace > stock_scraping > stock_toscrape

作成したプロジェクトに移動します。

% cd stock_toscrape

Step2:Spiderを作成する

Spiderの作成

Spider名を「stocks_index」として作成します。

URLは、前のプロトコル(https://)部と最後の/は削除して貼り付けます。

ここでURLを指定することで、Spiderがアクセスできるドメインを設定しています。

% scrapy genspider stocks_index kabutan.jp                 
Created spider 'stocks_index' using template 'basic' in module:
  stock_toscape.spiders.stocks_index

VScodeを立ち上げ、「stock_scraring」下の「stock_toscrape」フォルダを開きます。

Spiderの設定

Spiderの設定をします。

以下ファイルを開きます。

stock_scraring > stock_toscape > spiders > settings.py

このファイルの最後に行を追加して設定を記述します。

出力ファイルの文字コードを指定

指定することで文字化けしないようにします。

FEED_EXPORT_ENCODING = 'utf-8'

Download delayを設定

ページをダウンロードしてから、次のページをダンロードするまでの間隔を指定します。

これはサーバーに負荷をかけすぎないようにするためです。

28行目に予め記述があるのでコメントアウトを解除します。

これを0.3secに設定します。

DOWNLOAD_DELAY = 0.3

Step3:データの取得(スクレイピング)コードを記述

spiderがスクレイピングを開始するurlを記した”start_urls”を一部修正します。

  • デフォルトではhttpで作成されるため、httpsに修正
  • 末尾の/を削除
import scrapy


class StocksIndexSpider(scrapy.Spider):
    name = 'stocks_index'
    allowed_domains = ['kabutan.jp']
    start_urls = ['https://kabutan.jp']

    def parse(self, response):
        pass

parseメソッド内を編集していきます。

scrapyの一連の流れはこうなっています

  1. リクエストがstart_urlsのURLに送られる
  2. webサイトからのレスポンスをparseメソッドで受ける
    この中にXpath等を記述することで情報の抽出ができる。

XPathを指定

指数の値の、XPathを記述します。

import scrapy


class StocksIndexSpider(scrapy.Spider):
    name = 'stocks_index'
    allowed_domains = ['kabutan.jp']
    start_urls = ['https://kabutan.jp/']

    def parse(self, response):
        indexes = response.xpath('//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]')

XPathの書き方の詳細はこちらをご覧ください。

この記述で正常に値の取得ができているか、yieldを用いて確認をしていきます。

import scrapy


class StocksIndexSpider(scrapy.Spider):
    name = 'stocks_index'
    allowed_domains = ['kabutan.jp']
    start_urls = ['https://kabutan.jp']

    def parse(self, response):
        indexes = response.xpath(
            '//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]')

        yield {
            'indexes': indexes
        }

spiderを実行します。

% scrapy crawl stocks_index

ここでの注意点

Scrapyでtbodyは使えない!

今回取得したかった値のpathはこちら

//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tbody/tr/td[@class="close"]

しかし、このpathの通りtbodyを付けるとどうも取得ができない。

Scrapyでtbodyは使えないようです!

そこでtbodyを無しにして、以下にすると、無事に取得することができました!

'//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]'

実行結果の中身を確認すると以下の結果が出力されており、正常に取得できていることがわかります。

2022-04-10 12:14:03 [scrapy.core.scraper] DEBUG: Scraped from <200 https://kabutan.jp>
{'indexes': [
<Selector xpath='//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]' data='<td class="close">26,985.80</td>'>, 
<Selector xpath='//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]' data='<td class="close">1,896.79</td>'>, 
<Selector xpath='//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]' data='<td class="close">17,098.66</td>'>, 
<Selector xpath='//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]' data='<td class="close">792.86</td>'>, 
<Selector xpath='//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]' data='<td class="close">1,830.39</td>'>, 
<Selector xpath='//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]' data='<td class="close">2,102.05</td>'>, 
<Selector xpath='//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]' data='<td class="close">3,258.12</td>'>, 
<Selector xpath='//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]' data='<td class="close">1,972.17</td>'>
]}

上記の通り、8個の指標を取得することができました。これを、for分で展開し、1つずつ取り出します。

import scrapy


class StocksIndexSpider(scrapy.Spider):
    name = 'stocks_index'
    allowed_domains = ['kabutan.jp']
    start_urls = ['https://kabutan.jp']

    def parse(self, response):
        indexes = response.xpath(
            '//div[@class="box1"][1]/table[@class="sub_shihyou"][1]/tr/td[@class="close"]')

        for index in indexes:
            yield {
                'index': index.xpath('.//text()').get()
            }

このget()は、テキストで取得する場合に必要となってきます。

JSON形式で値を取得します。

% scrapy crawl stocks_index -o index_close.json

これにより、プロジェクト直下にindex_close.jsonファイルが作成され、取得した値が表示されます。

[
{"index": "26,985.80"},
{"index": "1,896.79"},
{"index": "17,098.66"},
{"index": "792.86"},
{"index": "1,830.39"},
{"index": "2,102.05"},
{"index": "3,258.12"},
{"index": "1,972.17"}
]

株探から指数の値を取得することができました!

いろいろ作ったので、階層がわけわからないことになっていると思うので、最後にまとめておくと、以下のようになっています。

ここまでお読みいただきありがとうございました。

コメント