Python 實戰範例

股息成長投資者利器

用Python自動化抓取股票財務資料

 

 

Introduction

前言

隨著股市投資的普及,投資者越來越重視如何利用財務數據來評估公司的經濟健康和未來潛力。每股盈餘(EPS)、自由現金流(FCF)和員工數量(NOE)等關鍵財務指標,成為了衡量公司營運狀況的重要參考。然而,手動抓取和整理這些財務數據往往需要大量時間和精力,對於需要分析多支股票的投資者來說,這是一項極具挑戰的任務。

為了提高分析效率,本篇文章將介紹如何使用 Python 來自動化股票財務數據的抓取和報告生成過程。我們將展示如何利用 yfinancerequestspandasxlwings 等工具,快速獲取每股盈餘、自由現金流、員工數量等資料,並將這些資料轉化為直觀的圖表和報告。這不僅能夠幫助投資者省去繁瑣的手動操作,還能提升分析效率,從而幫助做出更準確的投資決策。

本篇文章旨在向讀者展示如何簡單、快速地實現股票財務數據的自動化抓取,並透過 Python 輕鬆生成可視化報告。無論你是初學者還是有經驗的投資者,都能從中學到如何運用程式語言來強化自己的投資分析能力。

本文提供的 Python 實戰範例將幫助您掌握從數據抓取到報告生成的全過程,完整程式碼可以直接在這邊下載。

下載最新的Dividend Radar資料

首先,我們從 Portfolio Insight 網站下載最新的 Dividend Radar Excel 表格。這份表格包含了 Dividend Champions 排名,該排名是根據公司連續增派股息的年度數量來排序的。

Dividend Radar 檔案下載

顯示股票代號、公司名稱與連續股息年數

讀取包含股息資料的 Excel 檔案,並從中提取每行的基本資訊,包括股票代號、公司名稱以及連續股息年數。透過 head(10),程式會限制只顯示前 10 行的資料。

import pandas as pd

# 讀取 Excel 檔案,直接載入為 DataFrame
dividend_df = pd.read_excel('Dividend Radar 2024-12-20.xlsx', sheet_name=1, header=2)

# 遍歷 DataFrame 的前 10 行資料
for index, row in dividend_df.head(10).iterrows():  # 使用 .head(10) 限制為前 10 行
    print(f"處理第 {index + 1}/10 筆資料 - 股票代號: {row['Symbol']} 公司名稱: {row['Company']} 連續股息年數: {row['No Years']}")

程式碼說明

  1. 讀取 Excel 檔案:使用 pd.read_excel() 函式讀取指定的 Excel 檔案,並將其載入為 DataFrame 格式。
  2. 限制資料範圍:使用 head(10) 限制資料只顯示前 10 行,並透過 iterrows() 跑這些資料行。
  3. 顯示資訊:對每一行資料,程式會顯示股票代號、公司名稱和連續股息年數。

資料展示: 以下是從下載的資料中提取的部分內容

顯示前 10 名的連續股息資料

抓取股票10年每股盈餘 (EPS) 的資料

接下來,我們將針對每個股票抓取其 每股盈餘 (EPS) 的資料。EPS 是衡量公司盈利能力的重要指標,代表公司每股普通股所能分配到的利潤。透過這些資料,我們可以了解該公司過去的盈利表現,進一步分析其財務穩定性。

在這段程式碼中,我們將使用 requests 套件從 macrotrends.net 網站抓取指定股票代號(ticker)的 EPS 資料。這些資料會顯示最近 10 年的每股盈餘數據,並進行格式化處理後呈現給我們。

import requests
from fake_useragent import UserAgent
import pandas as pd
from io import StringIO

def get_eps(ticker):
    # 基本股票頁面 URL
    url = f'https://www.macrotrends.net/stocks/charts/{ticker}/'
    
    # 隨機生成 User-Agent 以避免被網站封鎖
    ua = UserAgent(platforms='desktop') 
    headers = {'User-Agent' : ua.random}
    
    # 發送 GET 請求並獲取股票頁面
    response = requests.get(url, headers=headers)

    # 生成新的 URL 以抓取 EPS 資料
    new_url = response.url + 'eps-earnings-per-share-diluted'
    response = requests.get(new_url, headers=headers)

    # 解析 HTML 表格並抓取前 10 筆資料
    dfs = pd.read_html(StringIO(response.text))
    df = dfs[0].iloc[:10].iloc[::-1]  # 反轉順序以便最新資料顯示在最上方

    # 清理和格式化 EPS 資料
    df = df.set_index(df.columns[0])  # 設置第一列為索引
    df = df.iloc[:, -1].str.strip('$').str.replace(',', '').astype(float)
    
    return df

# 使用股票代號 'ABM' 抓取公司 EPS 資料
ticker = 'ABM'
eps_df = get_eps(ticker)
print(eps_df)

呈現結果

該程式碼會返回最近 10 年的每股盈餘(EPS)數據,讓我們可以查看這段期間內每年的每股盈餘。

ABM Industries Annual EPS

接下來,我們將使用 matplotlib 來繪製每股盈餘(EPS)資料的折線圖。這樣我們可以更直觀地了解某股票的 EPS 變化趨勢。

import matplotlib.pyplot as plt

fig_eps = plt.figure()
eps_df.plot(style='o-')
plt.title('Annual EPS')
plt.xlabel('Date') 
plt.ylabel('EPS') 
plt.show()
ABM Industries Annual EPS plot v2

抓取股票10年自由現金流 (FCF) 的資料

接著,我們將針對個別股票抓取 自由現金流(Free Cash Flow, FCF) 的資料。自由現金流是企業經營活動產生的現金流量,減去資本支出後的剩餘現金,這部分的現金可供分紅、償還債務或進行再投資。這是評估公司財務健康的一個重要指標。

def get_fcf(ticker):
    # 構建 URL 並發送請求
    url = f'https://www.macrotrends.net/stocks/charts/{ticker}/'
    ua = UserAgent(platforms='desktop')
    headers = {'User-Agent' : ua.random}
    response = requests.get(url, headers=headers)

    # 訪問自由現金流頁面並獲取數據
    new_url = response.url + 'free-cash-flow'
    response = requests.get(new_url, headers=headers)

    # 讀取網頁中的表格數據並選擇需要的部分
    dfs = pd.read_html(StringIO(response.text))
    df = dfs[0].iloc[:10].iloc[::-1]  # 取前10行並反轉順序

    # 設定索引並處理數據格式
    df = df.set_index(df.columns[0])
    df = df.iloc[:, -1].astype(float)  # 轉換為浮點數格式

    return df

fcf_df = get_fcf(ticker)
fcf_df

呈現結果

此程式碼會返回過去 10 年的 自由現金流 數據。每個年度的自由現金流數值將反映該年度公司在扣除營運成本及資本支出後所剩餘的現金,這對於評估公司的現金生成能力和未來成長潛力是非常重要的。

ABM Industries Annual Free Cash Flow Data

接下來,我們會使用前面抓取的 自由現金流(Free Cash Flow, FCF) 數據來繪製圖表,方便我們視覺化每年的自由現金流變化。這樣的視覺化有助於我們直觀地了解公司的現金流趨勢,以及該公司是否能夠穩定產生現金流。

fig_fcf = plt.figure()
fcf_df.plot(kind="bar")
plt.title('Annual FCF')
plt.xlabel('Date') 
plt.ylabel('FCF') 
plt.show()
ABM Industries Annual Free Cash Flow

抓取股票10年員工人數 (Number of Employees) 的資料

接下來,我們將針對個別股票抓取 員工人數(Number of Employees) 的資料。員工人數是衡量公司規模和經營狀況的一個重要指標。公司員工數量的變化通常反映出企業的成長、縮編或其他內部調整。透過抓取過去10年的員工人數資料,我們可以了解公司的人力資源變化,進一步分析企業的發展趨勢。

以下是用來抓取並顯示指定股票過去10年員工人數資料的程式碼:

def get_noe(ticker):
    # 建立要抓取的 URL 地址,針對指定股票的數據頁面
    url = f'https://www.macrotrends.net/stocks/charts/{ticker}/'
    
    # 使用隨機 User-Agent 來模擬正常的瀏覽器請求
    ua = UserAgent(platforms='desktop')
    headers = {'User-Agent': ua.random}
    
    # 發送請求並取得響應
    response = requests.get(url, headers=headers)
    
    # 繼續抓取員工人數資料的頁面
    new_url = response.url + 'number-of-employees'
    response = requests.get(new_url, headers=headers)

    # 使用 pandas 讀取 HTML 中的表格
    dfs = pd.read_html(StringIO(response.text))
    
    # 取得表格資料,反轉順序並設定索引為年份
    df = dfs[0].iloc[:10].iloc[::-1]
    df = df.set_index(df.columns[0])
    
    # 取最後一欄的員工數並轉換為數值型態
    df = df.iloc[:, -1].astype(float)
    
    return df

呈現結果

程式執行後會返回員工人數的資料,顯示指定股票過去10年的員工數量。這些數據可用於分析公司規模變化及其對業務運營的影響。

ABM Industries Annual Employee Count

接下來,我們將使用 matplotlib 來繪製 員工人數(Number of Employees) 資料的條形圖(bar chart)。這樣我們可以更直觀地了解某股票過去10年來的員工人數變化趨勢,並觀察是否有顯著的增長或減少,這對於評估公司規模變化和發展方向非常有幫助。

fig_noe = plt.figure()
noe_df.plot(kind='bar')
plt.title('Annual NOE')
plt.xlabel('Date') 
plt.ylabel('NOE') 
plt.show()
ABM Industries Annual Employee Count Plot

抓取股票歷史價格資料並繪製收盤價變化圖

接下來,我們將使用 yfinance 庫來抓取某股票的歷史價格資料。透過該資料,我們可以繪製出股票的歷史收盤價變化圖,進一步了解該股票在過去一段時間內的價格波動情況。這對於評估股票的長期表現及其市場趨勢非常重要。

# 匯入所需的套件
import yfinance as yf

# 使用 yfinance 取得股票的歷史價格資料
t = yf.Ticker(ticker)
hist = t.history(period="max")

# 繪製股票歷史收盤價的折線圖
fig_hist = plt.figure()
hist.Close.plot()
plt.title('Hist Price')
plt.xlabel('Date') 
plt.ylabel('Price') 
plt.show()

呈現結果

這樣,你就能夠直觀地觀察股票的價格變化趨勢。

Hist Price

生成股票報告並輸出為 Excel 檔案

接下來,我們將編寫一個函數 create_report,它將根據提供的股票代號(ticker)抓取該股票的各項資料,並將其生成一份詳細的 Excel 報告。這份報告將包括公司基本資訊、歷史股價變化圖、每股盈餘(EPS)圖表、自由現金流(FCF)圖表及員工人數(NOE)圖表。最終,報告將被保存為 Excel 文件,並以股票代號和排名命名。

import time
import random
import xlwings as xw
import yfinance as yf
import matplotlib.pyplot as plt

def retry_operation(func, *args, max_retries=5, **kwargs):
    retries = 0
    while retries < max_retries:
        try:
            # 嘗試執行目標函數
            return func(*args, **kwargs)
        except Exception as e:
            retries += 1
            print(f"執行 {func.__name__} 時發生錯誤: {e}. 嘗試重試第{retries}次...")
            if retries < max_retries:
                time.sleep(random.uniform(30, 60))  # 等待一段時間後再重試
            else:
                print(f"達到最大重試次數,無法執行 {func.__name__}。")
                return None  # 若重試次數達到上限則返回None

def create_report(ticker, rank, row):
    with xw.App(visible=True) as app:
        wb = app.books.add()
        sheet = wb.sheets[0]
        sheet.name = ticker
    
        # 取得 ticker 資訊
        t = yf.Ticker(ticker)
        
        # 寫入公司基本資訊
        sheet['A1'].value = row.to_dict()
        sheet['A1:B40'].font.name = '微軟正黑體'
        sheet['A1'].expand('down').color = (198, 224, 180)
        sheet['A15'].expand('down').color = (255, 230, 153)
        sheet['A25'].expand('down').color = (180, 198, 231)
        sheet['A39'].expand('down').color = (173, 216, 230)
    
        sheet.autofit()
        
        # 寫入歷史股價
        hist = t.history(period="max")
        fig_hist = plt.figure()
        hist.Close.plot()
        plt.title('Hist Price')
        plt.xlabel('Date') 
        plt.ylabel('Price') 
    
        plot_hist = sheet.pictures.add(fig_hist, name='Hist', update=True)
        plot_hist.left = sheet['D3'].left
        plot_hist.top = sheet['D3'].top
        plot_hist.width = 394
        plot_hist.height = 268

        time.sleep(random.uniform(5, 10))

        # 寫入eps圖表
        eps_df = retry_operation(get_eps, ticker)
        if isinstance(eps_df, pd.Series):
            fig_eps = plt.figure()
            eps_df.plot(style='o-')
            plt.title('Annual EPS')
            plt.xlabel('Date') 
            plt.ylabel('EPS') 
        
            plot_eps = sheet.pictures.add(fig_eps, name='EPS', update=True)
            plot_eps.left = sheet['D20'].left
            plot_eps.top = sheet['D20'].top
            plot_eps.width = 394
            plot_eps.height = 268

        time.sleep(random.uniform(5, 10))

        # 寫入fcf圖表
        fcf_df = retry_operation(get_fcf, ticker)
        if isinstance(fcf_df, pd.Series):
            fig_fcf = plt.figure()
            fcf_df.plot(kind="bar")
            plt.title('Annual FCF')
            plt.xlabel('Date') 
            plt.ylabel('FCF') 
    
            plot_fcf = sheet.pictures.add(fig_fcf, name='FCF', update=True)
            plot_fcf.left = sheet['L3'].left
            plot_fcf.top = sheet['L3'].top
            plot_fcf.width = 394
            plot_fcf.height = 268

        time.sleep(random.uniform(5, 10))

        # 寫入NOE圖表
        noe_df = retry_operation(get_noe, ticker)
        if isinstance(noe_df, pd.Series):
            fig_noe = plt.figure()
            noe_df.plot(kind='bar')
            plt.title('Annual NOE')
            plt.xlabel('Date') 
            plt.ylabel('NOE') 
            
            plot_noe = sheet.pictures.add(fig_noe, name='NOE', update=True)
            plot_noe.left = sheet['L20'].left
            plot_noe.top = sheet['L20'].top
            plot_noe.width = 394
            plot_noe.height = 268
    
        wb.save(f'{rank}_{ticker}.xlsx')
        wb.close()

接下來,我們會從 Excel 檔案 Dividend Radar 2024-12-20.xlsx 讀取資料,並跑前 10 行,為每一筆資料生成股票報告。每個報告會包含股票的基本資訊、歷史價格圖表、每股盈餘(EPS)、自由現金流(FCF)以及淨營運利潤(NOE)等重要財務指標的視覺化圖表。

import pandas as pd

# 讀取 Excel 檔案,直接載入為 DataFrame
dividend_df = pd.read_excel('Dividend Radar 2024-12-20.xlsx', sheet_name=1, header=2)

# 遍歷 DataFrame 的前 10 行資料
for index, row in dividend_df.head(10).iterrows():  # 使用 .head(10) 限制為前 10 行
    print(f"處理第 {index + 1}/10 筆資料 - 股票代號: {row['Symbol']} 公司名稱: {row['Company']} 連續股息年數: {row['No Years']}")
    create_report(row['Symbol'], index + 1, row)  # 呼叫 create_report 函數生成報告

這段程式碼將使你能夠批量處理 Excel 檔案中的資料,並且對每一筆資料生成詳細的股票報告。這樣的流程能夠大大提高分析股票的效率,並且為每支股票提供豐富的財務資訊及視覺化報告。

Dividend Radar download

接著開啟檔案,可以看到每一個股票報告的 Excel 檔案中,包含了以下幾個重要的內容:

  1. 公司基本資料:在報告的最左邊,我們會將該股票的基本資料(如公司名稱、股票代號、股息年數等)以表格形式列出。這些資料能夠幫助你快速了解該公司的背景。
  2. 股票歷史價格圖表:在該報告中,會顯示該股票的歷史股價變化圖,這樣可以直觀地觀察股票的價格波動趨勢,並了解其過去的市場表現。
  3. 每股盈餘(EPS)圖表:每股盈餘(EPS)是衡量公司獲利能力的重要指標。在報告中,會顯示該公司過去幾年的EPS數據,並以折線圖呈現,讓你可以清楚看到其盈餘成長的趨勢。
  4. 自由現金流(FCF)圖表:自由現金流是衡量公司財務健康狀況的另一個重要指標。在報告中,將會顯示該公司過去幾年的自由現金流數據,並以柱狀圖展示,幫助你理解公司現金流的變化情況。
  5. 員工人數(NOE)圖表:員工人數能反映出公司的規模與成長性。這部分會顯示該公司過去幾年的員工數量,並以柱狀圖呈現,讓你能夠了解公司的人力資源變化。
report

結論

在這篇文章中,我們深入探討了如何使用 Python 來抓取並分析股票的財務數據,包括每股盈餘(EPS)、自由現金流(FCF)、員工數量(NOE)等重要指標。透過結合多個 Python 套件,如 yfinancerequestspandasxlwings,我們能夠自動化數據抓取並生成詳細的報告。

這些報告不僅包含基本的財務數據,還包括清晰的視覺化圖表,幫助使用者更直觀地理解股票的歷史表現及其財務健康狀況。這種方法能夠有效提升股票分析的效率,並且提供一個系統化的方式來評估多支股票的長期趨勢和基本面指標。

總結來說,透過這樣的自動化報告生成流程,投資者可以節省大量的時間,快速獲取並分析股票數據,從而做出更加科學和合理的投資決策。這樣的流程對於那些希望提高財務分析效率、減少手動操作並加速決策過程的投資者來說,無疑是一個強大的工具。

本文提供的 Python 實戰範例將幫助您掌握從數據抓取到報告生成的全過程,完整程式碼可以直接在這邊下載。

參考資料

  1. Python 官方文檔:了解Python的基本功能和用法。 Python官方網站
  2. xlwings 官方文檔:提供有關xlwings的使用說明,幫助將資料和圖表存入Excel。 xlwings官方網站
  3. yfinance 官方文檔:用於抓取股市資料的Python函式庫,yfinance官方網站

課程推薦

  1. Python 金融資訊爬蟲大師班:這是一門以「股票數據」、「金融資訊」與「財經新聞」為主軸的「網路爬蟲」課程。
  2. 用Python打造自己專屬的VIP看盤室:專注於使用Plotly進行資料視覺化,學習如何製作專業、美觀的互動圖表,幫助你有效地展示數據和趨勢。
  3. 用Python操作Excel|實現職場自動化與理財工具開發:掌握xlwings及其他工具來實現Excel的自動化處理,從數據整理到報告生成,全方位提升工作效率。

這些課程能夠幫助你進一步提升資料分析和視覺化的能力,並將所學應用到實際工作中,讓你在數據分析領域中更加游刃有餘。

全方位 Python 課程

無論你是初學者還是有經驗的開發者,這裡的課程都能滿足你的需求。精心設計的8堂課程,涵蓋不同類型的Python主題,幫助你在各個領域中脫穎而出。