iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0
生成式 AI

一起來打造 PTT 文章智慧問答系統!系列 第 12

【Day 12】監控與除錯 - 記錄爬蟲執行 Log 並儲存至資料庫

  • 分享至 

  • xImage
  •  

Hi大家好,
這是我參加 iT 邦幫忙鐵人賽的第 1 次挑戰,這次的主題聚焦在結合 Python 爬蟲、RAG(檢索增強生成)與 AI,打造一套 PTT 文章智慧問答系統。在過程中,我會依照每天進度上傳程式碼到 GitHub ,方便大家參考學習。也歡迎留言或來信討論,我的信箱是 gerryearth@gmail.com


當你開始將爬蟲任務交由 Celery 背景執行時,或許已經發現:當程式出錯或資料異常時,除錯變得不再直觀。

尤其是當爬蟲任務規模逐漸擴大、板塊變多,我們更需要一個有系統的方式來記錄每次執行的狀況。

今天,我們要來為爬蟲任務加入「執行日誌(Log)」功能,讓每次任務的成功與失敗都能被紀錄在資料庫中,方便後續的監控與除錯。


今日目標

  • 建立 Log 模型,記錄每次爬蟲任務的執行結果
  • 修改 Celery 任務邏輯,將成功或錯誤寫入資料庫
  • 為後續監控與除錯提供依據

建立爬蟲執行記錄模型

為了方便管理日誌(Log),我們建立新的 app 叫做 log_app (因為 log 是常用保留詞):

python manage.py startapp log_app

記得在 settings.pyINSTALLED_APPS 加上 log_app

並在 log_app/models.py 中新增 Log 資料表:

from django.db import models

class Log(models.Model):
    level = models.CharField(max_length=100)
    category = models.CharField(max_length=100)
    message = models.TextField()
    traceback = models.TextField(null=True, blank=True, default=None)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f'{self.level} - {self.created_at}'

記得遷移資料庫!
先進入 Docker 的 Django ,再遷移資料庫:

docker exec -it django_web /bin/bash
python manage.py makemigrations
python manage.py migrate

資料庫遷移後就可以得到 log_app:

欄位名稱 資料型別 長度 說明
id BIGINT 20 主鍵,自動遞增
level VARCHAR 100 層級
category VARCHAR 100 訊息處分類
message LONGTEXT X 訊息
traceback LONGTEXT X 錯誤詳細內容
created_at TIMESTAMP 6 發生時間

https://ithelp.ithome.com.tw/upload/images/20250811/20172834vUpBul6OFr.png

我用 category 分類日誌內容:

  1. scrape - board (爬蟲的版面)
  2. user - api (使用者使用的 API)

修改爬蟲任務,加入 Log 紀錄

打開 celery_app/scraper.py,將原來的 ptt_scrape 改成如下:

from log_app.models import Log
import traceback
def ptt_scrape(board: str) -> list:
    Log.objects.create(level='INFO', category=f'scrape-{board}', message=f'開始爬取 {board}')
    board_url = 'https://www.ptt.cc/bbs/' + board + '/index.html'
    board_html = get_html(board_url)
    article_urls = get_urls_from_board_html(board_html)
    article_id_list = []
    num_of_same_article = 0
    for article_url in article_urls:
        if Article.objects.filter(url=article_url).exists():
            num_of_same_article += 1
            continue
        article_html = get_html(article_url)
        try:
            article_data = get_data_from_article_html(article_html)
        except Exception as e:
            Log.objects.create(level='ERROR', category=f'scrape-{board}',
                               message=f'從url:{article_url}取得data失敗: {e}',
                               traceback=traceback.format_exc())
            continue
        try:
            article = Article.objects.create(
                board=board,
                title=article_data["title"],
                author=article_data["author"],
                url=article_url,
                content=article_data["content"],
                post_time=article_data["post_time"]
            )

            article_id_list.append(article.id)
        except Exception as e:
            Log.objects.create(level='ERROR', category=f'scrape-{board}',
                               message=f'{article_url}Data插入資料庫錯誤: {e}',
                               traceback=traceback.format_exc())
    Log.objects.create(level='INFO', category=f'scrape-{board}',
                       message=f'爬取 {board} 完成,{len(article_urls)}篇文章中取寫入{len(article_id_list)}筆資料,重複{num_of_same_article}筆資料')
    return article_id_list

為什麼這麼做?

  • 任務背景執行時,除錯較不方便
  • 有 Log 可以追蹤爬蟲是否運作正常
  • 未來可擴充 Log 欄位紀錄執行篇數、版面、錯誤類型等資訊

測試結果

https://ithelp.ithome.com.tw/upload/images/20250811/20172834lrSIsBwtHp.png


明天【Day13】Django REST Framework 入門介紹 - 打造高彈性 RESTful API 的利器,會讓大家了解什麼是 Django REST Framework(DRF),為專案的 API 實作打下基礎。


上一篇
【Day 11】使用 Celery + Redis 建立非同步排程 - 實作 Celery Beat
下一篇
【Day 13】Django REST Framework 入門介紹 - 打造高彈性 RESTful API 的利器
系列文
一起來打造 PTT 文章智慧問答系統!13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言