表題の通り、 Scrapy の start_urls
を外部ファイルから読み込んで設定する方法を書き留めます。
Scrapyのバージョンは 1.0.4
を想定しています。
導入
まず、scrapy コマンドで生成されるプレーンなスパイダーを見てみましょう。
$ scrapy startproject scrapy_sample $ cd scrapy_sample $ scrapy genspider example exapmle.com
こんな感じで scrapy コマンドを叩くと、spiders ディレクトリの中に example.py が生成されます。
scrapy_sample/spiders/example.py
# -*- coding: utf-8 -*- import scrapy class ExampleSpider(scrapy.Spider): name = "example" allowed_domains = ["example.com"] start_urls = ( "http://www.example.com", ) def parse(self, response): url = response.url title = response.xpath("//title/text()").extract_first() yield {"url": url, "title": title}
実際に動かしてみるために、あらかじめ perse()
に少し書き足しています。
よくある例ですが、URL と <title>
を抜き出して、辞書にして返すようにしました。
今回注目したいのは start_urls
の部分ですね。
現状だとスクレイピング対象の URL はハードコーディングされています。
対象 URL をソースコードに直書きしつつ管理したいと思う人はあまり居ないのでしょう。
そんなわけで、この start_urls
に指定する URL を外部ファイルから読み込んであげることにします。
start_urls 設定用ファイルの仕様
仕様といっても簡単なものです、プロジェクトディレクトリの直下に start_urls.txt というテキストファイルを作って、そこから URL を読み込むことにします。
URL は改行で区切ります。つまり、1行に1URLを書いていくスタイルです。
start_urls.txt
http://www.cnn.co.jp/ https://www.yahoo.com/ http://www.reuters.com/
ひとまず、当たり障りのない URL を並べておきます。
コンストラクタを記述
スパイダーの初期設定はコンストラクタの中でするのがふさわしいでしょう。ExampleSpider
クラスの __init__()
を記述します。
親クラスである scrapy.Spider
クラスの __init__()
を呼び出すのも忘れずに、まずはお決まりのコードを書きます。
scrapy_sample/spiders/example.py
# -*- coding: utf-8 -*- import scrapy class ExampleSpider(scrapy.Spider): name = "example" start_urls = ( "http://www.example.com", ) def __init__(self, *args, **kwargs): super(ExampleSpider, self).__init__(*args, **kwargs) # Do something. def parse(self, response): url = response.url title = response.xpath("//title/text()").extract_first() yield {"url": url, "title": title}
このように、
さて、Do something.
の所にテキストファイル読み込みの処理を書いていきましょう。
scrapy_sample/spiders/example.py
# -*- coding: utf-8 -*- import scrapy class ExampleSpider(scrapy.Spider): name = "example" start_urls = [] def __init__(self, *args, **kwargs): super(ExampleSpider, self).__init__(*args, **kwargs) f = open("start_urls.txt") urls = f.readlines() f.close() # 各要素の末尾についた改行文字 "\n" を削除 # 空行("\n" だけの行)も削除 self.start_urls = [url.rstrip("\n") for url in urls if url != "\n"] def parse(self, response): url = response.url title = response.xpath("//title/text()").extract_first() yield {"url": url, "title": title}
1行1URLにしたので、readlines()
を呼ぶだけで URL の配列を取得できます。
self.start_urls
に URL の配列を上書きしてあげればOKです。
ただし、
readlines()
で取得した配列は各要素の末尾に改行文字"\n"
を含んでいる- もしかしたら空行があるかも知れない
という2点を考慮して, リスト内包表記で 末尾の改行文字 と 空行 を取り除く処理をはさんでいます。
ファイル名を settings.py で設定
さて、無事に外部ファイルから start_urls
を読み込むことに成功しましたが、今度は "start_urls.txt"
というファイル名をハードコーディングすることになってしまいました。
せっかくなんで、読み込むファイル名は settings.py
に記述したいですよね。
settings.py
に独自の項目を追加して使う方法は以前の記事で書いた方法をそのまま使います。
まずは settings.py
の末尾に項目を追加しましょう。
scrapy_sample/settings.py
# -*- coding: utf-8 -*- # Scrapy settings for scrapy_sample project # # For simplicity, this file contains only settings considered important or # commonly used. You can find more settings consulting the documentation: # # http://doc.scrapy.org/en/latest/topics/settings.html # http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html # http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html BOT_NAME = 'scrapy_sample' # ... 中略 ... START_URLS = "start_urls.txt"
settings.py
に記述した設定値は crawler.settings
というオブジェクトに格納されます。
この crawler.settings
を ExampleSpider
クラスの中で読めるようにしましょう。
from_crawler()
メソッドを使うとクローラーから値を引き次ぐことができます。
scrapy_sample/spiders/example.py
# -*- coding: utf-8 -*- import scrapy class ExampleSpider(scrapy.Spider): name = "example" start_urls = [] def __init__(self, settings, *args, **kwargs): super(ExampleSpider, self).__init__(*args, **kwargs) # 読み込むファイルは settings.py の START_URLS で設定する f = open(settings.get("START_URLS")) urls = f.readlines() f.close() # 各要素の末尾についた改行文字 "\n" を削除 # 空行("\n" だけの行)も削除 self.start_urls = [url.rstrip("\n") for url in urls if url != "\n"] @classmethod def from_crawler(cls, crawler): # settings.py に記述された全ての設定項目を settings として格納 return cls(settings = crawler.settings) def parse(self, response): url = response.url title = response.xpath("//title/text()").extract_first() yield {"url": url, "title": title}
from_crawler()
の中で cls()
に crawler.settings
を渡してあげます。
それを受け取る __init__()
の側では、第2引数に settings
を追加しています。
これにて __init__()
の中で settings
にアクセスできるようになりました。
ファイル読み込み部分も f = open(settings.get("START_URLS"))
と書き換えています。
実行
$ scrapy crawl example -o result.json
とすれば、結果が result.json
に JSON 形式で保存されます。
result.json
[{"url": "http://www.cnn.co.jp/", "title": "CNN.co.jp"}, {"url": "https://www.yahoo.com/", "title": "Yahoo"}, {"url": "http://www.reuters.com/", "title": "Business & Financial News, Breaking US & International News | Reuters.com"}]
となっているので、上手く動いてくれているようです。
まとめ
Scrapy はシンプルで綺麗なアーキテクチャを持って設計されているフレームワークですが、公式ドキュメントを読むと「使用目的に合わせて好きなように組み替えて使ってくれぃ」というメッセージも感じます。
Scrapy の各クラスとメソッド群があれば、少ない記述で ある程度カスタマイズすることは可能です。
いい感じですね。
私からは以上です。