Python股票爬虫与机器学习预测实战指南

写在前面:一个以前做的课程设计,忘记看的哪些教程了,东拼西凑的一点东西,给大家一点参考吧。

 

一、概述

本程序旨在采用多线程的方式从东方财富网上爬取三支股票交易数据,并对这些数据进行处理、分析和预测。程序包括数据爬取、数据存储、数据预处理、数据分析、模型训练和结果评估等步骤,最后通过图形用户界面(GUI)展示。

二、设计过程

1.选择数据源

从东方财富网爬取股票代码为'688589'、'688286'、'301369'的历史数据。

2.编写爬虫

编写爬虫脚本,采用多线程的方式从东方财经网爬取股票交易数据。

3.数据存储

将爬取到的三支股票数据以Excel文件的形成存储到脚本所在的目录中,且重复运行程序,会将新爬取的数据覆盖旧数据。

4.数据处理

使用Pandas等数据处理库填充缺失值和剔除异常值。

5.数据分析

计算日收益率、波动率、进行各指标之间的相关性来分析历史交易数据。同时,展示收盘价的走势图、收盘价与移动平均线图、日收益率分布图和相关性矩阵。

6.机器学习预测

使用线性回归模型、随机森林模型、支持向量机模型等多个机器学习算法训练股票价格预测模型。

7.模型评估

使用均方误差、R2、夏普比率等指标量化评估结果。

8.数据展示

使用Matplotlib、Seaborn等数据可视化库展示收盘价的走势图、收盘价与移动平均线图、日收益率分布图和各模型预测结果图,并使用Tkinter GUI框架设计并实现用户界面。

三、程序流程图

代码:

import requests
import json
import pandas as pd
import os
import threading
import numpy as np
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import filedialog, ttk
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.svm import SVR
from sklearn.metrics import mean_squared_error, r2_score


# 从指定市场获取股票数据
def get_stock_data(stock_code, market, start_date, end_date):
    # 根据市场确定股票代码前缀(上海市场为'1.',其他市场为'0.')
    if market == 'sh':
        secid = '1.' + stock_code
    else:
        secid = '0.' + stock_code

    # 设定cookies
    cookies = {
        'st_si': '18111771124744',
        'qgqp_b_id': '3de6f7bdadd3772e02294fc70f2a3ef6',
        'websitepoptg_api_time': '1719145671714',
        'st_asi': 'delete',
        'HAList': f'ty-{1 if market == "sh" else 0}-{stock_code}-%u6C64%u59C6%u732B',
        'st_pvi': '30936530594296',
        'st_sp': '2024-06-22%2017%3A13%3A27',
        'st_inirUrl': 'https%3A%2F%2Fwww.bing.com%2F',
        'st_sn': '56',
        'st_psi': '20240624024651837-113200313000-5485613136',
    }

    # 设定HTTP请求头
    headers = {
        'accept': '*/*',
        'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,fr-CA;q=0.5,fr;q=0.4',
        'referer': 'https://quote.eastmoney.com/kcb/688286.html',
        'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Microsoft Edge";v="126"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
        'sec-fetch-dest': 'script',
        'sec-fetch-mode': 'no-cors',
        'sec-fetch-site': 'same-site',
        'user-agent':
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0',
    }

    # 设定请求参数
    params = {
        'cb': 'jQuery351011861680713642775_1719168411615',
        'secid': secid,  # 股票代码
        'beg': start_date,  # 开始日期
        'end': end_date,  # 结束日期
        'ut': 'fa5fd1943c7b386f172d6893dbfba10b',
        'fields1': 'f1,f2,f3,f4,f5,f6',
        'fields2': 'f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61',
        'klt': '101',
        'fqt': '1',
        'lmt': '120',
        '_': '1719168411647',
    }

    # 发起GET请求并获取响应
    response = requests.get('https://push2his.eastmoney.com/api/qt/stock/kline/get', params=params, cookies=cookies,
                            headers=headers)
    return response


# 将响应数据转换为Pandas DataFrame
def store_data_to_pandas(response):
    # 去除响应文本中的JavaScript回调函数名部分,获取纯JSON数据
    rep_text = response.text.replace('jQuery351011861680713642775_1719168411615(', '').replace(');', '')
    # 解析JSON数据
    json_data = json.loads(rep_text)
    # 提取股票K线数据
    stock_data = json_data['data']['klines']
    # 初始化Pandas DataFrame,并设置列名
    pd_data = pd.DataFrame(
        columns=['股票代码', '股票名称', '日期', '开盘价', '收盘价', '最高价', '最低价', '成交量', '成交额', '振幅',
                 '涨跌幅%', '涨跌额'])
    # 遍历股票数据,填充到DataFrame中
    for i in range(len(stock_data)):
        pd_data.loc[i] = [
            json_data['data']['code'],
            json_data['data']['name'],
            stock_data[i].split(',')[0],
            stock_data[i].split(',')[1],
            stock_data[i].split(',')[2],
            stock_data[i].split(',')[3],
            stock_data[i].split(',')[4],
            stock_data[i].split(',')[5],
            stock_data[i].split(',')[6],
            stock_data[i].split(',')[7],
            stock_data[i].split(',')[8],
            stock_data[i].split(',')[9]
        ]
    return pd_data


# 将股票数据保存为Excel文件
def getxlsx(stock_code, market, start_date, end_date):
    # 获取当前脚本所在目录
    current_dir = os.path.dirname(__file__)
    final_data = pd.DataFrame(
        columns=['股票代码', '股票名称', '日期', '开盘价', '收盘价', '最高价', '最低价', '成交量', '成交额', '振幅',
                 '涨跌幅%', '涨跌额'])
    tmp_value = store_data_to_pandas(get_stock_data(stock_code, market, start_date, end_date))
    final_data = pd.concat([final_data, tmp_value])
    # 将数据保存为Excel文件,文件名包含股票代码、市场、开始日期和结束日期信息
    final_data.to_excel(os.path.join(current_dir, f"{stock_code}_{market}_{start_date}_{end_date}.xlsx"), index=False)


# 股票数据爬取并存储为Excel文件
def worker(stock, market, start_date, end_date):
    try:
        # 调用getxlsx函数来获取并保存数据
        getxlsx(stock, market, start_date, end_date)
    except Exception as e:
        # 捕获异常并打印错误信息
        print("爬取失败,错误股票代码" + stock, e)
    else:
        print("股票" + stock + "爬取成功")


# 数据预处理
def preprocess_data(df):
    # 填充缺失值,使用前向填充
    df.fillna(method='ffill', inplace=True)
    # 去除离群点(根据收盘价与平均收盘价的差值与标准差的比例)
    df = df[(np.abs(df['收盘价'] - df['收盘价'].mean()) / df['收盘价'].std()) < 3]
    return df


# 数据分析
def analyze_data(df):
    # 将日期列转换为日期类型,并设置为DataFrame的索引
    df['日期'] = pd.to_datetime(df['日期'])
    df.set_index('日期', inplace=True)

    # 计算日收益率
    df['日收益率'] = df['收盘价'].pct_change()

    # 计算波动率(20日滚动标准差)
    df['波动率'] = df['日收益率'].rolling(window=20).std()
    print("前10个波动率数据:")
    print(df['波动率'].dropna().head(10))

    # 相关性分析
    correlation_matrix = df[
        ['开盘价', '收盘价', '最高价', '最低价', '成交量', '成交额', '振幅', '涨跌幅%', '涨跌额']].corr()

    # 数据可视化
    # 设置字体为SimHei,避免中文乱码
    plt.rcParams["font.sans-serif"] = ['SimHei']

    # 设置正常显示符号
    plt.rcParams["axes.unicode_minus"] = False

    # 收盘价走势图
    plt.figure(figsize=(14, 7))
    plt.plot(df['收盘价'])
    plt.title('收盘价走势')
    plt.xlabel('日期')
    plt.ylabel('收盘价')
    plt.show()

    # 收盘价与移动平均线图
    df['MA20'] = df['收盘价'].rolling(window=20).mean()
    df['MA50'] = df['收盘价'].rolling(window=50).mean()

    plt.figure(figsize=(14, 7))
    plt.plot(df['收盘价'], label='收盘价')
    plt.plot(df['MA20'], label='MA20')
    plt.plot(df['MA50'], label='MA50')
    plt.title('收盘价与移动平均线')
    plt.xlabel('日期')
    plt.ylabel('价格')
    plt.legend()
    plt.show()

    # 日收益率分布图
    plt.figure(figsize=(14, 7))
    plt.hist(df['日收益率'].dropna(), bins=50, alpha=0.75)
    plt.title('日收益率分布')
    plt.xlabel('日收益率')
    plt.ylabel('频率')
    plt.show()

    # 打印相关性矩阵
    print("相关性矩阵:")
    print(correlation_matrix)

    return df


# 训练多个模型并返回结果
def train_multiple_models(df):
    df['label'] = df['收盘价'].shift(-1)  # 将下一个交易日的收盘价作为标签
    df.dropna(inplace=True)  # 删除含有NaN的行

    x = df[['收盘价', 'MA20', 'MA50']]  # 提取特征
    y = df['label']  # 提取标签

    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)  # 划分训练集和测试集

    # 标准化
    scaler = StandardScaler()
    x_train_scaled = scaler.fit_transform(x_train)
    x_test_scaled = scaler.transform(x_test)

    models = {
        'LinearRegression': LinearRegression(),  # 线性回归模型
        'RandomForest': RandomForestRegressor(),  # 随机森林模型
        'SVR': SVR()  # 支持向量回归模型
    }

    results = {}  # 初始化存储结果的字典

    for name, model in models.items():
        model.fit(x_train_scaled, y_train)  # 训练模型
        y_pred = model.predict(x_test_scaled)  # 预测测试集
        mse = mean_squared_error(y_test, y_pred)  # 计算均方误差
        r2 = r2_score(y_test, y_pred)  # 计算R^2得分
        results[name] = {'model': model, 'mse': mse, 'r2': r2, 'y_pred': y_pred}

    return results, y_test


# 评估模型
def evaluate_models(results, y_test):
    for name, result in results.items():
        print(f'{name} 模型评估:')
        print(f'均方误差: {result["mse"]}')
        print(f'R^2 得分: {result["r2"]}')

        # 计算夏普比率
        returns = np.diff(result['y_pred']) / result['y_pred'][:-1]
        sharpe_ratio = np.mean(returns) / np.std(returns) * np.sqrt(252)
        print(f'夏普比率: {sharpe_ratio}')

        # 可视化
        plt.rcParams["font.sans-serif"] = ['SimHei']
        plt.rcParams["axes.unicode_minus"] = False
        plt.figure(figsize=(14, 7))
        plt.plot(y_test.values, label='实际值')
        plt.plot(result['y_pred'], label='预测值')
        plt.title(f'{name} 模型实际值与预测值比较')
        plt.xlabel('样本')
        plt.ylabel('收盘价')
        plt.legend()
        plt.show()


# 分析不同市场和时间段
def compare_markets_timeframes(dfs, labels):
    """
    对不同市场和时间段的股票数据进行比较分析
    参数:
    dfs (list of pd.DataFrame): 包含不同市场和时间段数据的DataFrames
    labels (list of str): 数据对应的标签
    """
    for df, label in zip(dfs, labels):
        df['日期'] = pd.to_datetime(df['日期'])
        df.set_index('日期', inplace=True)
        df['日收益率'] = df['收盘价'].pct_change()
        df['波动率'] = df['日收益率'].rolling(window=20).std()

    # 可视化比较
    plt.rcParams["font.sans-serif"] = ['SimHei']
    plt.rcParams["axes.unicode_minus"] = False
    plt.figure(figsize=(14, 7))
    for df, label in zip(dfs, labels):
        plt.plot(df.index, df['收盘价'], label=label)
    plt.title('不同市场和时间段的收盘价比较')
    plt.xlabel('日期')
    plt.ylabel('收盘价')
    plt.legend()
    plt.show()

    # 打印统计特性比较
    for df, label in zip(dfs, labels):
        print(f'{label} 的统计特性:')
        print(df.describe())


# 显示图形用户界面
def show_gui():
    def load_file():
        # 弹出文件选择对话框,选择Excel文件
        file_path = filedialog.askopenfilename()
        data = pd.read_excel(file_path)
        data = preprocess_data(data)
        data = analyze_data(data)
        results, y_test = train_multiple_models(data)
        evaluate_models(results, y_test)

    def load_and_compare():
        # 选择多个文件进行比较
        file_paths = filedialog.askopenfilenames()
        dfs = []
        labels = []
        for file_path in file_paths:
            data = pd.read_excel(file_path)
            data = preprocess_data(data)
            dfs.append(data)
            labels.append(os.path.basename(file_path).split('.')[0])
        compare_markets_timeframes(dfs, labels)

    # 设计界面
    root = tk.Tk()
    root.title("股票数据分析与预测")

    frame = ttk.Frame(root, padding="10")
    frame.grid(row=0, column=0, sticky="wens")
    ttk.Label(frame, text="股票数据分析与预测工具").grid(row=0, column=0, columnspan=2)

    ttk.Button(frame, text="加载数据", command=load_file).grid(row=1, column=0, columnspan=2)
    ttk.Button(frame, text="比较数据", command=load_and_compare).grid(row=2, column=0, columnspan=2)

    root.mainloop()


def main():
    stock_list = [
        ('688589', 'sh', '20200101', '20250101'),
        ('688286', 'sh', '20200101', '20250101'),
        ('301369', 'sz', '20200101', '20250101')
    ]
    threads = []
    for stock, market, start_date, end_date in stock_list:
        t = threading.Thread(target=worker, args=(stock, market, start_date, end_date))
        threads.append(t)
        t.start()

    for t in threads:
        t.join()

    show_gui()


if __name__ == '__main__':
    main()

四、操作说明

1.安装依赖

开发环境:Anaconda python3.9

确保所需的库:requests、json、pandas、numpy、matplotlib、tkinter、scikit-learn

2.配置信息

在程序配置文件中设置数据源、股票代码等相关信息。

3.运行程序

运行main.py文件,程序将自动执行数据爬取、存储流程,若数据爬取成功,将打印“股票+股票代码+爬取成功”,并弹出GUI界面。

点击“加载数据”,弹出文件选择对话框,选择某个Excel文件处理(该excel若爬取成功,将存储到脚本所在的目录中),将自动执行数据处理、数据分析、机器学习预测、模型评估和数据展示等流程。

这里选择了688286_sh_20200101_20250101.xlsx文件,打印结果为:

其中两张结果图为(其余结果图在这里不展示):

点击GUI界面中的“比较数据”,弹出文件选择对话框,选择多个Excel文件进行比较,将自动对不同市场和时间段的股票数据进行比较分析,展示不同市场和时间段的收盘价比较图,并打印统计特性比较。

4.查看结果

在本地文件上查看原始数据,在pycharm上查看分析结果和预测结果的图表展示。

作者:假发,桂!

物联沃分享整理
物联沃-IOTWORD物联网 » Python股票爬虫与机器学习预测实战指南

发表回复