Spider¶
Spiderは特定のWebサイト(あるいは複数のひとかたまりのWebサイト)を、どのように抽出するかを定義したクラスです。抽出方法にはクロール方法(どのようにリンクを辿るか)やどのような構造化されたデータ(抽出したアイテム)をそのページから抽出するかが含まれます。言い換えれば、Spiderは特定のWebサイト(あるいは複数のひとかたまりのWebサイト)のページをクロールして解析するためのカスタマイズされた動作を定義する場所です。
Spiderの抽出サイクルは以下の様なものです:
最初のURLを対象としたRequestsを生成し、それらのRequestsによってダウンロードされたResponseとともに特定のcallback関数が呼び出されます。
最初のrequestsは
start_requests()
メソッド(デフォルト)によって取得されます。これは、start_urls
で指定されたURLとparse
をコールバック関数に指定してRequest
を生成します。コールバック関数では、レスポンス(Webページの) をパースし、抽出されたデータのdict、
Item
オブジェクト、Request
オブジェクト、あるいはそれらのIterableなオブジェクトを戻します。それらのRequestもまたコールバック関数(おそらく同じもの)が指定されており、Scrapyによってダウンロードされたレスポンスを処理します。コールバック関数では、ページのコンテンツをパースします。通常は セレクタ を使用し、(BeautifulSoupやlxmlなどの他のパーサを利用することも可能です)パースされたデータを含んだItemを生成しします。
最後は、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のリストです。その場合、最初にダウンロードされるページは、これにリストされたページになります。後続の
Request
はstart_urls
に含まれるデータから順番に生成されます。
-
custom_settings
¶ 設定値を含んだディクショナリであり、Spiderが実行される際にプロジェクト単位で指定された設定を上書きます。インスタンス化される前に設定を更新するため、クラス属性として定義する必要があります。
利用可能なビルトインの設定のリストは Built-in settings reference を参照してください。
-
crawler
¶ この属性はクラスの初期化後に、
from_crawler()
クラスメソッドによってセットされこのSpiderインスタンスがどのCrawler
オブジェクトにリンクされているかを持ちます。クローラは、単一のエントリーアクセス(extension, middleware, signal manager等)のため、プロジェクト内のたくさんのコンポーネントをカプセル化しています。詳細については Crawler API を参照してください。
-
logger
¶ Spiderの
name
でPythonのloggerが作成されます。ログメッセージの送信はこれを介して行うことができます。 詳しくは Logging from Spiders を参照してください。
-
from_crawler
(crawler, *args, **kwargs)¶ これはScrapyによってSpiderを作成するために用いられるクラスメソッドです。
このメソッドを直接オーバライドする必要はありません。デフォルトの実装は
__init__()
に必要な引数 args と 名前付き引数 kwagrs を渡すProxyとして動作するためです。それでもなお、このメソッドは
crawler
とsettings
を新しいインスタンスに設定するため、Spiderのコード内でそれらの属性にアクセスすることができます。パラメータ: - crawler (
Crawler
instance) -- Spiderに紐付けられているCrawler - args (list) --
__init__()
に渡された引数 - kwargs (dict) --
__init__()
に渡された名前付き引数
- crawler (
-
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_eval や json.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つが使用されます。
このスパイダーは, オーバーライド可能なメソッドも公開しています:
-
クロールのルール¶
-
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
メソッドを使用しないように注意します。CrawlSpider
はparse
メソッドを自身のロジックに組み込んでいるためです。そのため、もし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
を使用することです。何故なら、xml
やhtml
のイテレータは解析のため、一度に完全なDOM構造を生成するためです。しかし、html
は不完全なマークアップで作成されたXMLを解析する際には有用です。イテレータとタグ名を設定するには、次のクラス属性を定義する必要があります:
-
iterator
¶ 使用するイテレータを定義する文字列です。以下の値が指定できます。
'iternodes'
- 正規表現に基づいた高速なイテレータ'html'
-Selector
を使用したイテレータ。これはDOMの解析のためメモリ上にすべてのDOMをロードする必要があり、大きなフィードではそれが問題につながることに注意してください。'xml'
-Selector
を使用したイテレータ。これはDOMの解析のためメモリ上にすべてのDOMをロードする必要があり、大きなフィードではそれが問題につながることに注意してください。
デフォルト:
'iternodes'
-
itertag
¶ 繰り返し対象であるノード名(あるいは要素)を示す文字列です。例:
itertag = 'product'
-
namespaces
¶ このスパイダーで処理される、文書で利用可能な名前空間を定義する
(prefix, uri)
のタプルのリストです。prefix
とurl
はregister_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は動作しません。このメソッドはItem
かRequest
あるいは、それらを含んだ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_response
とprocess_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に従います。
-
sitemap_alternate_links
¶ 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 filterentries
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
(seesitemap_alternate_links
) - namespaces are removed, so lxml tags named as
{namespace}tagname
become onlytagname
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 ...