1. 程式人生 > >Python爬取中國銀行外匯牌價

Python爬取中國銀行外匯牌價

專案倉庫


專案簡介

  • 爬蟲的核心程式碼就移步去Github倉庫上看吧~

  • 專案功能簡介:

    1. 獲取中國銀行外匯牌價的匯率(本專案模板以港幣為Base)
    2. 獲取時間可以自定義(設定起始時間不建議跨度太長)
    3. 爬蟲資料暫時存在於MySQL中
  • 爬取來源: srh.bankofchina.com/search/whpj…

  • 爬取目的:

    1. 純屬一片好奇心想去預測未來的幾天的匯率走向
    2. 順便做一做視覺化
  • 專案技術點:

    1. Scrapy獲取原始碼解析資料

2. selenium chrome headless 模式獲取頁碼 ( 需要安裝selenium以及配置chrome driver)

* 由於頁碼是通過JS進行載入的,所以暫時解決辦法用selenium無頭模式進行渲染(後期改為用Scrapy-splash)
複製程式碼


講講資料方面

  • 以下資料使用的是 2018-01-01 ~ 2018-11-06日的匯率資料(去重之後約48000條資料)
  1. 資料探索:
    • 中行網站上有四種不同的牌價(現匯買入價,現鈔買入價,現匯賣出價,現鈔賣出價,中行折算價), 中行折算價暫時先不考慮。
      • 現匯買入價——是指賬戶內的外匯通過結匯兌換成人民幣的銀行結算價。
      • 現鈔買入價——是指外幣現鈔結匯,兌換成人民幣的銀行結算價。
      • 現匯賣出價——是指購買外匯對外付款,人民幣兌換外幣的銀行結算價。
      • 現鈔賣出價——是指購買外幣現鈔,人民幣兌換外幣的銀行結算價。
    • 要考慮去香港買買買的話就可以用現匯賣出價或者現鈔賣出價進行資料視覺化和資料預測了。
    • 本人選擇了現匯賣出價, 接下來的資料都是用現匯賣出價進行視覺化並統計分析了(其他型別只是換了列資料而已)

  1. 資料清洗:
    • 在頁面上看或者從抓取回來的資料很容易看出會有部分重複資料(不知道中行為啥允許重複資料的出現)

    • 清洗手段:(我選擇了後者,在不給資料庫施壓的情況下,後者相對比較仁慈)

      • MySQL:SELECT DISTINCT語句
      • Pandas: Dataframe.drop_duplicates(根據釋出時間保留第一個值就ok了)
    • 程式碼大致如下:

    header = ['現匯買入價', '現鈔買入價', '現匯賣出價', '現鈔賣出價', '中行折算價', '查詢時間']
    
    # dataframe配置
    # 顯示所有列
    pd.set_option('display.max_columns', None)
    # 顯示所有行
    # pd.set_option('display.max_rows', None)
    # 設定value的顯示長度為100,預設為50
    pd.set_option('max_colwidth', 100)
    
    # 從MySQL中獲取資料並重置表頭
    sql = "SELECT buying_rate, cash_buying_rate, selling_rate, cash_selling_rate, boe_conversion_rate, rate_time " \
          "FROM exchange_rate.t_exchange_rate " \
          "WHERE currency_name = '港幣'"
    df = pd.read_sql(sql=sql, con=sql_conn())
    df.columns = header
    df = df.sort_values(by='查詢時間')
    # 轉換資料型別
    df['現匯買入價'] = df['現匯買入價'].astype('float')
    df['現鈔買入價'] = df['現鈔買入價'].astype('float')
    df['現匯賣出價'] = df['現匯賣出價'].astype('float')
    df['現鈔賣出價'] = df['現鈔賣出價'].astype('float')
    df['中行折算價'] = df['中行折算價'].astype('float')
    
    # 去重
    df = df.drop_duplicates(subset='查詢時間', keep='first')
    print(df[['現匯賣出價', '查詢時間']])
    複製程式碼

  1. 資料視覺化(折線圖和K線圖)
    • 以下程式碼均為部分程式碼使用的庫(Pandas, pyecharts)
  • 折線圖
# 折線圖資料
total_data = [
    df['現匯買入價'].tolist(), df['現鈔買入價'].tolist(), df['現匯賣出價'].tolist(), df['現鈔賣出價'].tolist()
]
draw_line_pic(
    title="人民幣和港幣的匯率折算(100港幣)",
    labels=header[0:4],
    data_package=total_data,
    x_axis=df['查詢時間'].tolist()
)

def draw_line_pic(title: str, labels: list, data_package: list, x_axis: list):
    """
    折線圖
    :param title:
    :param labels:
    :param data_package:
    :param x_axis:
    :return:
    """
    style = Style(
        title_top="#fff",
        title_pos="left",
        width=1920,
        height=900
    )

    line = Line(title=title, **style.init_style)
    for i, d in enumerate(labels):
        line.add(d, x_axis, data_package[i],
                 is_stack=False,
                 is_label_show=True,
                 is_smooth=True,
                 yaxis_min=78,
                 yaxis_max=90,
                 yaxis_formatter="元人民幣",
                 mark_point=["max", "min"],
                 mark_line=['average'],
                 is_datazoom_show=True,
                 datazoom_type="both",
                 datazoom_range=[80, 100])
    line.render(path='./file/line.html')
複製程式碼

  • 畫圖結果如下:


  • K線圖
    • 資料註明: 使用了現匯賣出價,根據資料進行groupby求每日的均值進行視覺化
 # K線圖資料
df['查詢時間'] = df['查詢時間'].apply(lambda x: x[:-9])
df['查詢時間'] = pd.to_datetime(df['查詢時間'], format="%Y-%m-%d")
df = df.groupby('查詢時間')['現匯賣出價']
labels = []
values = []
for d in df:
    temp_data = d[1].tolist()
    k_data = [temp_data[0], temp_data[-1], min(temp_data), max(temp_data)]
    labels.append(str(d[0])[:-9])
    values.append(k_data)
draw_kline_pic(title="人民幣和港幣的匯率折算(100港幣)", labels=labels, data_package=values)

def draw_kline_pic(title: str, labels: list, data_package: list):
    """
    K線圖
    :param title:
    :param labels:
    :param data_package:
    :return:
    """
    style = Style(
        title_top="#fff",
        title_pos="left",
        width=1920,
        height=900
    )
    kline = Kline(title=title, **style.init_style)
    kline.add('日K', labels, data_package,
              yaxis_min=78,
              yaxis_max=90,
              yaxis_formatter="元人民幣",
              mark_line=["min", "max"],
              mark_point=["min", "max"],
              is_datazoom_show=True,
              datazoom_type="both",
              datazoom_range=[80, 100])
    kline.render('./file/k_line.html')
複製程式碼

  • 畫圖結果如下:


  1. 開始預測

    • 預測之前各位同學可以先去了解下ARIMA模型(自迴歸積分滑動平均模型)
    • 這部分沒什麼程式碼(時序分析的步驟實在太長了,要考慮週期性、自相關性、反自相關性 balabala的~), 那段太凌亂了就沒有貼出來了,自己上一個程式碼 (用的庫是pyflux)
def model_training_1(df: pd.DataFrame):
    df['查詢時間'] = df['查詢時間'].apply(lambda x: x[:-9])
    df['查詢時間'] = pd.to_datetime(df['查詢時間'], format="%Y-%m-%d")
    df = df.groupby('查詢時間')['現匯賣出價'].mean()
    df = df.to_frame()
    print(df)

    # ARIMA
    model = pf.ARIMA(data=df, ar=2, ma=2, integ=0, target='現匯賣出價', family=pf.Normal())
    x = model.fit("MLE")
    x.summary()
    model.plot_z(figsize=(15, 5))
    model.plot_fit(figsize=(15, 10))
    model.plot_predict_is(h=50, figsize=(15, 5))
    model.plot_predict(h=2, past_values=50, figsize=(15, 5))
    res = model.predict(h=5)
    print(res)
複製程式碼

  • 先看擬合圖也就是plot_fit的圖


  • 再看模型驗證引數


  • 預測圖(用過去50天資料進行預測後幾天的資料)


  • 預測結果


  1. 資料校驗

    • 校驗這塊暫時還沒時間去做,但是看到擬合程度還是很可觀的就懶了~
    • 有空還是會補上的~
  2. 題外話

    • Pyflux倉庫地址: github.com/RJT1990/pyf…
    • Pyflux這個庫是一個專門做時間序列資料分析的庫,很可惜的是他的官網掛了,太久沒有維護了
    • 原來這開源專案還有個很酷炫的官網, 官網上還有example + 圖片現在只剩下git doc了.
    • 參考文件: pyflux.readthedocs.io/en/latest/g…
    • 注: 文件裡頭的圖基本都沒有了,只有剩下公式和程式碼塊,暫且將就著用吧。如果對時序分析預測感興趣的同學可以考慮用一下statsmodels這個統計學的庫,這個文件和工具就比較豐富和科學。

未來開發方向

  • 想往哪就往哪~Peace!