Help us understand the problem. What is going on with this article?

DjangoとCeleryでクローラを書きながら考えた由無し事

More than 3 years have passed since last update.

業務でもクローラを見ることが多いのですが、今回は新しくクローラ作るならとりあえずDjango+Celery使うだろうなぁということで、個人でサンプル書いて見つつ思ったところをつらつらと書いてみようと思います。

クローラの基本的な処理段階

クローラといっても人によって微妙に定義が違う状態で会話する事が多いのでとりあえず今回の対象は、

  • 一定の条件に従ってWebページのリンクを収集する
  • 各リンク内のページ内容をスクレイピングする
  • スクレイピングした後の処理については、今回は対象外とする

という前提でお話します。

今回のサンプルの構成

y-matsuwitter/django_sample_crawler

今回サンプル書く上での構成ですが、

  • Python 3.4.3
  • Django 1.9
  • Celery 3.1.19
  • lxml 3.5.0
  • requests 2.8.1

を利用し、下記を実施するものを作成しました。

  • 複数サーバにスケール可能
  • githubのtrendingなrepositoryをクロールする
  • クロール条件をあとから追加可能
  • 一定以上のペースでGithubにアクセスをかけない
  • リトライのしやすさ

Celeryによる各処理の分散実行

クローラを長期間運用していると、クローラの処理の一部のみが肥大化し遅くなることがあります。
例えば、コンテンツの抽出部でCPUをほぼ使い果たしてしまうなど。
この時にこの処理のみ分離する、といった対応を入れねばなりませんが、こうした分散運用を簡単にするには最初からMessageQueue系のシステムに乗っかっておく必要があるかと思います。
今回はこのレイヤの処理にCeleryを用いております。
今回のgithub trendingの取得の内部の処理としては、

スクリーンショット 2015-12-05 22.01.44.png

の順序で実行しています。
各過程を別々なceleryのtaskとして扱っており、workerの設定を調整すれば、処理ごとに別のサーバクラスタを割り当てられます。
ちなみに、今回の適当なクローラに対しては大仰な感じの分割ですが、各処理をtaskとして別々に指定することで、とあるタスクが失敗した際にretry対応が楽になります。例えば、trendingページのダウンロードをしたものの、パース段階でルールに不備があり止まってしまった場合、対象のtaskのみ修正して、再度そのtaskのみ流せば回復しますし、またそのレイヤでタスクが詰まっててもそこのレイヤを簡単にリソース追加することが出来ます。

periodic taskによるCronの代替

Celeryにはperiodic taskという仕組みがあり、定期実行するタスクをperiodic_taskとして指定することもできます。今回は一番基本にある各ルールのクローラを動作させる部分をscheduleで指定しています。

from celery.schedules import crontab


CELERYBEAT_SCHEDULE = {
    'run_all_crawler_schedule': {
        'task': 'crawler.tasks.run_all_crawler',
        'schedule': crontab(minute='*/2'),
    },
}

ちなみに二分ごとに実行になってるのはデバッグで適当に書いていただけなのでもし本当に運用する場合はもう少し間隔は空けます。

rate_limit

サイトを細かくスクレイピングする場合、htmlの取得処理で対象サイトに負荷をかけかねません。
celeryにはrate_limitという仕組みがあり、taskごとにどれくらいの頻度で実行するか指定することができます。

@celery_app.task(rate_limit="60/m")
def fetch_trending(crawler_id):
    pass

上記で1分に60回以上の動作をしないよう制約をかけることが出来ます。

Djangoと組み合わせるメリット

Celeryだけでも十分普通のクローラは作成できるのですが、長期間の運用ではルールやサイトの追加がどんどん膨らんでいくので、こうしたルールの追加などをDBに入れつつ、状況をWeb側の管理画面で確認するなど考慮するとDjangoとの組み合わせが便利になってきます。
今回は非常にシンプルなものなので、クローラのルールはtrendingの取得ルール程度のものになりますが、こうした情報をdjango adminで設定することができます。

あとからルールを追加する場合に、おそらく最も問題になるのはスクレイピングのルール周りで、これはxpathやcss selectorをルールとして保存するような方法で対応できるかと思います。
Scrapyの仕組みを参考にするのもいいかもしれません。

最後に

比較的大きめな規模のクローラを運用する知見があまりネット上にないような気もするので、この辺の知見を共有する勉強会とかしたいですね。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away