PyConTW 2017 TALK 用 Django 建立一個可以設定爬蟲排程任務與監控的網站

2017-06-11, YiChieh Chen(Jason)

用 Django 建立一個可以設定爬蟲排程任務與監控的網站

2017-06-11, YiChieh Chen(Jason)

Hands on the orange typewriter in a park
© YiChieh Chen

About Me

Outline

Story as background

公司與工廠間再生產產品期間時需要經常即時的傳遞零組件資料,這些資料存在品牌公司的資訊系統

這些資料需要定期人工下載、比對,但是品牌公司會基於某些原因與理由不開放 API 讓系統之間串接,因此爬蟲成了資料下載的管道。

一個工廠不會只生產一種產品,每種產品的零組件也不會只有幾十種。系統雖提供同一種下載模式(在同網站),但很多名稱不一定有規則,所以每個爬蟲程式多多少少需要一些客製化。然後...然後...然後...

	            try:
	                driver.get("https://{}")
	                content = wait.until(lambda driver:
driver.find_element_by_id("accountname"),"link fail")
wait.until(lambda driver: driver.find_element_by_id
("accountname"),"FAIL to find account")
driver.find_element_by_id("accountname").send_keys("{}") driver.find_element_by_id("accountpassword").send_keys("{}") driver.find_element_by_id("continueFieldbutton").click()

BAD IDEAL

設定排程

crontab
bang
law-in-shit

Story as background

電話響鈴聲~

對方:資料又沒抓到啦

對方:你什麼時候可以修好!!

對方:我現在要資料!!

對方:掛斷

wtf

排程任務
工具與框架

  1. 首先先講一點點關於 Directed Acyclic Graph ( DAG )的知識。
  2. 有些事情會有先後關係,舉例資料結構內用 Graph 來表示,vertex(穿襪子)、vertex(穿鞋子)與edge(先後關係):
  3. 時間軸當作主角,緣起緣滅、緣聚緣散,凡事都是 DAG
graph1

這樣的 Graph 顯示在穿鞋子之前必須先套上襪子。

graph2

Topological Sort (拓撲排序)

graph3

edge(2,6)、edge(6,9),那麼 Topological Sort 中,vertex(2) 一定要出現在 vertex(6) 之前,vertex(6) 一定要在 vertex(9) 前

實現一個排程的任務,就是要實作一個 DAG。 而且有很多使用 Python 開發的 workflow system (有國外大大稍微整理一下,大約<130>個)。天啊!

Dagobah

dagobah

Luigi

luigi

Pinball

pinball

Airflow

airflow

Luigi vs Airflow vs Pinball

Luigi Airflow Pinball
github stars 4029 1798 506
calendar scheduling no, use cron yes, LocalScheduler yes
web dashboard minimal nice yes
multiple dags no, just one yes yes
rayfun

建立一個
排程任務網站

RequirementS

  1. 簡單整合任務排程(整合起來不複雜)

Django 的套件從那找?

有一個挺好用的網站叫 Django Packages 網羅了許多 Django 套件,其中有一個分類叫:Workers, Queues, and Tasks 有列出很多相關的套件。

djpkg

Pictures

pkglist

等等,你是不是還忘記些什麼?Celery 大大呢?

Django-Q 簡介與設定方式

Django Q is a native Django task queue, scheduler and worker application using Python multiprocessing.

Django Q is tested with: Python 2.7 & 3.6. Django 1.8.18 LTS, 1.10.7 and 1.11

Feature

Feature

安裝與設定方式

Install the latest version with pip:
$ pip install django-q

Add django_q to INSTALLED_APPS in your projects settings.py: INSTALLED_APPS = (
# other apps
'django_q',
)

安裝與設定方式

Run Django migrations to create the database tables:
$ python manage.py migrate
Choose a message broker , configure it and install the appropriate client library.
Q_CLUSTER = { 'name': 'DjangORM',
'workers': 1,
'timeout': 1800, 'retry': 120, 'queue_limit': 50, 'bulk': 10, 'orm': 'default'
}

安裝與設定方式

Run Django Q cluster in order to handle tasks async:
$ python manage.py qcluster

基本上就完成所有 Django-Q 的設定了。接者讓 Django wsgi server 跑起來。
$ python manage.py runserver

在瀏覽器輸入 localhost:8000/admin, 登入後看到 Django-Q 就成功了。簡單吧。

django-admin

開始來
寫爬蟲吧

首先我想把爬蟲都放在 crawler 這個 app之下:
$ python manage.py startapp crawler

這個 app 就專門負責爬蟲任務,接著建立一個 crawler.py 的程式。爬蟲通常會用到幾個套件 requests, selenium, BeautifulSoup 都先裝起來。
$ pip install requests, selenium, BeautifulSoup4 安裝完成之後我們就可以開始來寫 crawler.py,crawler.py 目的就是將我們想做的爬蟲功能都做進去。

Code samples

            class Crawler(object):
              
            def __init__(self, *args, **kwargs):
                self.BASE_DIR = settings.BASE_DIR
                ...
            def driver(self):
                ...
        

Code samples

            def test(url):
                PHANTOMJS='{path}'
                crawler = Crawler(driver_path=PHANTOMJS):
                driver = crawler.driver()
                driver.get(url)
                pageSource = driver.page_source
                soup = bs(pageSource, "html.parser")
                driver.close()
                return soup
        

Code samples

隨便爬個網頁,得到一些結果。就完成了爬蟲程式。

            if __name__ == '__main__':
                logging.basicConfig(level=logging.INFO,
        format='%(asctime)s:%(name)s:%(levelname)s:%(message)s')
                print(test(url='http://pala.tw/js-example/'))
        

接著
來完成 Tasks

/crawler/ 下建立一個 tasks.py 這個檔案將用來撰寫與管理我們的任務。接著我們把前面用來測試的程式碼改個名稱 def crawler_job(url)加到 tasks.py 內,就完成第一隻 task。

接著進入到 admin 的後台,新增一個 schedule task 測試看看。

schedule_tasks

接著輸入這個 task 需要的參數,與設定重複次數、執行時間等等

schedule_tasks_2

$ python manage.py qcluster 的介面會顯示執行時狀態

process

Scheduled tasks 設定成功的畫面,最後一欄會顯示目前執行結果的狀態。

schedule_tasks_3

Successful tasks 會顯示執行成功的 Tasks

schedule_tasks_4

Failed tasks 則會顯示執行不成功的 Tasks

schedule_tasks_5

前端頁面
設計

希望有一個頁面可以即時的看到任務正在執行,和已經結束的任務。還能點進去觀看任務詳細執行歷程。

tasks

每個 task 記錄每個細節。

log

首先在 crawler/views.py 新增一個 crawler(request)取的所有 tasks

            def crawler(request):
                queue_crawler = OrmQ.objects.all().order_by('lock')
                complete_crawler = Task.objects.all().filter(
                    func__exact='crawler.tasks.crawler_job',
                )
                return render(request, 'crawler/tasks_crawler.html', {
                    'queue_crawler': queue_crawler,
                    'complete_crawler': complete_crawler
                })
        

view_task(request, id) 取得每個 task 的 detail

            def view_task(request, id):
                task = get_object_or_404(Task, id=id)
                result = task.result
                if task.func == 'crawler.tasks.crawler_job':
                    return render(request, 'crawler/tasks_detail.html', {
                        'result': result,})
                return Http404(
                'Given task of type %s does not have the....'
                % task.func)
        

介紹一個好用的 logging 套件,lazy_logger

lazy_logger

Demo

Shower logo See more on GitHub