今、クローリングについて話すと、誰もが多かれ少なかれ Python とクローリングを結びつけるでしょう。根本的には、Python の豊富なエコシステムと柔軟でシンプルな文法によるものです。同時に、Python に基づいていくつかの強力なクローリングフレームワークが存在し、クローリングの難易度を大幅に下げ、プログラムの作成効率を向上させました。最近、私も Scrapy を体験してみたので、入門の記録を残しておきます。
概要#
Scrapy は、ウェブサイトをクローリングし、構造化データを抽出するためのアプリケーションフレームワークであり、データマイニング、情報処理、または歴史的アーカイブなど、さまざまな有用なアプリケーションに使用できます。
—— 公式サイトからの翻訳
直接公式サイトを確認することをお勧めします。そこには完全な Scrapy チュートリアルもあり、フレームワークを深く学ぶための必須の選択肢です!
インストール#
Scrapy フレームワークをインストールする際、インターネット上には多くの方法がありますが、Python の環境によっては奇妙なエラーが発生することがあります。ここでは Python3 環境を基にして、pip を事前にインストールし、実際にテストしてみました。
事前インストール環境
Windows 10 + python 3.7.0 + pip20.1 + virtualenv
他のパッケージの依存関係の衝突を避けるために、新しい virtual 環境を開いてください。
前提条件のインストールコンポーネント
lxml
pyOpenSSL
Twisted
PyWin32
lxml のインストール
直接pip
でインストールできます。これはpython
のHTML
、XML
解析ライブラリで、フレームワークを使用しなくても頻繁に使用されます。
pip3 install lxml
PyWin32 のインストール
公式サイトから対応するバージョンのインストーラをダウンロードしてダブルクリックでインストールします [pywin32]([https://sourceforge.net/projects/pywin32/files/pywin32/Build%20221/](https://sourceforge.net/projects/pywin32/files/pywin32/Build 221/))
残りのコンポーネントのインストール
ここでまずwheel
について紹介します。
wheel
はpython
のパッケージ形式で、以前はpython
の主流のパッケージ形式は.egg
ファイルでしたが、現在は*.whl
ファイルも人気になっています。
wheel
は実際には Python の圧縮パッケージコンポーネントで、zip のようなものですが、この記事では wheel ファイル形式を使用することで、ライブラリを迅速に Python 環境にインストールできることを知っておけば大丈夫です。
インストールは実際にとても簡単です。
pip3 install wheel
これであなたのpython
環境は.whl
ファイル形式のインストールをサポートします。
次のステップは、各公式サイトから各コンポーネントのwhl
形式をダウンロードすることです。注意して、あなたの Python 環境に合わせてください。
インストール
pip3 install pyOpenSSL-19.1.0-py2.py3-none-any.whl
Twisted
あなたの Python バージョンに対応していることを確認してください。
私の環境では次のようになります。
pip3 install Twisted-20.3.0-cp37-cp37m-win_amd64.whl
Scrapy のインストール
すべての依存パッケージが正常にインストールされた後、直接 pip で Scrapy をインストールすれば問題ありません。
pip3 install Scrapy
コンポーネントの紹介#
まずプロジェクトを作成します。
クローリングプロジェクトを配置したいフォルダで実行します。xxx はあなたのプロジェクト名です。
scrapy startproject xxx
基本的な操作をいくつか記録しておきます。
- プロジェクトの作成:
scrapy startproject xxx
- プロジェクトに入る:
cd xxx #特定のフォルダに入る
- クローラーを作成:
scrapy genspider xxx(クローラー名) xxx.com (クローリングドメイン)
- ファイルを生成:
scrapy crawl xxx -o xxx.json (特定のタイプのファイルを生成)
- クローラーを実行:
scrapy crawl XXX
- すべてのクローラーをリスト:
scrapy list
- 設定情報を取得:
scrapy settings [options]
作成が完了すると、フォルダ内にこれらの内容が追加されているのが見えます。
これらのコンポーネントを一つずつ紹介します(spider_demo
はあなたのクローリングプロジェクト名です)。
scrapy.cfg
: プロジェクトの設定ファイル(プロジェクトフォルダの平行ディレクトリにあります)spider_demo/spiders/
:spider
コードを配置するディレクトリ(クローラーのロジックを配置する場所)spider_demo/items.py
: プロジェクト内のitem
ファイル(コンテナを作成する場所であり、最終的にクローラーが得る結果の形式を定義する場所)spider_demo/pipelines.py
: プロジェクト内のpipelines
ファイル(データのクレンジング、保存、検証を実現)spider_demo/settings.py
: プロジェクトの設定ファイル(クローラーの具体的な設定、特定のミドルウェアの起動や機能のオンオフなど)spider_demo/middlewares.py
: プロジェクトのダウンローダーミドルウェアとクローラーミドルウェアを定義
まだ少し混乱しているかもしれませんが、次にscrapy
の動作原理を簡単に紹介します。これにより、これらのコンポーネントの役割をより理解できると思います。
公式サイトのフローチャート
Scrapy
は実行エンジンによって制御されます。
Spider
がEngine
にリクエストを送信します。Engine
がリクエストをScheduler
に割り当て、次のクローリングリクエストを受け取ります。Scheduler
が次のリクエストを返します。Engine
がリクエストをDownloader Middlewares
を介してDownloader
に送信します。Downloader
がウェブページをクローリングし、結果をDownloader Middlewares
を介してEngine
に返します。- エンジンが応答を受け取り、
Spider Middleware
を介してSpider
に転送して処理します。 Spider
のparse()
メソッドが取得したresponse
を処理し、items
またはリクエストを解析し、解析されたitems
またはリクエストをEngine
に返します。Engine
がitems
をItem Pipeline
に送信し、リクエストをScheduler
に送信します。- 新しいリクエストがなくなるまでステップ 1 を繰り返します。
上記のステップで出現するコンポーネントをまとめると、
コンポーネント名 | コンポーネント機能 | |
---|---|---|
Engine | フレームワークのコア、全体のデータと信号のスケジューリングを担当 | フレームワーク実装 |
Scheduler | リクエストを格納するキュー | フレームワーク実装 |
Downloader | 具体的なダウンロードタスクを実行するユニット | フレームワーク実装 |
Spider | ダウンロードした応答結果を処理し、必要なデータを抽出する(具体的なビジネスロジック) | 自分で実装 |
Item Pipeline | 最終的に得られたデータを処理し、永続化操作を行う | 自分で実装 |
Downloader Middlewares | 本格的にダウンロードタスクを実行する前に、カスタム処理を行うことができる。リクエストヘッダーの設定やプロキシの設定など | 自分で実装 |
Spider Middlewares | リクエストをカスタマイズし、応答をフィルタリングする | 自分で実装 |
この一連の組み合わせで、このフレームワークに対する基本的な理解が得られたと思います。次は実戦を通じて記憶を強化しましょう。
簡単なアプリケーション#
今回は、インターネット上で猫眼映画のランキングをクローリングするデモを作成しました。今後、時間があれば、より複雑なデモに変更します。
実現目標は、映画ランキングのタイトル、スコア、順位をクローリングし、結果を.json
ファイルにjson
形式で保存することです。
まず、クローリングするデータを決定し、必要なデータをコンテナに記録します。item.py
で次のように記述します:
import scrapy
#ここでは順位、タイトル、評価人数、公開時間、スコアが必要です
class SpiderDemoItem(scrapy.Item):
# アイテムのフィールドをここで定義します:
# name = scrapy.Field()
index = scrapy.Field()
title = scrapy.Field()
star = scrapy.Field()
releasetime = scrapy.Field()
score = scrapy.Field()
次に、Spiders
フォルダ内に新しいクローラーファイルを作成します。例えば、MoyanSpider.py
というファイルを新しく作成しました。
import scrapy
from spider_demo.items import SpiderDemoItem
class MaoyanSpider(scrapy.Spider):
# これはクローラーの起動名で、後でクローラーを起動する際に使用します
name = "maoyan"
# クローリング可能なドメインの選択リスト
allowed_domains = ["maoyan.com"]
# 目標クローリングURL
start_urls = [
"http://maoyan.com/board/7/",
"http://maoyan.com/board/4/",
]
# ダウンロード済みのページを処理します
def parse(self, response):
dl = response.css(".board-wrapper dd")
# 解析を通じて具体的なデータをコンテナに格納します
for dd in dl:
item = SpiderDemoItem()
item["index"] = dd.css(".board-index::text").extract_first()
item["title"] = dd.css(".name a::text").extract_first()
item["star"] = dd.css(".star::text").extract_first()
item["releasetime"] = dd.css(".releasetime::text").extract_first()
score = dd.css('.integer::text').extract_first()
if score is not None:
item["score"] = score + dd.css('.fraction::text').extract_first()
else:
item["score"] = 0
# yieldを通じて結果を返します
yield item
ここで、scrapy
はさまざまなタイプの解析をサポートしており、Python の一般的な三大解析ライブラリを使用して解析できますが、フレームワークも独自の解析方法(Selector
)を提供しています。
- セレクタ
Xpath
ここでは詳細には述べませんが、後で時間があれば詳しく話しましょう。
同時に、setting.py
で設定を少し変更する必要があります(setting.py
には多くのデフォルト設定がありますが、ここでは変更部分のみを示します)。
# 自動生成されたUAがない場合は手動で定義する必要がありますが、毎回同じUAでクローリングすると検証操作が発生しやすいため、後でUAをランダム生成する方法も紹介します。
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
# ロボットプロトコルの許可、ロボットプロトコルの具体的な内容は自分でインターネットで調べてください。
ROBOTSTXT_OBEY = False
これで、基本的なクローラーが完成しました。実行するにはscrapy crawl maoyan
(最後の部分はあなたのクローラー名)を実行するだけです。
その後、2 つの重要なポイントがあります。一つはプロジェクトの永続化、もう一つはUser-Agent
のランダム化です。
まず永続化を見てみましょう。ここでは簡単のため、クローラーのデータをjson
形式でエクスポートする方法を示します。
ここでpipline.py
を変更する必要があります。なぜなら、以前のコンポーネントの紹介を見れば理解できると思います。
import json
import codecs
class SpiderDemoPipeline:
def process_item(self, item, spider):
return item
class JsonPipline(object):
def __init__(self):
print("ファイルを開いて書き込みの準備をします....")
self.file = codecs.open("maoyan.json", "wb", encoding='utf-8')
def process_item(self, item, spider):
print("書き込みの準備をしています...")
line = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(line)
return item
def close_spider(self, spider):
print("書き込み完了、ファイルを閉じます")
self.file.close
次に、setting.py
でカスタムpipline
を有効にします。
ITEM_PIPELINES = {
# 'spider_demo.pipelines.SpiderDemoPipeline': 300,
'spider_demo.pipelines.JsonPipline': 200
}
User-Agent
のランダム化については、UA を追加する原理を説明します。
scrapy
は最初にsetting.py
内の UA 設定を読み込み、その後middleware
を通過します。カスタム操作を行わなければ、設定された UA がリクエストヘッダーに追加されます。したがって、UA をランダム化するには、実際にはウェブリクエストを発起する前にDownload Middleware
で処理を行うことができます。
ここでmiddleware.py
を変更し、サードパーティパッケージfake_useragent
を導入しました。
from scrapy import signals
import random
from fake_useragent import UserAgent
class RandomUserAgentMiddleware(object):
# User-Agentをランダムに変更します
def __init__(self, crawler):
super(RandomUserAgentMiddleware, self).__init__()
self.ua = UserAgent()
self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random")
@classmethod
def from_crawler(cls, crawler):
return cls(crawler)
def process_request(self, request, spider):
def get_ua():
return getattr(self.ua, self.ua_type)
request.headers.setdefault('User-Agent', get_ua())
同時に、setting.py
でも変更を行います。
DOWNLOADER_MIDDLEWARES = {
'spider_demo.middlewares.SpiderDemoDownloaderMiddleware': 543,
'spider_demo.middlewares.RandomUserAgentMiddleware': 400,
'scrapy.downloadermiddleware.useragent.UserAgentMiddleware': None,
}
# UAをランダムに選択します
# これは自分で設定したもので、fake-useragentに依存しています
RANDOM_UA_TYPE = 'random'
これで、簡単なクローラーアプリケーションが実現しました!
UA が変更されたのが見えます。
同時にmaoyan.json
ファイルが生成されました。