Spider

Spiderは特定のWebサイト(あるいは複数のひとかたまりのWebサイト)を、どのように抽出するかを定義したクラスです。抽出方法にはクロール方法(どのようにリンクを辿るか)やどのような構造化されたデータ(抽出したアイテム)をそのページから抽出するかが含まれます。言い換えれば、Spiderは特定のWebサイト(あるいは複数のひとかたまりのWebサイト)のページをクロールして解析するためのカスタマイズされた動作を定義する場所です。

Spiderの抽出サイクルは以下の様なものです:

  1. 最初のURLを対象としたRequestsを生成し、それらのRequestsによってダウンロードされたResponseとともに特定のcallback関数が呼び出されます。

    最初のrequestsは start_requests() メソッド(デフォルト)によって取得されます。これは、start_urls で指定されたURLと parse をコールバック関数に指定して Request を生成します。

  2. コールバック関数では、レスポンス(Webページの) をパースし、抽出されたデータのdict、 Item オブジェクト、Request オブジェクト、あるいはそれらのIterableなオブジェクトを戻します。それらのRequestもまたコールバック関数(おそらく同じもの)が指定されており、Scrapyによってダウンロードされたレスポンスを処理します。

  3. コールバック関数では、ページのコンテンツをパースします。通常は セレクタ を使用し、(BeautifulSoupやlxmlなどの他のパーサを利用することも可能です)パースされたデータを含んだItemを生成しします。

  4. 最後は、spiderから戻されたitemは一般的には、データベースに永続化(詳しくは Item Pipeline 参照)されるか、Feed exports を使用してファイルに書き出されます。

このサイクルは(多かれ少なかれ)どのようなSpiderにも適用されます。様々な目的のための様々な種類のSpiderがデフォルトでScrapyにはバンドルされています。それらのSpiderについて説明します。

scrapy.Spider

class scrapy.spiders.Spider

これは最もシンプルなSpiderであり、他のすべてのSpiderが継承しなければなりません。(Scrapyにバンドルされているものだけではなく、貴方が自ら作成したSpiderにも該当します)これは特別な機能は提供しません。 提供されるのはSpiderの start_urls 属性をもとにしたRequestを送るためのデフォルトの start_requests() メソッドの実装とResponseごとに呼び出されるSpiderの parse メソッドの実装です。

name

Spiderの名前を定義します。このSpiderの名前はScrapyによってどのようにSpiderが配置されインスタンス化されているのかの識別に使われます。そのため一意である必要がありますが、同じSpiderから複数のインスタンスを生成することを妨げるものではありません。この属性はSpiderにおいて最も重要であり、必須です。

単一のドメインを扱うSpiderでは、TLD の有無にかかわらずSpiderの名前にドメイン名をつけるのが一般的な手法です。例えば、 mywebsite.com をクロールするSpiderの名前は多くの場合 mywebsite とされます。

注釈

Python 2では、ASCII文字のみでなければなりません。

allowed_domains

Spiderにクロールを許可するドメイン名のオプショナルなリストです。リクエストされたURLがそれらのリストに含まれるドメイン(あるいばサブドメイン)に所属していなかった場合 OffsiteMiddleware が有効化されていない限りは追跡されません。

例えば、対象のURLが https://www.example.com/1.html である場合、 'example.com' をリストに追加します。

start_urls

URLを明示的に指定しなかった時に、Spiderがクロールを始めるURLのリストです。その場合、最初にダウンロードされるページは、これにリストされたページになります。後続の Requeststart_urls に含まれるデータから順番に生成されます。

custom_settings

設定値を含んだディクショナリであり、Spiderが実行される際にプロジェクト単位で指定された設定を上書きます。インスタンス化される前に設定を更新するため、クラス属性として定義する必要があります。

利用可能なビルトインの設定のリストは Built-in settings reference を参照してください。

crawler

この属性はクラスの初期化後に、 from_crawler() クラスメソッドによってセットされこのSpiderインスタンスがどの Crawler オブジェクトにリンクされているかを持ちます。

クローラは、単一のエントリーアクセス(extension, middleware, signal manager等)のため、プロジェクト内のたくさんのコンポーネントをカプセル化しています。詳細については Crawler API を参照してください。

settings

実行されているSpiderの設定です。これは Settings インスタンスです。詳しくは Settings を参照してください。

logger

Spiderの name でPythonのloggerが作成されます。ログメッセージの送信はこれを介して行うことができます。 詳しくは Logging from Spiders を参照してください。

from_crawler(crawler, *args, **kwargs)

これはScrapyによってSpiderを作成するために用いられるクラスメソッドです。

このメソッドを直接オーバライドする必要はありません。デフォルトの実装は __init__() に必要な引数 args と 名前付き引数 kwagrs を渡すProxyとして動作するためです。

それでもなお、このメソッドは crawlersettings を新しいインスタンスに設定するため、Spiderのコード内でそれらの属性にアクセスすることができます。

パラメータ:
  • crawler (Crawler instance) -- Spiderに紐付けられているCrawler
  • args (list) -- __init__() に渡された引数
  • kwargs (dict) -- __init__() に渡された名前付き引数
start_requests()

このメソッドはSpiderがクロールするための最初のリクエストを含むiterableなインスタンスを戻さなければいけません。これはScrapyによってSpiderがクロールするために起動する際に呼び出されます。Scrapyはこれを一度だけ呼び出すため、 start_requests() をジェネレータとして安全に実装することができます。

デフォルトの実装では start_urls 内の各URLに対して Request(url, dont_filter=True) を生成します。

もし、ドメインのクロール開始のリクエストを変更したい場合は、このメソッドをオーバライドします。例えば、最初にPOSTリクエストでログインする必要がある場合は、以下のようにします。

class MySpider(scrapy.Spider):
    name = 'myspider'

    def start_requests(self):
        return [scrapy.FormRequest("http://www.example.com/login",
                                   formdata={'user': 'john', 'pass': 'secret'},
                                   callback=self.logged_in)]

    def logged_in(self, response):
        # here you would extract links to follow and return Requests for
        # each of them, with another callback
        pass
parse(response)

これはScrapyによってダウンロードされたレスポンスを処理するために、コールバックを指定しなかったリクエストでのデフォルトのコールバックです。

parse メソッドはレスポンスの処理と、抽出されたデータを戻します。また、さらに辿るべきURLを戻すこともあります。 他のリクエストのコールバックは Spider と同じ要件を持ちます。

このメソッドは、他のリクエストのコールバックと同じように、 Request のiterable、dict、 Item オブジェクトのいずれかを戻さなければいけません。

パラメータ:response (Response) -- 処理されるレスポンス
log(message[, level, component])

Spiderの logger を通じてログメッセージを送るための下位互換を保つために設けられたラッパーです。詳しくは Logging from Spiders を確認してください。

closed(reason)

スパイダーの終了時に呼ばれます。このメソッドは spider_closed シグナルを送るためのsignals.connect() のショートカットを提供します。

例:

import scrapy


class MySpider(scrapy.Spider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = [
        'http://www.example.com/1.html',
        'http://www.example.com/2.html',
        'http://www.example.com/3.html',
    ]

    def parse(self, response):
        self.logger.info('A response from %s just arrived!', response.url)

1つのコールバックで複数のRequestとItemを戻す場合:

import scrapy

class MySpider(scrapy.Spider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = [
        'http://www.example.com/1.html',
        'http://www.example.com/2.html',
        'http://www.example.com/3.html',
    ]

    def parse(self, response):
        for h3 in response.xpath('//h3').getall():
            yield {"title": h3}

        for href in response.xpath('//a/@href').getall():
            yield scrapy.Request(response.urljoin(href), self.parse)

start_urls の代わりに start_requests() を直接使い、またより構造化されたデータを戻すため Item を使用する場合:

import scrapy
from myproject.items import MyItem

class MySpider(scrapy.Spider):
    name = 'example.com'
    allowed_domains = ['example.com']

    def start_requests(self):
        yield scrapy.Request('http://www.example.com/1.html', self.parse)
        yield scrapy.Request('http://www.example.com/2.html', self.parse)
        yield scrapy.Request('http://www.example.com/3.html', self.parse)

    def parse(self, response):
        for h3 in response.xpath('//h3').getall():
            yield MyItem(title=h3)

        for href in response.xpath('//a/@href').getall():
            yield scrapy.Request(response.urljoin(href), self.parse)

Spiderの引数

Spiderは挙動の変更をもたらすいくつかの引数を受け入れます。一般的な用途としては、クロールの開始URLやサイトの一部をクロールするためのセクション指定などです。しかし、それ以外の機能を構成するためにも使用できます。

Spiderの引数は crawl コマンドの -a オプションを通じて渡されます。例えば以下の様になります:

scrapy crawl myspider -a category=electronics

Spiderに渡された引数は __init__ メソッドでアクセスすることができます:

import scrapy

class MySpider(scrapy.Spider):
    name = 'myspider'

    def __init__(self, category=None, *args, **kwargs):
        super(MySpider, self).__init__(*args, **kwargs)
        self.start_urls = ['http://www.example.com/categories/%s' % category]
        # ...

デフォルトの __init__ メソッドはすべてのSpiderの引数も受け取り、それらはSpiderの属性にコピーされます。上記の例は、次のように書くこともできます。

import scrapy

class MySpider(scrapy.Spider):
    name = 'myspider'

    def start_requests(self):
        yield scrapy.Request('http://www.example.com/categories/%s' % self.category)

Spiderの引数は文字列のみが使用できることに注意してください。Spiderは引数をパースすることはありません。 start_urls をコマンドラインからセットしようとする場合、ユーザ自身で ast.literal_evaljson.loads 等を使用してListにパースし、属性としてセットする必要があります。そうしない場合、 start_urls に渡した文字列自体をiterableとして処理され(Pythonでよくある落とし穴)、1文字ごとにURLとして扱われてしまいます。

有効なユースケースとしては、HttpAuthMiddleware を使用してHTTP認証のクレデンシャルをセットするものや、 UserAgentMiddleware を使用してユーザーエージェントをセットするものがあります:

scrapy crawl myspider -a http_user=myuser -a http_pass=mypassword -a user_agent=mybot

Spiderの引数はScrapydの schedule.json APIでも指定することができます。詳しくは Scrapyd documentation を参照してください。

一般的なSpider

Scrapyはいくつかの有用なSpiderを提供しています。ユーザはこれらのSpiderのサブクラスとして自身のSpiderを作成することができます。これらのSpiderの目的は、いくつかのクロールケースに合わせて有効な機能を提供することです。それは例えば、特定のルールに基づいてサイト上のすべてのリンクをたどったり、 Sitemaps からクロールしたり、XML/CSVフィードをパースしたりすることです。

以下の例で使用されるSpiderは、myproject.items モジュールで宣言された TestItem を持ったプロジェクト配下で作成されているものとします。

import scrapy

class TestItem(scrapy.Item):
    id = scrapy.Field()
    name = scrapy.Field()
    description = scrapy.Field()

CrawlSpider

class scrapy.spiders.CrawlSpider

通常のウェブサイトをクロールする最も一般的なSpiderです。これは、いくつかのルールに従ってリンクをたどっていく便利なメカニズムを提供します。あなたの特定のウェブサイトやプロジェクトには最適ではないかもしれませんが、多くのケースではこのSpiderで十分ですので、より多くのカスタム機能のために必要に応じて上書きしたり、独自のスパイダーを実装することができます。

Spiderから継承した属性(指定する必要がある)を除いて、 このクラスは新しい属性をサポートします:

rules

Rule オブジェクトの1つ(あるいは複数)のリストです。それぞれの Rule はサイトをクロールするための特定の動作を定義します。もし、あるリンクについて複数のルールが該当する場合この属性に定義した順に従った、最初の1つが使用されます。

このスパイダーは, オーバーライド可能なメソッドも公開しています:

parse_start_url(response)

このメソッドは start_urls のレスポンスのパースのために呼び出されます。初期のレスポンスの解析を行い、 Item オブジェクト、 Request オブジェクトあるいはそれらを含んだiterableなデータのいずれかを戻す必要があります。

クロールのルール

class scrapy.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)

link_extractor はクロールされたそれぞれのページからどのようにリンクを抽出するかを定義した Link Extractor オブジェクトです。

callback はlink_extractorによって抽出された各リンク毎に呼び出される、呼び出し可能オブジェクトか文字列(この場合, その名前を持つSpiderのメソッドが使用されます)です。このコールバックは第一引数にレスポンスを受け取り、 Item および/または Request オブジェクト(あるいはそれらのサブクラス)のリストを戻す必要があります。

警告

スパイダーのクロールのルールを作成する際は、 parse メソッドを使用しないように注意します。 CrawlSpiderparse メソッドを自身のロジックに組み込んでいるためです。そのため、もし parse メソッドをオーバーライドしてしまうと、CrawlSpiderは動作しなくなります。

cb_kwargs はコールバック関数に渡される名前付き引数を含んだdictです。

follow は, このルールで抽出された各レスポンスからリンクをたどるかどうかを指定する bool 値です. もし callback がNoneの場合、 follow はデフォルトで True になり、それ以外では False になります。

process_links はlink_extractorによって抽出された各リンク毎に呼び出される、呼び出し可能オブジェクトか文字列(この場合、その名前を持つSpiderのメソッドが使用されます)です。主な目的はフィルタリングです。

process_request は ruleによって抽出されたすべてのリクエストに対して呼び出される、呼び出し可能オブジェクトか文字列(この場合、その名前を持つSpiderのメソッドが使用されます)です。これはrequestかNone(フィルタされた事を示す)を戻す必要があります。

CrawlSpiderの例

ルールを使用したCrawlSpiderの例を見てみましょう:

import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class MySpider(CrawlSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com']

    rules = (
        # Extract links matching 'category.php' (but not matching 'subsection.php')
        # and follow links from them (since no callback means follow=True by default).
        Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),

        # Extract links matching 'item.php' and parse them with the spider's method parse_item
        Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item'),
    )

    def parse_item(self, response):
        self.logger.info('Hi, this is an item page! %s', response.url)
        item = scrapy.Item()
        item['id'] = response.xpath('//td[@id="item_id"]/text()').re(r'ID: (\d+)')
        item['name'] = response.xpath('//td[@id="item_name"]/text()').get()
        item['description'] = response.xpath('//td[@id="item_description"]/text()').get()
        return item

このSpiderはexample.comのホームページをクロールし、カテゴリのリンクとアイテムのリンクを収集し、後者を parse_item メソッドによって解析します。各アイテムのレスポンスでは、XPathを使ってHTMLからデータを抽出し、 Item にデータを埋めます。

XMLFeedSpider

class scrapy.spiders.XMLFeedSpider

XMLFeedSpiderはXMLフィードの特定のノード名について繰り返し解析するように設計されています。イテレータは iternodes , xml, そして html から選ぶことができます。パフォーマンス的な観点から推奨は iternodes を使用することです。何故なら、 xmlhtml のイテレータは解析のため、一度に完全なDOM構造を生成するためです。しかし、 html は不完全なマークアップで作成されたXMLを解析する際には有用です。

イテレータとタグ名を設定するには、次のクラス属性を定義する必要があります:

iterator

使用するイテレータを定義する文字列です。以下の値が指定できます。

  • 'iternodes' - 正規表現に基づいた高速なイテレータ
  • 'html' - Selector を使用したイテレータ。これはDOMの解析のためメモリ上にすべてのDOMをロードする必要があり、大きなフィードではそれが問題につながることに注意してください。
  • 'xml' - Selector を使用したイテレータ。これはDOMの解析のためメモリ上にすべてのDOMをロードする必要があり、大きなフィードではそれが問題につながることに注意してください。

デフォルト: 'iternodes'

itertag

繰り返し対象であるノード名(あるいは要素)を示す文字列です。例:

itertag = 'product'
namespaces

このスパイダーで処理される、文書で利用可能な名前空間を定義する (prefix, uri) のタプルのリストです。 prefixurlregister_namespace() メソッドを使用して名前空間を自動的に登録するために使用されます。

itertag 属性に名前空間をもったノードを指定することができます。

例:

class YourSpider(XMLFeedSpider):

    namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')]
    itertag = 'n:url'
    # ...

これらの新しい属性とは別に、このスパイダーは次のオーバーライド可能なメソッドも持っています:

adapt_response(response)

SpiderミドルウェアからのレスポンスがSpiderによって解析される前に受け取るメソッドです。解析の前にレスポンスボディを変更するために利用できます。このメソッドはレスポンスを受け取りレスポンスを戻します。(同じものかあるいは別のものです)

parse_node(response, selector)

指定されたタグ名(itertag)と一致するノードに対して呼び出されるメソッドです。レスポンスと Selector を各ノードごとに受け取ります。このメソッドをオーバーライドすることは必須です。そうしない場合、Spiderは動作しません。このメソッドは ItemRequest あるいは、それらを含んだiterableを戻す必要があります。

process_results(response, results)

Spiderによって戻された各結果(ItemかRequest)に対して呼び出されるメソッドであり、結果をフレームワークのコアに戻す際に必要な最後の処理を行うことを目的としています。それは例えば、アイテムのID設定等です。このメソッドは結果のリストとそれらの結果の元になったレスポンスを受け取ります。これは結果(ItemかRequest)のリストを戻す必要があります。

XMLFeedSpiderの例

これらのSpiderは簡単に利用できます。まずは例を見てみましょう:

from scrapy.spiders import XMLFeedSpider
from myproject.items import TestItem

class MySpider(XMLFeedSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com/feed.xml']
    iterator = 'iternodes'  # This is actually unnecessary, since it's the default value
    itertag = 'item'

    def parse_node(self, response, node):
        self.logger.info('Hi, this is a <%s> node!: %s', self.itertag, ''.join(node.getall()))

        item = TestItem()
        item['id'] = node.xpath('@id').get()
        item['name'] = node.xpath('name').get()
        item['description'] = node.xpath('description').get()
        return item

上述の基本的な例は、 start_urls によって指定されたフィードからダウンロードしそれぞれの item タグを繰り返し処理し、出力し、ランダムなデータを Item に格納するSpiderです。

CSVFeedSpider

class scrapy.spiders.CSVFeedSpider

このSpiderは、ノードの代わりに行を繰り返し処理する点を除きXMLFeedSpiderとよく似ています。各繰り返しに対して呼び出されるメソッドは parse_row() です。

delimiter

CSVファイルの各フィールドの区切り文字を指定します。デフォルトは ',' (カンマ) です。

quotechar

CSVファイル内の各フィールドの囲み文字を含む文字列です。デフォルトは '"' (ダブルクォテーション)です。

headers

CSVファイルに含まれるカラム名のリストです。

parse_row(response, row)

レスポンスと、CSVファイルを元に指定された(あるいは検知された)ヘッダーをキーとするdict(各行を表現する)を受け取ります。このSpiderも adapt_responseprocess_results メソッドをオーバライドすることができ、前後に独自の処理を行うことができます。

CSVFeedSpiderの例

前述のものと似ていますが、 CSVFeedSpider を用いた例を見てみましょう:

from scrapy.spiders import CSVFeedSpider
from myproject.items import TestItem

class MySpider(CSVFeedSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com/feed.csv']
    delimiter = ';'
    quotechar = "'"
    headers = ['id', 'name', 'description']

    def parse_row(self, response, row):
        self.logger.info('Hi, this is a row!: %r', row)

        item = TestItem()
        item['id'] = row['id']
        item['name'] = row['name']
        item['description'] = row['description']
        return item

SitemapSpider

class scrapy.spiders.SitemapSpider

SitemapSpiderは Sitemaps を使ってURLを探索しながらサイトをクロールすることができます。

ネストされたSitemapや robots.txt からSitemapを取得して利用することもサポートされています。

sitemap_urls

クロールしたいSitemapを指すURLのリストです。

robots.txt を指定することもできます。その場合、その解析結果に含まれるSitemapが利用されます。

sitemap_rules

タプル (regex, callback) のリストです:

  • regex はSitemapから抽出されたURLにマッチさせる正規表現です。 regex は文字列でもコンパイル済のregexオブジェクトでも指定できます。
  • callbackは正規表現にマッチしたURLを処理するためのコールバック関数です。 callback は文字列(この場合、その名前を持つSpiderのメソッドが使用されます)か呼び出し可能オブジェクトを指定します。

例:

sitemap_rules = [('/product/', 'parse_product')]

ルールは順番に適用され、最初に該当したものだけが使用されます。

もしこの属性を省略した場合、Sitemapに含まれる全てのURLが処理され、 parse コールバックメソッドで処理されます。

sitemap_follow

従うべきSitemapの正規表現のリストです。これは ほかのSitemapファイルを指し示す Sitemap index files を使っているサイトでのみ利用されます。

デフォルトでは全てのSitemapに従います。

url の代替リンクに従うかどうかを指定します。これらのリンクは、同じ url ブロック内で渡された別の言語の同じWEBサイトへのリンクです。

例:

<url>
    <loc>http://example.com/</loc>
    <xhtml:link rel="alternate" hreflang="de" href="http://example.com/de"/>
</url>

sitemap_alternate_links が設定されている場合、両方のURLが取得されます。 sitemap_alternate_links が無効化されている場合は、 http://example.com/ のみが取得されます。

デフォルトでは sitemap_alternate_links は無効化されています。

sitemap_filter(entries)

This is a filter funtion that could be overridden to select sitemap entries based on their attributes.

例:

<url>
    <loc>http://example.com/</loc>
    <lastmod>2005-01-01</lastmod>
</url>

We can define a sitemap_filter function to filter entries by date:

from datetime import datetime
from scrapy.spiders import SitemapSpider

class FilteredSitemapSpider(SitemapSpider):
    name = 'filtered_sitemap_spider'
    allowed_domains = ['example.com']
    sitemap_urls = ['http://example.com/sitemap.xml']

    def sitemap_filter(self, entries):
        for entry in entries:
            date_time = datetime.strptime(entry['lastmod'], '%Y-%m-%d')
            if date_time.year >= 2005:
                yield entry

This would retrieve only entries modified on 2005 and the following years.

Entries are dict objects extracted from the sitemap document. Usually, the key is the tag name and the value is the text inside it.

It's important to notice that:

  • as the loc attribute is required, entries without this tag are discarded
  • alternate links are stored in a list with the key alternate (see sitemap_alternate_links)
  • namespaces are removed, so lxml tags named as {namespace}tagname become only tagname

If you omit this method, all entries found in sitemaps will be processed, observing other attributes and their settings.

SitemapSpiderの例

もっともシンプルな例: Sitemapから発見された全てのURLを parse コールバックを用いて処理する場合:

from scrapy.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/sitemap.xml']

    def parse(self, response):
        pass # ... scrape item here ...

いくつかのURLを特定のコールバックで処理し、他のURLは異なるコールバックで処理する場合:

from scrapy.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/sitemap.xml']
    sitemap_rules = [
        ('/product/', 'parse_product'),
        ('/category/', 'parse_category'),
    ]

    def parse_product(self, response):
        pass # ... scrape product ...

    def parse_category(self, response):
        pass # ... scrape category ...

robots.txt ファイルに定義されたSitemapのうち /sitemap_shop をURLに含んだものにのみ従う場合:

from scrapy.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/robots.txt']
    sitemap_rules = [
        ('/shop/', 'parse_shop'),
    ]
    sitemap_follow = ['/sitemap_shops']

    def parse_shop(self, response):
        pass # ... scrape shop here ...

SitemapSpiderを他のソースから取得されたURLと組み合わせる場合:

from scrapy.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/robots.txt']
    sitemap_rules = [
        ('/shop/', 'parse_shop'),
    ]

    other_urls = ['http://www.example.com/about']

    def start_requests(self):
        requests = list(super(MySpider, self).start_requests())
        requests += [scrapy.Request(x, self.parse_other) for x in self.other_urls]
        return requests

    def parse_shop(self, response):
        pass # ... scrape shop here ...

    def parse_other(self, response):
        pass # ... scrape other here ...