ScrapyでURLが既知の複数のページをスクレイピング

Posted: | Tags: Python, Scrapy, スクレイピング

Pythonのスクレイピング・クローリングフレームワークScrapyでURLが既知の複数のページをスクレイピングする方法について説明する。

Scrapyの基本的な使い方、ページ内のリンク(事前に分からないURL)をクロールする方法については以下の記事を参照。

ここでは以下の内容について説明する。

  • 例: Yahoo! Japanの検索結果をスクレイピング
  • start_urlsに複数のURLを指定
  • start_requests()を使用してURLを動的に指定

scrapy crawlコマンドから引数を渡してURLを制御したり、別のスクリプトからScrapyの処理を呼び出したりしたい場合は以下の記事を参照。

例: Yahoo! Japanの検索結果をスクレイピング

例としてYahoo! Japanの検索結果をスクレイピングする。

まず、単独のページに対してスクレイピングするスパイダーのコードを示す。「python」という検索ワードの1ページ目から各順位のタイトルとURLを抽出している。

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


class SearchSingleSpider(scrapy.Spider):
    name = 'search_single'
    allowed_domains = ['yahoo.co.jp']
    start_urls = ['https://search.yahoo.co.jp/search?p=python']

    def parse(self, response):
        for i, w in enumerate(response.css('div#WS2m div.w')):
            d = {}
            d['rank'] = i
            d['title'] = w.css('h3 a').xpath('string()').extract_first()
            d['url'] = 'https://' + w.css('div.a span.u').xpath('string()').extract_first()
            yield d

ここではitems.pyは使わずにただの辞書(dict型オブジェクト)に結果を格納している。enumerate()でインデックスを取得し検索順位rankとしている(0始まり)。

Scrapyプロジェクトの作成など基本的な部分は以下の記事を参照。

プロジェクトのリポジトリは以下。

start_urlsに複数のURLを指定

start_urlsには複数のURLを指定できる。

例えば複数の検索ワードを一括で処理したい場合は以下のように指定する。

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


class SearchMultiSpider(scrapy.Spider):
    name = 'search_multi'
    allowed_domains = ['yahoo.co.jp']
    start_urls = ['https://search.yahoo.co.jp/search?p=python',
                  'https://search.yahoo.co.jp/search?p=ruby']
    # start_urls = ['https://search.yahoo.co.jp/search?p=' + q for q in ['python', 'ruby']]

    def parse(self, response):
        for i, w in enumerate(response.css('div#WS2m div.w')):
            d = {}
            d['rank'] = i
            d['title'] = w.css('h3 a').xpath('string()').extract_first()
            d['url'] = 'https://' + w.css('div.a span.u').xpath('string()').extract_first()
            yield d

例ではコメントアウトしているが、リスト内包表記でもOK。

連番になったURL(https://example.com/1/ ~ https://example.com/9/など)を一気にスクレイピングしたい場合は以下のような書き方が可能。

start_urls = ['https://example.com/{}/'.format(str(i)) for i in range(1, 10)]

start_requests()を使用してURLを動的に指定

上の例のように、コード中にURLを決め打ちで記述するのではなく、例えば外部ファイルからURLのリストを読み込んでスクレイピングしたい場合などはstart_requests()を使う。

デフォルト(start_requests()を定義しない場合)ではscrapy.Request()start_urlsの各URLが渡される。

ここでは検索ワードのリストを以下のようなテキストファイルから読み込んでスクレイピングする例を示す。

python
ruby
source: list.txt

テキストファイルから読み込んだ検索ワードをベースとなるURLと連結し、scrapy.Request()に指定する。

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


class SearchMultiRequestsSpider(scrapy.Spider):
    name = 'search_multi_requests'
    allowed_domains = ['yahoo.co.jp']

    def start_requests(self):
        with open('list.txt') as f:
            for q in f:
                yield scrapy.Request('https://search.yahoo.co.jp/search?p=' + q)

    def parse(self, response):
        for i, w in enumerate(response.css('div#WS2m div.w')):
            d = {}
            d['rank'] = i
            d['title'] = w.css('h3 a').xpath('string()').extract_first()
            d['url'] = 'https://' + w.css('div.a span.u').xpath('string()').extract_first()
            yield d

この例の場合、テキストファイルlist.txtはカレントディレクトリであるプロジェクトディレクトリ(scrapy.cfgがあるディレクトリ)に置いておく。別の場所に置く場合は適宜パスを指定する。

テキストファイルの読み込みについては以下の記事を参照。

scrapy crawlコマンドから引数を渡してURLを制御したり、別のスクリプトからScrapyの処理を呼び出したりしたい場合は以下の記事を参照。

関連カテゴリー

関連記事