Skip to content

如何使用 Airflow 呼叫 API 串接 Discord Webhook,追蹤關渡醫院某醫生的看診進度

Published: at 上午07:39

起因

因為最近後背靠近腰部的部分總是癢癢的,於是掛號關渡醫院晚診看皮膚科,掛到了37號 該晚不晚該早不早

想說先去吃個飯,再去醫院,但又怕吃太久導致過號,又要等看過兩三個人才能看醫生

於是想說做一個定時去爬看診進度的推播,來追蹤提醒自己現在看到幾號了

使用套件

新增一個檔案

requirements.txt

內容為

requests==2.32.3
beautifulsoup4==4.12.3

然後開啟cmd

pip install -r requirements.txt

python 驗證

from datetime import datetime, timedelta
import requests
from bs4 import BeautifulSoup

# 定義爬取資料的函式
def fetch_doctor_status():
    url = "https://www.gandau.gov.tw/IdxGp3/webreg/%E9%96%80%E8%A8%BA%E6%99%82%E5%88%BB%E8%A1%A8/QrySeeStat.asp?DocN=08"  # 替換為實際 API 的 URL
    response = requests.get(url)
    if response.status_code == 200:
        response.encoding = 'utf-8'
        print(f"response:{response.text}")
        soup = BeautifulSoup(response.text, "html.parser")
        print(f"soup: {soup}")

        # 找到 "李政源" 的燈號資料
        rows = soup.find_all("tr", bgcolor="#E2FFA6")
        for row in rows:
            cells = row.find_all("td")
            if "李政源" in cells[1].text:
                status = cells[2].text.strip()  # 燈號狀態
                print(f"李政源醫生目前看到的號碼: {status}")
                return status
    else:
        print("無法取得資料,請檢查 API 狀態")
        return None


if __name__ == "__main__":
    records = fetch_doctor_status()
    print(f"records:{records}")

result

李政源醫生目前看到的號碼: 0

airflow 串接

from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime, timedelta
import requests
from bs4 import BeautifulSoup
from airflow.timetables.trigger import CronTriggerTimetable

# 定義 Discord Webhook 的函式
def send_to_discord(message):
    webhook_url = "{{放入你自己的 discord webhook url }}"
    payload = {"content": message}
    response = requests.post(webhook_url, json=payload)
    if response.status_code == 204:  # Discord Webhook 成功回應
        print("訊息成功發送到 Discord")
    else:
        print(f"無法發送訊息到 Discord,HTTP 狀態碼: {response.status_code}, 回應: {response.text}")
# 定義爬取資料的函式
def fetch_doctor_status():
    url = "https://www.gandau.gov.tw/IdxGp3/webreg/%E9%96%80%E8%A8%BA%E6%99%82%E5%88%BB%E8%A1%A8/QrySeeStat.asp?DocN=08"  # 替換為實際 API 的 URL
    response = requests.get(url)
    response.encoding = 'utf-8'
    if response.status_code == 200:
        soup = BeautifulSoup(response.text, "html.parser")

        # 找到 "李政源" 的燈號資料
        rows = soup.find_all("tr", bgcolor="#E2FFA6")
        for row in rows:
            cells = row.find_all("td")
            if "李政源" in cells[1].text:
                status = cells[2].text.strip()  # 燈號狀態
                message = f"李政源醫生目前看到的號碼: {status}"
                print(message)
                send_to_discord(message)
                print(f"李政源醫生目前看到的號碼: {status}")
                return status
    else:
        print("無法取得資料,請檢查 API 狀態")
        return None

# 定義 Airflow DAG
default_args = {
    'owner': 'airflow',
    'depends_on_past': False,
    'email_on_failure': False,
    'email_on_retry': False,
    'retries': 1,
    'retry_delay': timedelta(minutes=5),
}

with DAG(
    'fetch_doctor_status',
    default_args=default_args,
    description='Fetch doctor status every minute',
        schedule=CronTriggerTimetable(
        cron="*/1 17-20 * * *",  #
        timezone="Asia/Taipei",  # 指定時區為台北(UTC+8)
    ),
    start_date=datetime(2025, 1, 22),
    end_date=datetime(2025, 1, 22, 20, 30),  # 設置結束日期和時間
    catchup=False,
    tags=['web scraping', 'doctor status'],
) as dag:

    task_fetch_status = PythonOperator(
        task_id='fetch_doctor_status',
        python_callable=fetch_doctor_status,
    )

歷程

因為 1800 醫生才開始營業 於是選定 1800-2000 來做 推送

但有問題是 2010 還是一直在收到推送

才發現 原來這樣的設定是指 1800-2059 ~

discord 也不是每一則都會發推播 如果每兩分鐘抓一次的推送 和上一次一樣 他可能就不會推播了

編碼問題

一開始 api 回來都亂碼

查了才知道

要加

response.encoding = 'utf-8'

才會正常

思考

邊吃飯邊想 就算我在吃飯 過號了我還是來不及

假設我是 30 號 今天看診共60號

但如果前面 15號 叫號碼時都 no show 且 是跟我一樣時間到達醫院補報到呢?

不是還是來不及 或是還是要等