スパイダー¶
パイダーとは, クロールの実行方法(リンクをたどる方法)やページから構造化データを抽出する方法(アイテムのスクレイピングなど)を含む, 特定のサイト(またはサイトのグループ)のスクレイピング方法を定義するクラスです. 言い換えれば, スパイダー は, 特定のサイト(場合によってはサイトのグループ)のページをクロールして 解析するためのカスタム動作を定義する場所です.
スパイダーの場合, スクレイピングのライフサイクルは以下のようになります:
最初のURLをクロールする最初のリクエストを生成し, それらのリクエストからダウンロードされたレスポンスで呼び出されるコールバック関数を指定します.
実行する最初のリクエストは,
start_requests()
メソッドを呼び出すことによって取得されます. このメソッドは, デフォルトでstart_urls
で指定されたURLとRequest
のコールバック関数として呼ばれるparse
メソッドで,Request
を生成します.コールバック関数では, レスポンス(Webページ)を解析し, 抽出されたデータ,
Item
オブジェクト,Request
オブジェクト, またはこれらのオブジェクトの反復可能なものを返します これらのリクエストには, コールバックが含まれ, Scrapyによってダウンロードされ, その後指定されたコールバックによってリクエストが処理されます.コールバック関数では, セレクタ を使用してページの内容を解析します(ただし, BeautifulSoup, lxmlなどの任意のメカニズムを使用することもできます).
最後に, スパイダーから返されたアイテムは, 通常, データベース(一部の アイテムパイプライン 内)に永続化されるか, または フィードのエクスポート を使用してファイルに書き込まれます.
このサイクルはどんな種類のスパイダーにも(多かれ少なかれ)適用されますが, さまざまな種類のデフォルトのスパイダーが Scrapy にバンドルされています. これらのタイプについてはここで説明します.
scrapy.Spider¶
-
class
scrapy.spiders.
Spider
¶ これは最もシンプルなスパイダーで, 他のすべてのスパイダーが継承しなければならないものです(Scrapyにバンドルされたスパイダー, あなた自身で作成したスパイダーを含む). 特別な機能は提供しません.
start_urls
属性からリクエストを送信し, スパイダーのparse
メソッドを, レスポンス結果ごとに呼び出すstart_requests()
メソッドの実装を提供するだけです.-
name
¶ スパイダーの名前を定義する文字列. 名前は, スパイダーが Scrapy によってどのように配置(インスタンス化)されているか判別するために, ユニークでなければなりません. ただし, 同じスパイダーのインスタンスは一つだけ作成可能で, 複数インスタンス化することはできません. これは最も重要なスパイダー属性であり, 必須です.
スパイダーが単一のドメインをスクラップする場合, 一般的には, TLD の有無にかかわらず, ドメイン名と同じの名前を付けます. したがって, たとえば,
mywebsite.com
をクロールするスパイダーには,mywebsite
という名前をつけます.
注釈
Python 2 では, ASCIIのみでなければなりません.
-
allowed_domains
¶ このスパイダーがクロールできるドメインを含む文字列のオプションのリスト.
OffsiteMiddleware
が有効になっている場合, このリスト(またはそのサブドメイン)で指定された ドメイン名に属していないURLに対するリクエストは追跡されません.
-
start_urls
¶ 特定のURLが指定されていない場合, スパイダーがクロールを開始するURLのリスト. したがって, ダウンロードされる最初のページはここにリストされたページになります. 後続のURLは, 開始URLに含まれるデータから順番に生成されます.
-
custom_settings
¶ このスパイダーを実行するときにオーバーライドされるプロジェクトの設定の辞書. インスタンス化前に設定が更新されるため, クラス属性として定義する必要があります.
使用可能なビルトイン設定のリストについては, ビルトイン設定リファレンス を参照してください.
-
crawler
¶ この属性は, クラスを初期化した後の
from_crawler()
クラスメソッドによって設定され, このスパイダーインスタンスがバインドされているCrawler
オブジェクトへのリンクになります.クローラは, 単一エントリアクセス(エクステンション, ミドルウェア, シグナルマネージャなど)のために, プロジェクト内の多くのコンポーネントをカプセル化します. 詳細については クローラー API を参照してください.
-
logger
¶ スパイダーの
name
で作成された Python ロガー. スパイダーからのロギング で説明しているように, これを使ってログメッセージを送信することができます.
-
from_crawler
(crawler, *args, **kwargs)¶ これは Scrapy があなたのスパイダーを作成するために使用するクラスメソッドです.
デフォルトの実装は
__init__()
メソッドへのプロキシとして機能し, 与えられた引数 args と名前付き引数 kwargs を呼び出すので, これを直接オーバーライドする必要はないでしょう.それにもかかわらず, このメソッドは
crawler
とsettings
属性を新しいインスタンスに設定し, 後でスパイダのコード内でアクセスできるようにします.パラメータ:
-
start_requests
()¶ このメソッドは, スパイダーの最初のクロールリクエストで繰り返し可能な値を返す必要があります.
これは, 特定のURLが指定されずにスパイダーを開いてスクレイピングするときに, Scrapy によって呼び出されるメソッドです. 特定のURLが指定されている場合, 代わりに
make_requests_from_url()
がリクエストを作成するために使用されます. このメソッドはScrapyから1回だけ呼び出されるため, 安全にジェネレータとして実装することができます.デフォルトの実装では,
make_requests_from_url()
を使用して,start_urls
内の各URLのリクエストを生成します.ドメインのスクレイピングを開始するために使用されるリクエストを変更したい場合は, これをオーバーライドすることができます. たとえば, 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
-
make_requests_from_url
(url)¶ URLを受け取って,
Request
オブジェクト (またはRequest
オブジェクトのリスト) を返すメソッド. このメソッドは,start_requests()
メソッドで初期リクエストを作成するために使用され, 通常は URL をリクエストに変換するために使用されます.オーバーライドされない限り, このメソッドは, callback関数として
parse()
メソッドを使用し, パラメータを有効にしてリクエストを返します (詳細はRequest
クラスを参照してください).
-
parse
(response)¶ これは, リクエストがコールバックを指定していないときに, ダウンロードされたレスポンスを処理するために Scrapy に使用されるデフォルトのコールバックです.
parse
ソッドは, レスポンスを処理し, スクレイピングされたデータおよび/または, より多くのURLを返すことを担当します. その他のリクエストコールバックはSpider
クラスと同じ要件を持ちます.このメソッドおよび他のRequestコールバックは, イテレータブルな
Request
および/または,dict
または,Item
オブジェクトを返さなければなりません.パラメータ: response ( Response
) – パースするレスポンス
-
log
(message[, level, component])¶ スパイダーの
logger
を介してログメッセージを送信し, 下位互換性を保つために保管されたラッパー. 詳細については スパイダーからのロギング を参照してください.
-
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)
単一のコールバックから複数のリクエストとアイテムを返します:
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').extract():
yield {"title": h3}
for url in response.xpath('//a/@href').extract():
yield scrapy.Request(url, callback=self.parse)
start_urls
の代わりに start_requests()
を直接使うことができます.
アイテム を使用することで多くの構造体にデータを与えることができます:
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').extract():
yield MyItem(title=h3)
for url in response.xpath('//a/@href').extract():
yield scrapy.Request(url, callback=self.parse)
スパイダーの引数¶
スパイダーは行動を変更する引数を受け取ることができます. スパイダー引数の一般的な用途の1つは, 開始URLを定義するか, サイトの特定のセクションにクロールを制限することですが, スパイダーの機能を構成するために使用できます.
スパイダーの引数は, -a
オプションを使用して crawl
コマンドに渡されます. 例えば:
scrapy crawl myspider -a category=electronics
スパイダーは __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]
# ...
スパイダーの引数は, Scrapydの schedule.json
API を介して渡すこともできます.
Scrapyd documentation を参照してください.
一般的なスパイダー¶
Scrapy には, スパイダーをサブクラス化するために使用できる, いくつかの有用なスパイダーがあります. その目的は, 特定のルールに基づいてサイトのすべてのリンクをたどったり, Sitemaps からクロールしたり, XML / CSV フィードを解析するなど, いくつかの一般的なスクラップケースに対して便利な機能を提供することです
以下のスパイダーで使用されているサンプルについては,
myproject.items
モジュールで宣言された TestItem
を持つプロジェクトがあると仮定します:
import scrapy
class TestItem(scrapy.Item):
id = scrapy.Field()
name = scrapy.Field()
description = scrapy.Field()
CrawlSpider¶
-
class
scrapy.spiders.
CrawlSpider
¶ これは定期的なウェブサイトをクロールするために最も一般的に使用されるスパイダーです. 一連のルールを定義してリンクをたどるための便利なメカニズムを提供します. 特定のウェブサイトやプロジェクトには最適ではないかもしれませんが, いくつかのケースでは十分に一般的なので, より多くのカスタム機能のために必要に応じて上書きしたり, 独自のスパイダーを実装することができます.
Spiderから継承した属性(指定する必要がある)を除いて, このクラスは新しい属性をサポートします:
-
rules
¶ 1つ(または複数)の
Rule
オブジェクトのリストです. 各Rule
は, サイトをクロールするための特定の動作を定義します. Rules オブジェクトについては以下で説明します. 複数のルールが同じリンクに一致する場合, この属性で定義されている順序に従って, 最初のリンクが使用されます.
このスパイダーは, オーバーライド可能なメソッドも公開しています:
-
Rule¶
-
class
scrapy.spiders.
Rule
(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)¶ link_extractor
は, クロールされた各ページからリンクを抽出する方法を定義する Link Extractor オブジェクトです.callback
は抽出されたリンクごとに呼び出される, 呼び出し可能または文字列です(この場合, その名前を持つスパイダーオブジェクトのメソッドが使用されます). このコールバックは, 最初の引数としてレスポンスを受け取り,Item
オブジェクトおよび/またはRequest
オブジェクト(またはそれらのサブクラス)を含むリストを返す必要があります.警告
クロールスパイダールールを作成するときは,
CrawlSpider
はparse
メソッド自体を使用してロジックを実装するため,parse
メソッドをコールバックとして使用しないでください.parse
メソッドをオーバーライドすると, CrawlSpider が機能しなくなります.cb_kwargs
は, コールバック関数に渡すキーワード引数を含むdict
です.follow
は, このルールで抽出された各レスポンスからリンクをたどるかどうかを指定する bool 値です.callback
が None の場合,follow
のデフォルトはTrue
になります. それ以外の場合はFalse
にデフォルト設定されますprocess_links
は呼び出し可能です. 指定されたlink_extractor
を使用して各レスポンスから抽出されたリンクのリストごとに呼び出される文字列 (その名前のスパイダーオブジェクトのメソッドが使用されます)です. これは, 主にフィルタリングの目的で使用されます.process_request
は呼び出し可能です. このルールで抽出されたリクエストごとに呼び出され, リクエストを返す必要があります(リクエストをフィルタにかけるには, その名前を持つスパイダオブジェクトのメソッドが呼び出されます).
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 = (
# 'category.php' に一致するリンクを抽出する ('subsection.php' とは一致しません)
# そして, それらのリンクをたどります (なぜならコールバックはデフォルトで follow=True ではないからです).
Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),
# 'allow' と一致するリンクを抽出し, スパイダーの 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()').extract()
item['description'] = response.xpath('//td[@id="item_description"]/text()').extract()
return item
このスパイダーは, example.comのホームページをクロールし, カテゴリリンクとアイテムリンクを収集し,
後者を parse_item
メソッドで解析します.
各アイテムのレスポンスでは, XPathを使用してHTMLからいくつかのデータが抽出され,
Item
にそのデータをいれています.
XMLFeedSpider¶
-
class
scrapy.spiders.
XMLFeedSpider
¶ XMLFeedSpider は, XML フィードを特定のノード名で繰り返し解析するために設計されています. イテレーター は
iternodes
,xml
, 及びhtml
から選択することができます.xml
とhtml
のイテレーターは, 解析するために一度にDOM
全体を生成するので, パフォーマンス上の理由からiternode
イテレーターを使用することをお勧めします. しかし, イテレータとしてhtml
を使用すると, だめなマークアップで書かれたXML
を解析するときに便利です.イテレータとタグ名を設定するには, 次のクラス属性を定義する必要があります:
-
iterator
¶ 使用するイテレータを定義する文字列:
デフォルト:
'iternodes'
.
-
itertag
¶ 反復処理するノード(または要素)の名前の文字列.
例:
itertag = 'product'
-
namespaces
¶ このスパイダーで処理される, 文書で利用可能な名前空間を定義する
(prefix, uri)
タプルのリスト.prefix
とuri
は,register_namespace()
メソッドを使って名前空間を自動的に登録するために使われます.itertag
属性に名前空間を持つノードを指定できます.例:
class YourSpider(XMLFeedSpider): namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')] itertag = 'n:url' # ...
これらの属性とは別に, このスパイダーは次の無効化可能なメソッドも持っています:
-
adapt_response
(response)¶ スパイダーミドルウェアから送られたレスポンスを, スパイダーが解析を開始する前に受け取るメソッド. これは, 解析する前にレスポンス本体の内容を変更するために使用できます. このメソッドはレスポンスを受け取り, レスポンスを返します(同じものか別のものかが返されます).
-
parse_node
(response, selector)¶ このメソッドは, 指定されたタグ名 (
itertag
) と一致するノードに対して呼び出されます. 各ノードのレスポンスとSelector
を受け取ります. このメソッドをオーバーライドすることは必須です. そうしなければ, スパイダーが動作しません. このメソッドは,Item
オブジェクト,Request
オブジェクト, またはいずれかを含む iterable を返す必要があります.
-
process_results
(response, results)¶ このメソッドは, スパイダーから返された各結果(アイテムまたはリクエスト)に対して呼び出され, 結果をフレームワークコアに返す前に必要な最後の処理(たとえば, アイテムIDの設定)を実行するためのものです. これは結果のリストとそれらの結果を生み出したレスポンスを受け取ります. 結果のリスト(アイテムまたはレスポンス)を返す必要があります.
-
XMLFeedSpider の例¶
これらのスパイダーはかなり使いやすいです. 一例を見てみましょう:
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.extract()))
item = TestItem()
item['id'] = node.xpath('@id').extract()
item['name'] = node.xpath('name').extract()
item['description'] = node.xpath('description').extract()
return item
上記は基本的な, 指定された start_urls
からフィードをダウンロードし, それぞれの item
タグを繰り返して印刷し,
ランダムなデータを Item
に格納するスパイダーです.
CSVFeedSpider¶
-
class
scrapy.spiders.
CSVFeedSpider
¶ このスパイダーはXMLFeedSpiderと非常に似ていますが, ノードの代わりに行を反復する点が異なります. 各反復で呼び出されるメソッドは,
parse_row()
です.-
delimiter
¶ CSVファイルの各フィールドの区切り文字を含む文字列. デフォルトは
','
(コンマ).
-
quotechar
¶ CSVファイル内の各フィールドのエンクロージャ文字を含む文字列. デフォルトは
'"'
(引用符)です.
-
headers
¶ ファイルからフィールドを抽出するために使用されるCSVファイルのフィードに含まれる行のリスト.
-
parse_row
(response, row)¶ CSVファイルの各ヘッダーのキーを使用(または検出)して, レスポンスと
dict
(各行を表す)を受け取ります. このスパイダーは, 事前処理と後処理の目的で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を発見することでサイトをクロールできます.
これはネストされたサイトマップをサポートし, robots.txt からサイトマップのURLを見つけることもできます.
-
sitemap_urls
¶ クロールしたいサイトマップを指すURLのリスト.
サイトマップのURLを抽出するために robots.txt を指定することもできます.
-
sitemap_rules
¶ タプル
(regex, callback)
のリスト:regex
は, サイトマップから抽出されたURLに一致する正規表現です.regex
は str かコンパイルされた正規表現オブジェクトのどちらかです.callback
は, 正規表現に一致するURLを処理するために使用するコールバックです.callback
は文字列(スパイダーメソッドの名前を示す)または呼び出し可能なものにすることができます.
例えば:
sitemap_rules = [('/product/', 'parse_product')]
ルールは順番に適用され, 一致する最初のルールのみが使用されます.
この属性を省略すると, サイトマップ内のすべてのURLが
parse
コールバックで処理されます.
-
sitemap_follow
¶ あなたが従うべきサイトマップの正規表現のリスト. これは, 他のサイトマップファイルを指す Sitemap index files を使用するサイトのみに適用されます.
デフォルトでは, すべてのサイトマップに従います.
-
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
は無効化されています.
-
SitemapSpider の例¶
かんたんな例: サイトマップを利用して見つけた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を処理する:
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_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 ...