読者です 読者をやめる 読者になる 読者になる

ITの隊長のブログ

ITの隊長のブログです。いや、まだ隊長と呼べるほどには至っていないけど、日々がんばります。CakePHPとPlayFrameworkを使って仕事しています。最近はAngular2をさわりはじめたお(^ω^ = ^ω^)

pythonのフレームワークの「scrapy」を使ってみた。

Python scrapy

スポンサードリンク

急遽クローラーが必要なことに
何でする!何でする!とキャッキャッヾ(*´∀`*)ノ ウフフしていたところ
久々にPythonを打ちたくなったので、Pythonを選定。


んで、便利なライブラリないかなーって探索していたら
「scrapy」という、フレームワークに出会った。


フレームワークかよ。と思いきや、結構便利そう。
せっかくなんで試してみることに!


環境は


インストール

$ sudo pip install scrapy


おし!入った。

ちな、easy_installでも入れられるっぽい

$ easy_install Scrapy


では、次にどれどれ・・・

あれ・・・?
プロジェクトを作成しなきゃいけないっぽい。Djangoみたいだ。。。


ちなみに、ドキュメントはこちら
Scrapy 0.22 documentation


全くではないが、読めへん。夜間にはキツイ


プロジェクトを作成

$ scrapy startproject aipa-commander
Error: Project names must begin with a letter and contain only
letters, numbers and underscores

ふぁっ!!?
エラーがでた。。。


どうやら、
「プロジェクトの名前には英数字とアンダースコアだけ使っておくれ」
ということらしい
何よ!ケチ!(#`Д’)


まぁ、そんなに困っていないんで、従います。

$ scrapy startproject aipa_commnander


うむ。
んで、ディレクトリ構造を移したいんだけど
どうやら「tree」コマンドがインストールされていなかったようなので
ついで、インストール

$ brew install tree
$ tree aipa_commander/
aipa_commander/
├── aipa_commander
│   ├── __init__.py
│   ├── items.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       └── __init__.py
└── scrapy.cfg

できたできた。
なんか、色々できた。


それぞれのディレクトリ構造はこういう意味があるらしい
※ドキュメントは英語だったので、じゃっくばうわー風に訳しました。
間違ってたら指摘ちょーだい。m(_ _ )m

scrapy.cfg:

作成したプロジェクトの設定ファイルだ。

aipa_commander/:

プロジェクトのモジュールディレクトリだ。
後からモジュールを追加したい場合は、ここに追加してくれ。

aipa_commander/items.py:

わかるだろう?このプロジェクトのアイテムファイルだ。

aipa_commander/pipelines.py:

piplinesファイルだ。
イマイチピンときていない俺は、まだまだってことさ。
(ごめんなさい、マジでわからん)

aipa_commander/settings.py:

settingファイルだ。
たぶんだが、プロジェクトの環境変数はここで追加するんだ(そうなるはずだ)

aipa_commander/spiders/:

spidersディレクトリだ。
今後作成するクローラースクリプトはここにおいてくれ。
ちなみに俺はクモが大嫌いだ。


´・ω・)つ今後、運用して理解でき次第、
上記内容が間違っていたら修正致します。ご了承くださいませ。


さて、ディレクトリ構造をなんとなく理解した上で、次のチュートリアル

Defining our Item
Items are containers that will be loaded with the scraped data; they work like simple python dicts but provide additional protecting against populating undeclared fields, to prevent typos.


(´・ω・)

We begin by modeling the item that we will use to hold the sites data obtained from dmoz.org, as we want to capture the name, url and description of the sites, we define fields for each of these three attributes. To do that, we edit items.py, found in the tutorial directory. Our Item class looks like this:


(´・ωゞ)グシグシグシグシ

This may seem complicated at first, but defining the item allows you to use other handy components of Scrapy that need to know how your item looks like.


なるほど・・・、わからん!!
(`・ω・)クワッ!


よくわからないので、ここからは隊長・ザ・オリジン・翻訳で進めます。

  • aipa_commander/items.py
from scrapy.item import Item, Field

class DmozItem(Item):
    title = Field()
    link = Field()
    desc = Field()


クローラが取得したデータは、デフォルトではJsonデータ形式で取ってくるとか
んで、それぞれ取得したいデータの形はdictに近い形式で取得することができます。
それをitems.pyに記述します。


今回はチュートリアルの形式をまずは試したいので
そのままコピペで持ってきています。

  • aipa_commander/aipa_commander/spiders/dmoz_spider.py
from scrapy.spider import Spider

class DmozSpider(Spider):
    name = "dmoz"
    allowed_domains = ["dmoz.org"]
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
    ]

def parse(self, response):
    filename = response.url.split("/")[-2]
    open(filename, 'wb').write(response.body)

dmoz_spider.pyというファイルを
aipa_commander/aipa_commander/spiders/ディレクトリに作成しました。

「start_urls」にクロールしたい、urlを複数代入
んで、「parse」関数で、urlをsplitしたあとに
書き込みを実施、って感じですかね。


それではクローラーを動かしたいと思います。

# format: scrapy crawl ${classでnameに入れた値}
$ scrapy crawl dmoz
[dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: None)
xxxx-xx-xx xx:xx:xx+xxxx [dmoz] ERROR: Spider error processing <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/>
	Traceback (most recent call last):
	  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/twisted/internet/base.py", line 1178, in mainLoop
	    self.runUntilCurrent()
	  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/twisted/internet/base.py", line 800, in runUntilCurrent
	    call.func(*call.args, **call.kw)
	  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/twisted/internet/defer.py", line 368, in callback
	    self._startRunCallbacks(result)
	  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/twisted/internet/defer.py", line 464, in _startRunCallbacks
	    self._runCallbacks()
	--- <exception caught here> ---
	  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/twisted/internet/defer.py", line 551, in _runCallbacks
	    current.result = callback(current.result, *args, **kw)
	  File "/Library/Python/2.7/site-packages/scrapy/spider.py", line 56, in parse
	    raise NotImplementedError
	exceptions.NotImplementedError:


ゴフッ( ゚∀゚)・∵.
ナンデや。

と、思った瞬間でしたよ。
どうやら、「aipa_commander/aipa_commander/spiders/dmoz_spider.py」の
「def parse()」がインデントされていないもよう。

from scrapy.spider import Spider

class DmozSpider(Spider):
    name = "dmoz"
    allowed_domains = ["dmoz.org"]
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
    ]

        # インデント
	def parse(self, response):
	    filename = response.url.split("/")[-2]
	    open(filename, 'wb').write(response.body)


もっかい。

$ scrapy crawl dmoz
xxxx-xx-xx xx:xx:xx+xxxx [dmoz] INFO: Dumping Scrapy stats:
	{'downloader/request_bytes': 516,
	 'downloader/request_count': 2,
	 'downloader/request_method_count/GET': 2,
	 'downloader/response_bytes': 16515,
	 'downloader/response_count': 2,
	 'downloader/response_status_count/200': 2,
	 'finish_reason': 'finished',
	 'finish_time': datetime.datetime(xxxx, x, xx, xx, xx, xx, xxxxx),
	 'log_count/DEBUG': 4,
	 'log_count/INFO': 7,
	 'response_received_count': 2,
	 'scheduler/dequeued': 2,
	 'scheduler/dequeued/memory': 2,
	 'scheduler/enqueued': 2,
	 'scheduler/enqueued/memory': 2,
	 'start_time': datetime.datetime(xxxx, x, xx, xx, xx, xx, xxxxx)}
xxxx-xx-xx xx:xx:xx+xxxx [dmoz] INFO: Spider closed (finished)

おおっ!できたっぽいお!
やったぜ!


で・・・?

$  tree aipa_commander/
aipa_commander/
├── Books
├── Resources
├── __init__.py
├── __init__.pyc
├── items.py
├── pipelines.py
├── settings.py
├── settings.pyc
└── spiders
    ├── __init__.py
    ├── __init__.pyc
    ├── dmoz_spider.py
    └── dmoz_spider.pyc

BooksとResourcesができあがったとさ。

しかし、これでは、パースした値をどうこうすることはまだできません。
ので、チュートリアルはまだ続きます。

と、、、言いたいところですが、文字数制限に引っかかったようなので
続きは次の記事で。