内容が古くなっている可能性がありますのでご注意下さい。
ScrapyはWebサイトのクローリングとスクレイピングのための、Pythonで実装されたフレームワークです。スクレイピングはWebページから情報を抽出する技術であり、クローリングはWebページのリンクを辿りながら情報を取得する技術です。
Scrapyを使用して、指定したサイト内の画像ファイルとその画像が貼られたページのURLリストをMySQLのデータベースに格納するWebクローラーを開発します。
ScrapyはPIPコマンドでインストールします。
> pip install Scrapy
Anacondaにはcondaコマンドでインストールすることができます。
> conda install -c conda-forge scrapy
インストールすると、scrapyコマンドが使用可能になります。
> scrapy version Scrapy 2.2.0
はじめに、プロジェクトを作成します。ここでは、プロジェクトの名前は mycrawler とします。
> scrapy startproject mycrawler
以下のようなディレクトリとファイルが作成されます。
mycrawler <DIR> scrapy.cfg mycrawler <DIR> items.py middlewares.py pipelines.py settings.py spiders <DIR> __init__.py __pycache__ <DIR> __init__.py __pycache__ <DIR>
settings.py には、クローラーの動作に関する設定値が記述されています。以下のHTTPキャッシュについては、コメントを外して有効にした方が良いでしょう。
# Enable and configure HTTP caching (disabled by default) # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings #HTTPCACHE_ENABLED = True #HTTPCACHE_EXPIRATION_SECS = 0 #HTTPCACHE_DIR = 'httpcache' #HTTPCACHE_IGNORE_HTTP_CODES = [] #HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
次に、mycrawlerディレクトリに移動し、クローラーのテンプレートを生成します。
> scrapy genspider -t crawl moon moon.midoriit.com
-t オプションでクローラーの種類を選択します。リンクを辿って情報を取得する場合は crawl を指定します。クローラーの名前とクロールするドメインも指定します。ここでは、クローラーの名前は moon、ドメインは月待ビンゴプロジェクトの moon.midoriit.com を指定しています。
spidersディレクトリ下に moon.py が作成されます。
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule class MoonSpider(CrawlSpider): name = 'moon' allowed_domains = ['moon.midoriit.com'] start_urls = ['http://moon.midoriit.com/'] rules = ( Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True), ) def parse_item(self, response): item = {} #item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get() #item['name'] = response.xpath('//div[@id="name"]').get() #item['description'] = response.xpath('//div[@id="description"]').get() return item
ここで、クローラーが収集した情報を格納するテーブルをMySQLのデータベースに作成しておきます。テーブル名は images、カラムは url と referer を可変長文字列で作成します。
自動生成された moon.py を以下のように変更します。
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule import MySQLdb class MoonSpider(CrawlSpider): name = 'moon' allowed_domains = ['moon.midoriit.com'] start_urls = ['https://moon.midoriit.com/'] rules = ( Rule(LinkExtractor(), callback='parse_item', follow=True), ) def parse_item(self, response): conn = MySQLdb.connect(user='scrapy', passwd='scrapy', host='localhost', db='scrapy') sql = 'INSERT INTO images VALUES (%s, %s)' c = conn.cursor() for img in response.xpath('//img/@src').getall(): try: c.execute(sql, (response.urljoin(img), response.url)) except MySQLdb.Error as e: print('MySQLdb.Error: ', e) c.close() conn.commit()
MySQLと接続するために、mysqlclientのインポートを追加します。
import MySQLdb
mysqlclientがインストールされていない場合は、
pip install mysqlclient
でインストールします。
LinkExtractor()の中は空にしています。これは、フィルタリングせずにmoon.midoriit.com内のすべてのリンクを辿るためです。
parse_item()では、MySQLに接続し、response.xpath(‘//img/@src’).getall()でページ内のすべての<img>要素のsrc属性の値を取得し、response.urljoin()で相対パスを絶対パスに変換し、当該ページのURL(response.url)と共に images テーブルに挿入します。
上記ソースのSQL文で、’IINSERT’の’I’が全角になっているのは、ブログ投稿時にWAFでエラーになるのを防ぐためです。もちろん半角の’i’が正しいです。
クローラーの実行は以下のコマンドを使用します。
> scrapy crawl moon
最後の引数は、genspiderで指定したクローラーの名前です。
実行した結果、以下のように50の画像ファイルとページのURLのセットが得られました。
キャッシュを有効にした場合、mycrawler/.scrapy/httpcache ディレクトリ下にファイルが保存されますので、不要になったら削除します。