F.A.Q.(よくある質問と回答)¶
ScrapyはBeautifulSoupやlxmlと比較してどうですか?¶
BeautifulSoup と lxml は、HTMLとXMLを解析するためのライブラリです。 Scrapyは、Webサイトをクロールし、そこからデータを抽出するWebスパイダーを作成するためのアプリケーションフレームワークです。
Scrapyはデータを抽出するための組み込みメカニズムを提供します( セレクター とよばれます)が、より快適に作業できる場合は、代わりに BeautifulSoup (または lxml )を簡単に使用できます。結局のところ、それらは任意のPythonコードからインポートして使用できるライブラリを利用しているだけです。
いいかえると、 BeautifulSoup (または lxml )とScrapyを比較することは、 jinja2 と Django を比較するようなものです。
ScrapyでBeautifulSoupを使用できますか?¶
はい、できます。 上記 のように、 BeautifulSoup はScrapyコールバックでHTMLレスポンスをパースするために使用できます。 レスポンスのボディを BeautifulSoup
オブジェクトに送り、必要なデータを抽出するだけです。
HTMLパーサーとして lxml
を使用して、BeautifulSoup API を使用するスパイダーの例を次に示します:
from bs4 import BeautifulSoup
import scrapy
class ExampleSpider(scrapy.Spider):
name = "example"
allowed_domains = ["example.com"]
start_urls = (
'http://www.example.com/',
)
def parse(self, response):
# use lxml to get decent HTML parsing speed
soup = BeautifulSoup(response.text, 'lxml')
yield {
"url": response.url,
"title": soup.h1.string
}
注釈
BeautifulSoup
はいくつかのHTML/XMLパーサーをサポートしています。利用可能なものについてはBeautifulSoupの公式ドキュメント(BeautifulSoup's official documentation) を参照してください。
ScrapyはどのPythonバージョンをサポートしていますか?¶
ScrapyはCPython(デフォルトのPython実装)での Python 2.7 および Python 3.5+ と、PyPy(PyPy 5.9以降) でサポートされています。Python 2.6のサポートはScrapy 0.20以降は削除されました。Python 3のサポートはScrapy 1.1で追加されました。 PyPyサポートはScrapy 1.4で追加され、PyPy3サポートはScrapy 1.5で追加されました。
注釈
WindowsでPython 3をサポートするには、 インストールガイドで概説 しているように、Anaconda/Miniconda を使用することをお勧めします。
ScrapyはDjangoからhogehogeを盗んだ?¶
たぶん。だけど、私たちはそういう言い方はしないな。 Django は素晴らしいオープンソースプロジェクトであり、従うべき例であると考えているため、Scrapyの着想を得るのに利用したんだよ。
車輪の再発明する必要はないという信念です。この信念は、オープンソースおよびフリーソフトウェアの基礎の1つであることに加えて、ソフトウェアだけでなく、ドキュメント、手順、ポリシーなどにも適用されます。したがって、各問題を自分で進めるのではなく、それらのプロジェクトからアイデアをコピーすることを選択します それが既に各問題を適切に解決しているので、私たちは解決する必要がある実際の問題に焦点を当てる事ができます。
私たちはScrapyが他のプロジェクトのインスピレーションとして役立つことを誇りに思います。 じゃんじゃん盗め!
ScrapyはHTTPプロキシ経由で動作しますか?¶
はい。 HTTPプロキシのサポートは、HTTPプロキシ・ダウンローダー・ミドルウェアを通じて提供されます(Scrapy 0.8以降)。 HttpProxyMiddleware
を参照してください。
異なるページの属性を持つアイテムをスクレイピングするにはどうすればよいですか?¶
Scrapyがクラッシュします。「ImportError: No module named win32api」¶
あなたは「このツイストバグ」(this Twisted bug)のため、 pywin32 をインストールする必要があります。
スパイダーでユーザーログインをシミュレートするにはどうすればよいですか?¶
Scrapyは幅(breadth)優先または深さ(depth)優先でクロールしますか?¶
デフォルトでは、Scrapyは保留中のリクエストを保存するために LIFO キューを使用します。これは基本的に、DFO順序(DFO order)でクロールすることを意味します。 ほとんどの場合、この順序の方が便利です。
あなたが本当にBFO順(BFO order)でクロールしたい場合は、次の設定を行うことで実行できます:
DEPTH_PRIORITY = 1
SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue'
保留中のリクエストが CONCURRENT_REQUESTS
または CONCURRENT_REQUESTS_PER_DOMAIN
または CONCURRENT_REQUESTS_PER_DOMAIN
の設定値を下回っている間、これらのリクエストは同時に送信されます。その結果、クロールの最初のいくつかのリクエストが目的の順序に従うことはほとんどありません。 これらの設定を 1
に下げると、目的の順序が強制されますが、クロール全体が大幅に遅くなります。
Scrapyクローラーにメモリリークがあります。 何か私にできる事がありますか?¶
メモリ・リークのデバッグ 参照。
また、Pythonには Scrapyではリークしてないのにリークしてるorz で説明されている組み込みのメモリリークの問題があります。
Scrapyが消費するメモリを減らすにはどうすればよいですか?¶
1つ前の質問を見て下さい。
スパイダーは基本HTTP認証を使用できますか?¶
はい。 HttpAuthMiddleware
参照。
Scrapyが母国語ではなく英語でページをダウンロードするのはなぜですか?¶
DEFAULT_REQUEST_HEADERS
設定をオーバーライドして、デフォルトの Accept-Language リクエスト・ヘッダーを変更してみてください。
プロジェクトを作成せずにスパイダーを実行できますか?¶
はい。 runspider
コマンドを使用できます。たとえば、 my_spider.py
ファイルにスパイダーが記述されている場合は、次のコマンドで実行できます:
scrapy runspider my_spider.py
詳細については runspider
を参照してください。
"Filtered offsite request"(フィルターされたオフサイト要求)メッセージが表示されます。 どうすれば修正できますか?¶
これらのメッセージ(DEBUGレベルでログに記録される)は、必ずしも問題があることを意味するわけではないため、修正する必要はありません。
これらのメッセージは、オフサイト・スパイダー・ミドルウェアによって送出されます。オフサイト・スパイダー・ミドルウェアは、スパイダーの対象外のドメインへのリクエストをフィルター処理することを目的とするスパイダー・ミドルウェア(デフォルトで有効)です。
詳細は OffsiteMiddleware
を参照して下さい。
Scrapyクローラーを運用環境に展開する推奨方法は何ですか?¶
スパイダーのデプロイ 参照。
大規模なエクスポートにJSONを使用できますか?¶
出力の大きさに依存します。 JsonItemExporter
文書の 警告 を参照してください。
シグナルハンドラーから(Twisted)遅延(deferred)を返すことができますか?¶
ハンドラーからの遅延(deferred)を返すことをサポートするシグナルもあれば、サポートしないシグナルもあります。 組み込みシグナル・リファレンス を参照して、どれがどれか確認してください。
レスポンス・ステータス・コード999の意味は何ですか?¶
999は、リクエストを抑制するためにYahooサイトで使用されるカスタム・レスポンス・ステータス・コードです。スパイダーで 2
(またはそれ以上)のダウンロード遅延を使用して、クロール速度を遅くしてみてください:
class MySpider(CrawlSpider):
name = 'myspider'
download_delay = 2
# [ ... rest of the spider code ... ]
または、 DOWNLOAD_DELAY
設定でプロジェクトのグローバル・ダウンロード遅延を設定します。
スパイダーから pdb.set_trace()
を呼び出してデバッグできますか?¶
はい。ただし、スパイダーによって処理されているレスポンスをすばやく分析(および変更)できるScrapyシェルを使用することもできます。これは、通常の pdb.set_trace()
よりも非常に便利です。
詳細は スパイダーからシェルを呼び出してレスポンスを検査する を参照して下さい。
スクレイピングしたすべてのアイテムをJSON/CSV/XMLファイルにダンプする最も簡単な方法は?¶
JSONファイルにダンプするには:
scrapy crawl myspider -o items.json
CSVファイルにダンプするには:
scrapy crawl myspider -o items.csv
XMLファイルにダンプするには:
scrapy crawl myspider -o items.xml
詳細は フィード・エクスポート を参照して下さい。
いくつかのフォームで使用されているこの巨大な __VIEWSTATE
パラメーターは何ですか?¶
__VIEWSTATE
パラメーターは、ASP.NET/VB.NETで構築されたサイトで使用されます。動作の詳細については、http://search.cpan.org/~ecarroll/HTML-TreeBuilderX-ASP_NET-0.09/lib/HTML/TreeBuilderX/ASP_NET.pm を参照してください。また、これらのサイトの1つをスクレイピングする example spider もあります。
大きなXML/CSVデータ・フィードを解析する最良の方法は何ですか?¶
XPathセレクターを使用して大きなフィードを解析すると、フィード全体のDOMをメモリに構築する必要があるため問題が発生する可能性があります。これは非常に遅く、大量のメモリを消費する可能性があります。
メモリ内のフィード全体を一度に解析することを避けるために、 scrapy.utils.iterators
モジュールの関数 xmliter
と csviter
を使用できます。 実際、これはフィード・スパイダー( スパイダー 参照)が内部で使用しているものです。
Scrapyはクッキーを自動的に管理しますか?¶
はい、Scrapyはサーバーから送信されたクッキーを受信して追跡し、通常のWebブラウザーが行うように、後続のリクエストでそれらを送り返します。
詳細は リクエストとレスポンス と CookiesMiddleware を参照下さい。
Scrapyとの間で送受信されているCookieを確認するにはどうすればよいですか?¶
COOKIES_DEBUG
設定を有効にします。
スパイダーに自分自身を止めるように指示するにはどうすればよいですか?¶
コールバックから CloseSpider
例外を発生させます。 詳細については、 CloseSpider
を参照してください。
Scrapyボットがバン(BAN)されるのを防ぐにはどうすればよいですか?¶
バン(拒否)されるのを避ける 参照。
スパイダーを設定するには、スパイダーの引数または設定を使用する必要がありますか?¶
スパイダー引数 と 設定 の両方を使用して、スパイダーを設定できます。どちらか一方を使用することを義務付ける厳密なルールはありませんが、設定は一度設定するとあまり変化しないパラメーターに適しています。一方、スパイダーの引数はスパイダーの実行ごとに頻繁に変更されることを意図しており、スパイダーを実行するには(たとえば、スパイダーの開始URLを設定するためなど、)どうせ必要になります。
例で説明するために、データをスクレイピングするためにサイトにログインする必要があるスパイダーがあり、(毎回異なる)サイトの特定のセクションからのみデータをスクレイピングしたいとします。 その場合、ログインする資格情報は設定になり、スクレイピングするセクションのURLはスパイダー引数になります。
XMLドキュメントをスクレイピングしていますが、XPathセレクターはアイテムを返しません¶
名前空間を削除する必要がある場合があります。 名前空間(namespace)の削除 参照。
アイテム・パイプラインでアイテムを複数のアイテムに分割する方法は?¶
アイテム・パイプライン は、入力アイテムごとに複数のアイテムを生成できません。 代わりに スパイダー・ミドルウェア を作成し、この目的で process_spider_output()
メソッドを使用します。例えば以下です:
from copy import deepcopy
from scrapy.item import BaseItem
class MultiplyItemsMiddleware:
def process_spider_output(self, response, result, spider):
for item in result:
if isinstance(item, (BaseItem, dict)):
for _ in range(item['multiply_by']):
yield deepcopy(item)