【Python3】Scrapyの基本的な使い方(クローリング、スクレイピング)

 どうも、梅雨ですね。雨ですね。嫌ですね。

 さて、結構前になりますがPythonの強力なクローリング、スクレイピングのフレームワークである「Scrapy」についていくつかTips的な記事を書いたことがあるのですが、そもそもScrapyの基本的な使い方に関しては紹介していなかったと思うので、今回はScrapyを使ってYahooのニュースサイトの情報を取得してみたいと思います。

環境

 今回の環境は以下の通り。

今回の環境

  • Mac OSX
  • Vagrant 2.2.3
  • Python 3.6.4
  • Scrapy 1.6

 今回は上記のとおり、Vagrantで仮装環境を作りPython3.6.4をインストールした上で進めていきたいと思います。VagrantやPythonの環境構築は別途用意してみてください。

Scrapyとは?

 まず、Scrapyとは、簡潔に言うとPythonのオープンソースフレームワークでクローリング・スクレイピングを手助けしてくれるPythonを代表するフレームワークの1つになります。クローリングやスクレーピングのプログラムをスクラッチで構築するとなると、面倒な処理が結構多いのですが、Scrapyを使うことで冗長的な処理をフレームワーク側に任せることができるので、クローリング・スクレイピング処理のみに集中することが最大のメリットかと思います。

Scrapyのインストール

 では、まずScrapyのインストールをしてみましょう。

 インストールはScrapyの公式サイトにも記載があるとおり、下記コマンドでインストールすることができます。

$ pip install scrapy

 インストールが完了したら、下記コマンドでバージョンを確認しておきます。

$ scrapy version
Scrapy 1.6.0

プロジェクトの生成

 インストールが完了したら、実際にクローリングするためのプロジェクトを生成していきます。

 Scrapyにはプロジェクトの生成のコマンドも用意されていますので、下記コマンドで生成します。

$ scrapy startproject プロジェクト名

 Scrapyコマンドの「startproject」の後ろにプロジェクト名を入力することで、プロジェクト名のディレクトリが生成されます。

 とりあえず、今回は適当に「scraping」というプロジェクトを生成したいと思います。

$ scrapy startproject scraping

プロジェクトの中身

 上記のコマンドを実行したら、実行ディレクトリ上に「scraping」というディレクトリが生成されていると思います。

 treeコマンドでディレクトリの構成を確認してみます。

$ tree scraping
scraping
├── scraping
│   ├── __init__.py
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── __pycache__
│   │   ├── __init__.cpython-36.pyc
│   │   └── settings.cpython-36.pyc
│   ├── settings.py
│   └── spiders
│       ├── __init__.py
│       ├── news.py
│       └── __pycache__
│           ├── __init__.cpython-36.pyc
│           └── news.cpython-36.pyc
└── scrapy.cfg

 scrapingディレクトリの中身はこんな感じになっており、たくさんのファイルやフォルダが存在しますが、基本的なクローリング作業をするのであれば、実際に編集が必要なファイルは一部のファイルのみになるので難しいことはありません。

ダウンロード間隔の設定

 では、まず実際にクローリング処理を記述するまえにWebサイトのダウンロード間隔を設定しておきます。ダウンロード間隔は1つのページをダウンロードして次のページのダウンロードを開始するまでの間隔のことになります。

 この間隔を短く設定してしまうと相手サーバーに思わぬ負荷を掛けてしまいかねないので必ず迷惑にならないような間隔に設定しましょう。

 ダウンロード間隔の設定はsettings.pyファイルに記述することで設定できます。settings.pyファイルを開くと30行目あたりに「DOWNLOAD_DELAY = 3」というコードがコメントアウトされていると思うので、このコメントアウトを外します。

DOWNLOAD_DELAY = 3

 このコードの意味は3秒間隔でダウンロードするという意味になります。

Spiderクラスの生成

 ダウンロード間隔を設定したら、Scrapyを使う上で主役ともいうべき「Spider」クラスを生成していきます。

 Spiderクラスは対象となるWebサイトごとに作成するもので、実際に対象サイトのURLを記述したり、Webサイトへのリクエスト、取得したデータのパースなどのクローリングにおいて核となる処理を記述するものになります。

 では、まずscrapingディレクトリに移動します。

$ cd ./scraping/scraping

移動したら、下記のとおり、genspiderコマンドでクラス名と対象となるWebサイトのドメインを入力して実行します。

$ scrapy genspider news news.yahoo.co.jp

 実行したら、「spiders」ディレクトリにに「news.py」というファイルができていると思います。

$ tree spiders
spiders
├── __init__.py
├── news.py
└── __pycache__
    ├── __init__.cpython-36.pyc
    └── news.cpython-36.pyc

クローリング

 では、生成したnews.pyファイルを開いて実際にクローリング処理を記述していきたいと思います。

 生成時は下記のような内容になっていると思います。

# -*- coding: utf-8 -*-
import scrapy

class NewsSpider(scrapy.Spider):
    name = 'news'
    allowed_domains = ['news.yahoo.co.jp']
    start_urls = ['http://news.yahoo.co.jp/']

    def parse(self, response):
        pass

allowed_domains

 NewsSpiderクラスがあり、その中に「allowed_domains」があるかと思いますが、ここで指定しているドメインのみ許可するということになります。

start_urls

 その下に「start_urls」という項目もあると思いますが、ここで指定してるURLが開始するページになります。

ニュース一覧のリンク先URLを取得する

 では、このファイルを以下のように編集してニュース一覧のリンクを取得してみましょう。

import scrapy

class NewsSpider(scrapy.Spider):
    name = 'news'
    allowed_domains = ['news.yahoo.co.jp']
    start_urls = ['https://news.yahoo.co.jp/'] #http -> httpsに変更

    def parse(self, response):
        print('ニュースリンク一覧:')
        print(response.css('.topicsListItem a::attr("href")').extract())
        pass

 変更箇所は、一番上にあった「# -*- coding: utf-8 -*-」というコードを削除(Python3では必要ない)して、start_urlsで指定しているURLをhttpsに変更して、parseメソッドに処理を追記しました。

 リンク先のURLを取得しているのはpraseメソッド内の「response.css(‘.topicsListItem a::attr(“href”)’).extract()」というコードになります。ページをダウンロードして返ってきたresponseデータからresponse.cssメソッドで「topicsListItem」というクラス名が付与されているタグ内にあるaタグのhrefの内容を取得しています。extract()メソッドはノード一覧を文字列listとして取得するメソッドになります。

 では、実際に実行してみます。

 実行する場合は、「spiders」ディレクトリに移動して下記コマンドを入力することで実行することができます。

$ scrapy crawl news

 実行するといろいろと結果が返ってくると思いますが、その中に下記のような記述があると思います。

ニュースリンク一覧:
['https://news.yahoo.co.jp/pickup/6326593', 'https://news.yahoo.co.jp/pickup/6326582', 'https://news.yahoo.co.jp/pickup/6326591', 'https://news.yahoo.co.jp/pickup/6326594', 'https://news.yahoo.co.jp/pickup/6326574', 'https://news.yahoo.co.jp/pickup/6326583', 'https://news.yahoo.co.jp/pickup/6326589', 'https://news.yahoo.co.jp/pickup/6326595']

 これがpraseメソッドで実行した処理になります。しっかりとニュース一覧のリンク先が取得できていますね。

ニュースタイトルを取得してみる

 では、今度は取得したリンク先一覧をもとにそれぞれのリンク先もクローリングしてニュースのタイトルを取得してみます。

 news.pyファイルを再度下記のように編集します。

import scrapy

class NewsSpider(scrapy.Spider):
    name = 'news'
    allowed_domains = ['news.yahoo.co.jp']
    start_urls = ['https://news.yahoo.co.jp/'] #http -> httpsに変更

    def parse(self, response):
        print('ニュースリンク一覧:')
        print(response.css('.topicsListItem a::attr("href")').extract())

        links = response.css('.topicsListItem a::attr("href")').extract()

        print('ニュースタイトル一覧:')
        for link in links:
          yield scrapy.Request(link, self.parse_title)

    def parse_title(self, response):
        print(response.css('.tpcNews_title::text').extract_first())
        

 先ほどのデータに追加するかたちで取得したリンク先一覧をfor分で回して、scrapyRequestメソッドでそのページをダウンロードして、コールバックメソッドにparse_titleメソッドを指定して、parse_titleメソッドでニュースタイトルを取得しています。

 これを改めて実行すると、ニュースタイトルが取得できていると思います。

Itemの生成

 ここまでやればなんとなくクローリングしている感覚が掴めたと思います。たったこれだけ記述するだけで綺麗にクローリングできるのもScrapyのメリットですね。

 さて、クローリングしてデータの取得まで出来るようになりましたが、取得したデータはdictに保存してもいいのですが、ScrapyにはItemというSpiderが抽出したデータを格納できるオブジェクトのようなものが用意されているので、Itemを作成してそこにデータを格納してみたいと思います。

 spidersディレクトリの一階層上に「items.py」というファイルがあるのでそれを開いて下記のように編集します。

import scrapy

class NewsItem(scrapy.Item):
    title = scrapy.Field()
    link = scrapy.Field()

 内容としては「NewsItem」というクラスを作成して、その中に「title」と「link」というフィールドがあるといった感じですね。

クローリングしてItemにデータを格納する

 Itemを作成したら、またSpidersディレクトリのnews.pyに戻って下記のように編集してみます。

import scrapy
from scraping.items import NewsItem

class NewsSpider(scrapy.Spider):
    name = 'news'
    allowed_domains = ['news.yahoo.co.jp']
    start_urls = ['https://news.yahoo.co.jp/'] #http -> httpsに変更

    def parse(self, response):
        links = response.css('.topicsListItem a::attr("href")').extract()

        item = NewsItem()

        for link in links:
          item['link'] = link
          request = scrapy.Request(link, self.parse_title)
          request.meta['item'] = item
          yield request

    def parse_title(self, response):
        item = response.meta['item']
        item['title'] = response.css('.tpcNews_title::text').extract_first()
        yield item

 上の方で作成したNewsItemクラスを読み込み、そのItemの中にデータを格納するように編集しました。Itemはrequest.metaメソッドを使用することでメソッド間で受け渡しができるので、これも合わせて覚えておくといいかもしれません。

 あとは、実際に実行してみて結果を確認してみてください。

 ということで、今回はPythonのクローリング、スクレイピングのフレームワーク「Scrapy」の基本的な使い方をYahooのニュース情報を取得するかたちで紹介しました。

 データ収集は機械学習等、データ分析には欠かせない要素なので、このようにScrapyを利用して簡単に作成できるようにしておくと役立つかもしれませんね。

 てことで、ここまで。また!

お仕事のご相談・ご依頼
お気軽にお問い合わせください!

お仕事の依頼はこちら

著者プロフィール

Taka

東京、奄美大島を拠点にサーフィンとスノーボードが好きなフリーランスのWebクリエイターです。普段はプログラム書いたりデザインしたり映像作ったりしています。いろいろな人の話しを聞くのが好きなので、このブログを通して多くの人と繋がりが出来たら嬉しいです。noteとInstagramもやっているのでフォローしてくれたらありがたいです!

人気記事

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です