こんにちはマダラです。
Pythonのスクレイピングに特化したライブラリ、Scrapyを使用したのでその使い方備忘録としてまとめようと思います。
また、クロール先でJavaScriptイベントを実行できるsplashも合わせて併用したのでそちらも書きます。
splashも合わせた日本記事の情報はあまりなかったので、ピンポイントなニーズがあるのではないでしょうか???
scrapyとは
クローラーを実装するためのフレームワーク。
BeautifulSoup や lxml などの HTML パーサーがよく使用されるが、 Scrapy はこれらのライブラリと違うレイヤーのもので、クローラーのアプリケーション全体を実装するためのフレームワークである。
BeautifulSoup や lxml などの HTML パーサーがよく使用されるが、 Scrapy はこれらのライブラリと違うレイヤーのもので、クローラーのアプリケーション全体を実装するためのフレームワークである。
Scrapy はクローラーを実装・運用するために欲しい機能がいろいろ用意されている
-
Items は抽出したいデータ構造のモデル
-
Spider は対象サイトへのリクエストとレスポンスのパーサー
-
Pipeline は抽出したデータに対する加工・保存 (など)
詳しくはQiitaの10分で理解するScrapyも参考になります。
インストール
$ pip install scrapy
プロジェクトの作成
$ scrapy startproject test_scrapy
Spiderの実装
では実際にSpiderを実装してスクレイピングできるようにしていきましょう。
作成されたプロジェクト内にあるItems.pyにスクレイピングしたい要素を設定していきましょう。
すでにサンプルのクラスが設定されていますが、自由に書き換えてしまって大丈夫です。
下記のような感じで追加していきます。
items.py
class TestItem(scrapy.Item):
url = scrapy.Field()
title = scrapy.Field()
ちなみにFieldにはいろいろ設定できて、1個目だけを取得したりタグは排除したり、リストで取ってきたり色々できます。
class TestItem(scrapy.Item):
name = scrapy.Field(
input_processor=MapCompose(remove_tags, str.strip),
output_processor=TakeFirst(),
)
category = scrapy.Field(
input_processor=MapCompose(remove_tags, str.strip),
output_processor=Join(),
)
ではSpiderを追加しましょう。スクレイピングしたいサイトのURLを設定します。
$ cd ./test_scrapy $ scrapy genspider test_spider hogehoge.com
ひな型ファイルはこのようになっています
class ScrapyBlogSpiderSpider(scrapy.Spider):
name = 'test_spider'
allowed_domains = ['blog.scrapinghub.com']
start_urls = ['http://blog.scrapinghub.com/']
def parse(self, response):
pass
start_urls に指定されているurlがクロールされます。
単純に複数クロールしたければ、ここに追加していけばいいですね!
レスポンスを受け取るとparse()メソッドが呼び出されます。
あとはこんな感じでSpiderを実装しましょう
test_spider.py
class ScrapyBlogSpiderSpider(scrapy.Spider):
name = "test_spider"
target = "url末尾"
allowed_domains = ["www.hogehoge.jp"]
start_urls = [f"http://www.hogehoge.jp/{target}"]
def spider_idle(self, spider):
print("クロールが終わったんだぜ〜〜〜ワイルドだろ〜〜〜")
def parse(self, response):
for ranking in response.css("div.クラス名"):
item_loader = ItemLoader(item=TestItem(), selector=ranking)
item_loader.add_css("name", "div.クラス > a::text")
item_loader.add_css("url", "div.クラス > a::attr(href)")
next_url = ranking.css("div.listing_title > a::attr(href)").extract_first()
if next_url is not None:
next_page = response.urljoin(next_url)
yield scrapy.Request(next_page, callback=self.parse_item, meta={"item": item_loader.load_item()})
next_url = response.css("div.pagination > a.next::attr(href)").extract_first()
if next_url is not None:
next_page = response.urljoin(next_url)
yield scrapy.Request(next_page)
self.crawler.signals.connect(self.spider_idle, signal=signals.spider_idle)
def parse_item(self, response):
info = response.css("div#ID名")
item_loader = ItemLoader(item=response.meta["item"], selector=info)
item_loader.add_css("name", "div.クラス名 > div.クラス名.クラス名 > div > span::text")
item_loader.add_css("url", "div.クラス名 > div.クラス名.クラス名 > div > a::attr(href)")
yield item_loader.load_item()
勝手にページングしたり、終了時にお伝えしてくれる処理も入ってます。
スラックかラインにでも通知させてあげると便利ですね。
スラックかラインにでも通知させてあげると便利ですね。
実行です(sql,csv,jsonが使える)
$ scrapy crawl spider名 -o ファイル名.json
まとめ
文字書くのって苦手です。。すごい疲れますね。
やはり私はブロガーには向いていない、、、
JavaScriptを実行できる、つまりクロール中にボタンをクリックしたりできるSplashに関しても書きたかったけど疲れたので、また気が向いたら書きます。
JavaScriptを実行できる、つまりクロール中にボタンをクリックしたりできるSplashに関しても書きたかったけど疲れたので、また気が向いたら書きます。
これができると、通常は隠されている要素も取得できたり、条件を絞ったりできるようになります。
つかれた