跳轉到

Dog CEO API

Dog CEO API 主打開源狗狗圖片資料,官方說它提供超過 20,000 張狗狗圖片、涵蓋 120 多種品種;多張隨機圖片端點最多一次回傳 50 張

# API 網址
https://dog.ceo/dog-api/

# 隨機取得一張狗狗圖片:
https://dog.ceo/api/breeds/image/random

# 一次取得多張狗狗圖片:
https://dog.ceo/api/breeds/image/random/3

# 取得所有狗狗品種:
https://dog.ceo/api/breeds/list/all

# 指定品種隨機圖片,例如柴犬:
https://dog.ceo/api/breed/shiba/images/random

範例:終端機版 Dog CEO 查詢器

"""
範例:Dog CEO API 終端機查詢器

功能:
1. 隨機取得一張狗狗圖片
2. 一次取得多張狗狗圖片
3. 查看所有狗狗品種
4. 指定品種取得圖片
5. 詢問是否用瀏覽器開啟圖片

執行方式:
uv run python dog_ceo_cli_explorer.py
"""

import webbrowser
import requests


BASE_URL = "https://dog.ceo/api"


def get_json(url: str) -> dict:
    """送出 GET 請求,並把 API 回傳結果轉成 dict。"""
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    return response.json()


def get_random_image() -> str:
    """取得一張隨機狗狗圖片。"""
    data = get_json(f"{BASE_URL}/breeds/image/random")
    return data["message"]


def get_random_images(count: int) -> list[str]:
    """一次取得多張隨機狗狗圖片。"""
    data = get_json(f"{BASE_URL}/breeds/image/random/{count}")
    return data["message"]


def get_all_breeds() -> dict:
    """取得所有狗狗品種。"""
    data = get_json(f"{BASE_URL}/breeds/list/all")
    return data["message"]


def get_breed_image(breed: str) -> str:
    """指定品種,取得一張隨機圖片。"""
    breed = breed.strip().lower()
    data = get_json(f"{BASE_URL}/breed/{breed}/images/random")
    return data["message"]


def ask_open_image(image_url: str) -> None:
    """詢問使用者是否要用瀏覽器開啟圖片。"""
    print("\n圖片網址:")
    print(image_url)

    answer = input("\n是否要用瀏覽器開啟圖片?(y/n):").strip().lower()

    if answer == "y":
        webbrowser.open(image_url)
        print("已開啟瀏覽器。")
    else:
        print("已取消開啟圖片。")


def show_all_breeds() -> None:
    """用比較好讀的方式印出所有品種。"""
    breeds = get_all_breeds()

    print("\n=== 所有狗狗品種 ===")

    for breed, sub_breeds in breeds.items():
        if sub_breeds:
            print(f"- {breed}{', '.join(sub_breeds)}")
        else:
            print(f"- {breed}")


def main() -> None:
    while True:
        print("\n=== Dog CEO API 狗狗圖片查詢器 ===")
        print("1. 隨機取得一張狗狗圖片")
        print("2. 一次取得多張狗狗圖片")
        print("3. 查看所有狗狗品種")
        print("4. 指定品種取得圖片")
        print("0. 離開")

        choice = input("\n請選擇功能:").strip()

        try:
            if choice == "1":
                image_url = get_random_image()
                ask_open_image(image_url)

            elif choice == "2":
                count = int(input("請輸入圖片張數(1-50):"))

                if count < 1 or count > 50:
                    print("張數請輸入 1 到 50。")
                    continue

                image_urls = get_random_images(count)

                print("\n=== 圖片列表 ===")
                for index, url in enumerate(image_urls, start=1):
                    print(f"{index}. {url}")

                answer = input("\n是否要開啟第一張圖片?(y/n):").strip().lower()
                if answer == "y":
                    webbrowser.open(image_urls[0])

            elif choice == "3":
                show_all_breeds()

            elif choice == "4":
                breed = input("請輸入狗狗品種英文,例如 shiba、hound、pug:")
                image_url = get_breed_image(breed)
                ask_open_image(image_url)

            elif choice == "0":
                print("程式結束。")
                break

            else:
                print("請輸入正確選項。")

        except requests.RequestException as error:
            print("API 連線失敗:", error)

        except ValueError:
            print("輸入格式錯誤,請重新輸入。")


if __name__ == "__main__":
    main()

範例:Streamlit 狗狗圖片牆

import random
import requests
import pandas as pd
import streamlit as st


### API 設定
BASE_URL = "https://dog.ceo/api" # Dog CEO API 的基礎網址


### Streamlit 頁面設定
st.set_page_config(
    page_title="Dog CEO 狗狗圖片牆", # 瀏覽器分頁標題
    page_icon="🐶", # 瀏覽器分頁圖示
    layout="wide" # 使用寬版頁面配置
)


### 自訂 CSS 樣式
st.markdown(
    """
    <style>
    .main-title {
        font-size: 48px;
        font-weight: 900;
        margin-bottom: 0px;
    }

    .subtitle {
        color: #666;
        font-size: 18px;
        margin-bottom: 28px;
    }

    .dog-card {
        padding: 18px;
        border-radius: 24px;
        background: linear-gradient(135deg, #fff4d6, #ffffff);
        box-shadow: 0 8px 24px rgba(0,0,0,0.08);
        border: 1px solid rgba(0,0,0,0.06);
    }

    .big-number {
        font-size: 34px;
        font-weight: 900;
    }

    .small-note {
        color: #777;
        font-size: 14px;
    }
    </style>
    """,
    unsafe_allow_html=True # 允許 Streamlit 執行 HTML 與 CSS
)


### 建立 API GET Request 函式
def get_json(url: str) -> dict:
    """
    向指定 API 網址發送 GET Request,
    並將回傳的 JSON 資料轉成 Python dict。
    """

    response = requests.get(
        url,
        timeout=10 # 最多等待 API 回應 10 秒
    )

    response.raise_for_status() # HTTP 狀態碼不是成功狀態時直接產生例外

    return response.json() # 將 JSON Response 轉成 Python dict


### 取得所有狗狗品種
@st.cache_data(show_spinner=False)
def get_all_breeds() -> dict:
    """
    取得 Dog CEO API 提供的所有狗狗品種。

    因為品種資料不常變動,
    使用 Streamlit cache 避免每次重新呼叫 API。
    """

    data = get_json(
        f"{BASE_URL}/breeds/list/all"
    )

    return data["message"] # message 裡面存放所有品種資料


### 取得多張隨機狗狗圖片
def get_random_images(count: int) -> list[str]:
    """
    根據指定數量,
    取得多張隨機狗狗圖片網址。
    """

    data = get_json(
        f"{BASE_URL}/breeds/image/random/{count}"
    )

    return data["message"] # 回傳圖片網址 list


### 取得指定品種的多張隨機圖片
def get_breed_random_images(
    breed: str,
    count: int
) -> list[str]:
    """
    根據指定狗狗品種與圖片數量,
    取得多張隨機圖片網址。
    """

    data = get_json(
        f"{BASE_URL}/breed/{breed}/images/random/{count}"
    )

    return data["message"]


### 取得指定品種的一張隨機圖片
def get_breed_random_image(breed: str) -> str:
    """
    根據指定狗狗品種,
    取得一張隨機圖片網址。
    """

    data = get_json(
        f"{BASE_URL}/breed/{breed}/images/random"
    )

    return data["message"]


### 整理狗狗品種資料
def flatten_breeds(breeds: dict) -> list[str]:
    """
    將 Dog CEO API 的品種 dict,
    整理成 Streamlit 下拉選單可以使用的 list。

    這個版本只顯示主品種,
    不另外展開子品種。
    """

    return sorted(
        breeds.keys() # 取得 dict 裡所有主品種名稱並排序
    )


### 建立狗狗圖片牆函式
def show_image_grid(
    image_urls: list[str],
    columns_count: int = 3
) -> None:
    """
    根據指定欄位數量建立 Streamlit columns,
    並將狗狗圖片平均排列到不同欄位。
    """

    columns = st.columns(
        columns_count
    )

    for index, image_url in enumerate(image_urls):

        # 使用餘數決定目前這張圖片要放在哪一欄
        column = columns[
            index % columns_count
        ]

        with column:

            st.image(
                image_url,
                width="stretch" # 圖片寬度自動填滿目前欄位
            )

            st.caption(
                f"Dog #{index + 1}" # 顯示圖片編號
            )


### 顯示網站標題
st.markdown(
    "<div class='main-title'>🐶 Dog CEO 狗狗圖片牆</div>",
    unsafe_allow_html=True
)

st.markdown(
    """
    <div class='subtitle'>
    用 REST API 打造一個超療癒的狗狗圖片展示網站。
    </div>
    """,
    unsafe_allow_html=True
)


### 主程式
try:

    ### 取得所有狗狗品種
    breeds = get_all_breeds()

    breed_options = flatten_breeds(
        breeds
    )


    ### 建立 Sidebar 查詢設定
    with st.sidebar:

        st.header(
            "🐶 查詢設定"
        )

        mode = st.radio(
            "選擇模式",
            [
                "隨機圖片牆",
                "指定品種",
                "今日狗狗抽卡",
                "品種資料表"
            ]
        )

        image_count = st.slider(
            "圖片數量",
            min_value=3, # 最少顯示 3 張
            max_value=50, # 最多顯示 50 張
            value=9, # 預設顯示 9 張
            step=1
        )


        ### 設定預設狗狗品種
        default_index = 0 # 找不到 shiba 時預設選擇第一個品種

        if "shiba" in breed_options:
            default_index = breed_options.index(
                "shiba"
            ) # 找到 shiba 在品種清單中的索引位置


        selected_breed = st.selectbox(
            "選擇狗狗品種",
            breed_options,
            index=default_index
        )

        columns_count = st.slider(
            "每列顯示幾張",
            min_value=2,
            max_value=5,
            value=3
        )

        run_button = st.button(
            "🐾 開始產生",
            type="primary",
            width="stretch"
        )

        st.divider()

        st.caption(
            "建議查詢:shiba、pug、husky、retriever、terrier"
        )


    ### 顯示網站統計資訊
    metric_col1, metric_col2, metric_col3 = st.columns(3)

    metric_col1.metric(
        "API 主題",
        "狗狗圖片"
    )

    metric_col2.metric(
        "可選品種",
        len(breed_options) # 計算目前 API 提供多少個主品種
    )

    metric_col3.metric(
        "最多隨機張數",
        "50 張"
    )

    st.divider()


    ### 模式 1:隨機圖片牆
    if mode == "隨機圖片牆":

        st.subheader(
            "🎲 隨機狗狗圖片牆"
        )


        ### 取得隨機狗狗圖片
        if run_button:

            with st.spinner(
                "正在召喚狗狗..."
            ):

                image_urls = get_random_images(
                    image_count
                ) # 根據 Sidebar 選擇的圖片數量取得圖片

        else:

            image_urls = get_random_images(
                9
            ) # 第一次開啟網站時預設顯示 9 張圖片


        ### 顯示狗狗圖片牆
        show_image_grid(
            image_urls,
            columns_count
        )


        ### 建立圖片網址 DataFrame
        df = pd.DataFrame(
            {
                "編號": range(
                    1,
                    len(image_urls) + 1
                ),
                "圖片網址": image_urls
            }
        )


        ### 將 DataFrame 轉成 CSV
        csv_data = df.to_csv(
            index=False # CSV 不加入 DataFrame index
        ).encode(
            "utf-8-sig" # 使用 UTF-8 BOM,避免 Excel 開啟中文時出現亂碼
        )


        ### 提供 CSV 下載按鈕
        st.download_button(
            label="📥 下載圖片網址 CSV",
            data=csv_data,
            file_name="dog_image_urls.csv",
            mime="text/csv"
        )


    ### 模式 2:指定品種
    elif mode == "指定品種":

        st.subheader(
            f"🐕 指定品種:{selected_breed}"
        )


        ### 設定要取得的圖片數量
        if run_button:

            count = image_count # 按下按鈕後使用 Sidebar 選擇的圖片數量

        else:

            count = 6 # 第一次進入這個模式時預設顯示 6 張


        ### 取得指定品種圖片
        with st.spinner(
            f"正在尋找 {selected_breed}..."
        ):

            image_urls = get_breed_random_images(
                selected_breed,
                count
            )


        st.success(
            f"已取得 {count}{selected_breed} 圖片"
        )


        ### 顯示指定品種圖片牆
        show_image_grid(
            image_urls,
            columns_count
        )


        ### 建立指定品種圖片 DataFrame
        df = pd.DataFrame(
            {
                "品種": [
                    selected_breed
                ] * len(image_urls), # 每一筆圖片資料都填入相同品種名稱

                "圖片網址":
                    image_urls
            }
        )


        ### 將 DataFrame 轉成 CSV
        csv_data = df.to_csv(
            index=False
        ).encode(
            "utf-8-sig"
        )


        ### 提供 CSV 下載按鈕
        st.download_button(
            label="📥 下載圖片網址 CSV",
            data=csv_data,
            file_name=f"{selected_breed}_images.csv",
            mime="text/csv"
        )


    ### 模式 3:今日狗狗抽卡
    elif mode == "今日狗狗抽卡":

        st.subheader(
            "✨ 今日狗狗抽卡"
        )


        ### 建立 Session State 儲存抽卡結果
        # Streamlit 每次操作元件都會重新執行程式
        # 因此使用 session_state 保存目前抽到的狗狗,避免每次重新執行都自動換卡
        if "lucky_breed" not in st.session_state:

            st.session_state.lucky_breed = random.choice(
                breed_options
            ) # 第一次進入頁面時隨機選擇一個品種

            st.session_state.lucky_image = (
                get_breed_random_image(
                    st.session_state.lucky_breed
                )
            ) # 根據抽到的品種取得一張隨機圖片


        ### 按下按鈕重新抽卡
        if run_button:

            st.session_state.lucky_breed = random.choice(
                breed_options
            )

            st.session_state.lucky_image = (
                get_breed_random_image(
                    st.session_state.lucky_breed
                )
            )


        ### 取得目前 Session State 中的抽卡結果
        lucky_breed = (
            st.session_state.lucky_breed
        )

        lucky_image = (
            st.session_state.lucky_image
        )


        ### 今日狗狗隨機評語
        comments = [
            "今天適合放慢速度,像狗狗曬太陽一樣。",
            "今天的任務是:不要把自己逼太緊。",
            "你今天的幸運值很高,適合開始一個小專案。",
            "這張狗狗提醒你:debug 也是人生的一部分。",
            "今天適合寫程式,也適合看狗。"
        ]


        ### 建立抽卡結果左右兩欄
        card_col1, card_col2 = st.columns(
            [1, 2] # 左欄寬度 1、右欄寬度 2
        )


        ### 左側顯示狗狗圖片
        with card_col1:

            st.markdown(
                "<div class='dog-card'>",
                unsafe_allow_html=True
            )

            st.image(
                lucky_image,
                width="stretch"
            )

            st.markdown(
                "</div>",
                unsafe_allow_html=True
            )


        ### 右側顯示抽卡資訊
        with card_col2:

            st.markdown(
                "### 你的今日狗狗"
            )

            st.markdown(
                f"""
                <div class='big-number'>
                {lucky_breed.upper()}
                </div>
                """,
                unsafe_allow_html=True
            ) # 將抽到的狗狗品種轉成大寫顯示

            st.write(
                random.choice(
                    comments
                )
            ) # 隨機顯示一句今日評語

            st.info(
                "這個功能可以拿來教學生:"
                "API 不只能查資料,"
                "還可以搭配隨機邏輯,"
                "做成有互動感的小作品。"
            )

            st.markdown(
                "#### 圖片網址"
            )

            st.code(
                lucky_image
            ) # 顯示目前狗狗圖片的原始網址


    ### 模式 4:品種資料表
    elif mode == "品種資料表":

        st.subheader(
            "📋 Dog CEO 品種資料表"
        )


        ### 建立品種資料 list
        rows = []

        for breed, sub_breeds in breeds.items():

            rows.append(
                {
                    "主品種": breed,

                    "是否有子品種":
                        "是"
                        if sub_breeds
                        else "否",

                    "子品種":
                        ", ".join(sub_breeds)
                        if sub_breeds
                        else "無"
                }
            )


        ### 將品種資料轉成 DataFrame
        df = pd.DataFrame(
            rows
        )


        ### 顯示品種資料表
        st.dataframe(
            df,
            width="stretch", # 表格寬度自動填滿頁面
            height=520 # 設定表格高度
        )


        ### 將品種資料轉成 CSV
        csv_data = df.to_csv(
            index=False
        ).encode(
            "utf-8-sig"
        )


        ### 提供 CSV 下載按鈕
        st.download_button(
            label="📥 下載品種資料 CSV",
            data=csv_data,
            file_name="dog_breeds.csv",
            mime="text/csv"
        )


### 處理 API 連線錯誤
except requests.RequestException as error:

    st.error(
        f"API 連線失敗:{error}"
    )


### 處理其他程式錯誤
except Exception as error:

    st.error(
        f"程式發生錯誤:{error}"
    )