【Python爬虫课程设计】--股票数据爬取+数据分析

发布时间 2023-12-30 16:28:04作者: 2203840434陈睿

一、选题课程背景

  随着互联网技术的发展和信息爆炸的时代,人们对于获取和分析海量数据的需求日益增长。股票市场作为全球经济的重要风向标,其数据信息的获取和分析对于投资者、研究人员以及企业决策者具有重要的参考价值。然而,传统的股票数据分析方法往往受到数据来源限制和数据处理能力的制约,无法充分利用互联网上的海量信息。因此,利用Python爬虫技术抓取互联网上的股票数据,并对其进行深入分析,不仅可以提高数据分析的效率和准确性,还可以为投资者、研究人员提供更加全面和准确的信息,帮助他们做出更加明智的决策。

 

二、选题意义

  Python爬虫课程设计的选题意义在于,通过将Python爬虫技术与股票数据分析相结合,不仅可以帮助学生掌握Python爬虫的基本原理和应用技巧,还可以让学生了解如何利用爬虫技术获取和分析股票数据,从而为实际应用提供支持和帮助。

  股票数据分析的选题意义在于为投资者提供决策支持、揭示市场走势、优化资产配置、提升决策效率、推动金融科技创新以及促进经济发展等方面。通过对股票数据的深入分析,投资者可以从中得到启发,思考和研究市场的发展趋势、行业和公司的经营状况以及投资策略的制定等问题。同时,还可以通过与其他投资者交流和分享经验,不断拓展自己的投资视野和知识储备。

 

三、选题目标

  • 使用baostockAPI获取贵州茅台(股票代码:600519)、京东方 A(股票代码:000725)两支股票的历史行情数据的日 k 线和月 k 线的数据,并对数据进行清洗,如异常值处理、缺失值处理等。
  • 获取两支股票近 2023 年第一天开盘到 2023 年 11 月 30 日的日 k 线数据,分别将该股每日的开盘价、收盘价和最高价、最低价的数据绘制到两张图上,分析股票随时间的波动情况。
  • 对两支股票近 3 年的月 k 线数据进行分析,将该股的每月成交量和成交额绘制到柱状图上,观察股票的交易情况。
  • 根据贵州茅台股票的每日数据,筛选出在最近一年内该股跌涨幅最大以及换手率最大的时间,简要分析换手率和股票价格的关系。
  • 假如你有 100w,你在 2023 年(2023 年第一天开盘到 2023 年 11 月 30 日)初全仓买入了贵州茅台的股票,2023 年 11 月 30 日,你的收益是多少?

 

四、设计方案

数据来源于baostockAPI平台:http://baostock.com/baostock/index.php/A%E8%82%A1K%E7%BA%BF%E6%95%B0%E6%8D%AE

  1. fetch_daily_k_data和fetch_monthly_k_data函数分别获取指定股票在指定时间范围内的日K线和月K线数据,用fetch_stock_data函数通过调用上述两个函数,获取多只股票的日K线和月K线数据;

  1. 数据清洗,clean_data函数将获取到的数据,除日期外数据均转换为数值。

  3.plot_daily_price函数用于可视化每日开盘价、收盘价和最高最低价的波动情况;

  4.plot_monthly_volume_and_amount函数用于绘制每月成交量和成交额的柱状图;

  5.使用fetch_daily_k_data获取贵州茅台最近一年的日K线数据,然后通过计算涨跌幅和换手率找到最大值的日期及对应涨跌幅和换手率;

  6.使用函数calculate_investment_return通过调用函数fetch_daily_k_data获取贵州茅台最近一年的日K线数据,将100w除以贵州有茅台股票在第一天的收盘价,得到购买的股票数量,模拟全仓买入情况,将持有的股票数量乘以最后一天的股票收盘价,得到最终市值,减去初始投资额即为最终受益。

五、大数据分析结果

1.两支股票的历史行情数据的日 k 线和月 k 线的数据,并对数据进行清洗,如缺失值处理以及数据类型转换等。

处理后部分内容如下所示:

  1. 2023 年第一天开盘到 2023 年 11 月 30 日的日 k 线数据,两股每日的开盘价、收盘价和最高价、最低价的数据绘制:

  根据对比可得,贵州茅台相对于京东方A,变化幅度和变化范围明显更大,但是两支股票在大概1-3月和6-9月的范围内,股票的开盘价、收盘价、最高价、最低价的数据相对于3-6月和9-11月会较高一些,两支股票的图像波动情况基本呈现M型;

  1. 近 3 年的月 k 线数据每月成交量和成交额柱状图:

  贵州茅台每月成交量远小于京东方A,但是每月成交额基本与京东方A相近,甚至高于京东方A,由于成交量*成交价格=成交额,说明贵州茅台的股票成交价格应该远高于京东方A的股票成交价格。

  贵州茅台股票近三年每月成交量和成交额虽有波动,在第一年呈现小幅度下降趋势,在第二到第三年基本平稳,在小范围内波动;

京东方A股票近三年每月成交量和成交额总体呈现较大幅度的下降,在近一年基本平稳,在小范围内波动。

  1. 最近一年内贵州茅台股票跌涨幅最大以及换手率最大的时间:

  由于个股的流通盘的大小不一,仅仅用成交金额的简单比较意义不大。在考察成交量时,不仅要看成交股数的多少,更要分析换手率的高低。换手率是指在一定时期内股票的交易量占总发行股本的比例。换手率高一般意味着股票流通性好,进出市场比较容易,具有较强的变现能力。

  股票的换手率突然上升,成交量放大,可能意味着有投资者或者股民在大量买进,股票价格可能会随之上扬。但是如果该股票在持续上涨了一段时间后,换手率又迅速上升,则可能意味着一些获利者要将股票进行套现,股票价格可能会随之下跌。、

  1. 100w全仓买入贵州茅台股票。2023年1月1日至2023年11月30日期间的收益是多少:

  将100w全仓买入贵州茅台股票,经过11个月的时间,最终市值将会提升至1034831.33元,最终收益为34831.33元,最终收益率为7.92%。

 

六、总结

  通过本次的课程选题我学习了如何通过baostock API获取股票数据,利用numpy或者pandas库处理数据,以及如何通过可视化来分析数据。在该实验中遇到的最大问题可能就是通过baostock获取到的股票数据类型均为文本类型。如下图所示,所绘制的日k线开盘价和收盘价可视化图像的y轴是一个非常混乱的状态,图像也很混乱。

将数据导入到excel表格中显示这两列的数据无法进行绘图,数据类型为文本型。在数据清洗阶段添加如下代码,将这几列的数据类型转换为数值类型,成功解决该问题。

 

 

源代码如下:

 

{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import baostock as bs\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "login success!\n",
      "登录响应代码:0\n",
      "登录响应信息:success\n"
     ]
    }
   ],
   "source": [
    "# 登录并显示返回信息\n",
    "lg = bs.login()\n",
    "print('登录响应代码:'+lg.error_code)\n",
    "print('登录响应信息:'+lg.error_msg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1.两支股票的历史行情数据的日 k 线和月 k 线的数据,并对数据进行清洗,如缺失值处理等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 获取日k线数据\n",
    "def fetch_daily_k_data(stock_code, start_date, end_date):\n",
    "    rs = bs.query_history_k_data(\n",
    "        stock_code,\n",
    "        \"date,code,open,high,low,close,volume,amount,turn,pctChg\",\n",
    "        start_date=start_date, end_date=end_date,\n",
    "        frequency=\"d\", adjustflag=\"3\")\n",
    "    data = rs.get_data()\n",
    "    return data\n",
    "\n",
    "# 获取月k线数据\n",
    "def fetch_monthly_k_data(stock_code, start_date, end_date):\n",
    "    rs = bs.query_history_k_data(\n",
    "        stock_code,\n",
    "        \"date,code,open,high,low,close,volume,amount,turn,pctChg\",\n",
    "        start_date=start_date, end_date=end_date,\n",
    "        frequency=\"m\", adjustflag=\"3\")\n",
    "    data = rs.get_data()\n",
    "    return data\n",
    "\n",
    "# 获取股票数据\n",
    "def fetch_stock_data(stock_codes, start_date, end_date):\n",
    "    stock_data = {}\n",
    "    for code in stock_codes:\n",
    "        daily_data = fetch_daily_k_data(code, start_date, end_date)\n",
    "        monthly_data = fetch_monthly_k_data(code, start_date, end_date)\n",
    "        stock_data[code] = {'daily': daily_data, 'monthly': monthly_data}\n",
    "    return stock_data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 数据清洗函数,处理缺失值\n",
    "def clean_data(data, columns,method='backfill'):\n",
    "    for column in columns:\n",
    "        #将数据转换为数值类型\n",
    "        data[column] = pd.to_numeric(data[column], errors='coerce')\n",
    "        #采用向后填充的方式填充缺失值\n",
    "        data[column].fillna(method=method, inplace=True)\n",
    "    return data"
   ]
  },
  {
   "source": [
    "#定义需要清洗的列\n",
    "columns = ['open', 'high', 'low', 'close', 'volume', 'amount', 'pctChg', 'turn']\n",
    "# 清洗数据\n",
    "for code in stock_codes:\n",
    "    data_day[code]['daily'] = clean_data(data_day[code]['daily'], columns)\n",
    "    data_month[code]['monthly'] = clean_data(data_month[code]['monthly'], columns)\n",
    "    #查看清洗后的数据\n",
    "    print('股票代码:'+code)\n",
    "    print('清洗后的日线数据:')\n",
    "    print(data_day[code]['daily'])\n",
    "    print('清洗后的月线数据:')\n",
    "    print(data_month[code]['monthly'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2.获取两支股票近 2023 年第一天开盘到 2023 年 11 月 30 日的日 k 线数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "??",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "??",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "??",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "??",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#将股票每日的开盘价、收盘价和最高价、最低价的数据绘制到两张图上,分析股票随时间的波动情况\n",
    "def plot_daily_price(stock_data):\n",
    "    for code, data in stock_data.items():\n",
    "        # 绘制开盘和收盘价\n",
    "        plt.figure(figsize=(10, 5))\n",
    "        plt.plot(data['daily']['date'], data['daily']['open'], label='Open',color='red')\n",
    "        plt.plot(data['daily']['date'], data['daily']['close'], label='Close',color='green')\n",
    "        plt.title(f'{code} Daily Open and Close')\n",
    "        plt.xlabel('Date')\n",
    "        plt.legend()\n",
    "        plt.xticks([])\n",
    "        plt.show()\n",
    "\n",
    "        # 绘制最高和最低价\n",
    "        plt.figure(figsize=(10, 5))\n",
    "        plt.plot(data['daily']['date'], data['daily']['high'], label='High',color='orange')\n",
    "        plt.plot(data['daily']['date'], data['daily']['low'], label='low',color='blue')\n",
    "        plt.title(f'{code} Daily High and Low')\n",
    "        plt.xlabel('Date')\n",
    "        plt.legend()\n",
    "        plt.xticks([])\n",
    "        plt.show()\n",
    "        \n",
    "#绘制图表\n",
    "plot_daily_price(data_day)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3.对两支股票近 3 年的月 k 线数据进行分析"
   ]
  },
  {
    "cell_type": "code",
    "execution_count": 16,
    "metadata": {},
    "outputs": [
      {
        "data": {
          "image/png": "??",
          "text/plain": [
            "<Figure size 1000x500 with 1 Axes>"
          ]
        },
        "metadata": {},
        "output_type": "display_data"
      },
      {
        "data": {
          "image/png": "",
          "text/plain": [
            "<Figure size 1000x500 with 1 Axes>"
          ]
        },
        "metadata": {},
        "output_type": "display_data"
      },
      {
        "data": {
          "image/png": "??",
          "text/plain": [
            "<Figure size 1000x500 with 1 Axes>"
          ]
        },
        "metadata": {},
        "output_type": "display_data"
      }
    ],
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "4.根据贵州茅台股票的每日数据,筛选出在最近一年内该股跌涨幅最大以及换手率最大的时间,简要分析换手率和股票价格的关系"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "茅台股票跌涨幅最大日期: 2023-11-01, 涨跌幅为:5.7231%\n",
            "茅台股票换手率最大日期: 2023-11-01, 换手率为:0.7415\n"
          ]
        }
      ],
      "source": [
        "# 获取最近一年的贵州茅台每日数据\n",
        "maotai_daily_data = fetch_daily_k_data('sh.600519', '2022-12-01', '2023-11-30')\n",
        "\n",
        "#将换手率和跌涨幅转化为数值类型\n",
        "maotai_daily_data['pctChg'] = pd.to_numeric(maotai_daily_data['pctChg'], errors='coerce')\n",
        "maotai_daily_data['turn'] = pd.to_numeric(maotai_daily_data['turn'], errors='coerce')\n",
        "\n",
        "# 找出跌涨幅最大的日期\n",
        "max_chg_date = maotai_daily_data.loc[maotai_daily_data['pctChg'].idxmax()]['date']\n",
        "max_chg_value = maotai_daily_data['pctChg'].max()\n",
        "\n",
        "# 找出换手率最大的日期\n",
        "max_turn_date = maotai_daily_data.loc[maotai_daily_data['turn'].idxmax()]['date']\n",
        "max_turn_value = maotai_daily_data['turn'].max()\n",
        "\n",
        "print(f'茅台股票跌涨幅最大日期: {max_chg_date}, 涨跌幅为:{max_chg_value}%')\n",
        "print(f'茅台股票换手率最大日期: {max_turn_date}, 换手率为:{max_turn_value}')\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "5.假如你有 100w,你在 2023 年(2023 年第一天开盘到 2023 年 11 月 30 日)全仓买入了贵州茅台的股票,2023 年 11 月 30 日,你的收益是多少?"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {},
      "outputs": [],
      "source": [
        "def calculate_investment_return(stock_code, initial_investment, start_date, end_date):\n",
        "    #获取股票数据\n",
        "    daily_data = fetch_daily_k_data(stock_code, start_date, end_date)\n",
        "    #将开盘价和收盘价转换为数值类型\n",
        "    daily_data['close'] = pd.to_numeric(daily_data['close'], errors='coerce')\n",
        "    daily_data['open'] = pd.to_numeric(daily_data['open'], errors='coerce')\n",
        "    #计算投资收益\n",
        "    initial_price = daily_data.iloc[0]['open']\n",
        "    shares_bought = initial_investment / initial_price\n",
        "    final_value = shares_bought * daily_data.iloc[-1]['close']\n",
        "    profit = final_value - initial_investment\n",
        "\n",
        "    return profit, final_value"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "最终市值:1079216.87元\n",
            "投资收益:79216.87元\n",
            "投资收益率:7.92%\n"
          ]
        }
      ],
      "source": [
        "#计算投资收益\n",
        "profit, final_value = calculate_investment_return('sh.600519', 1000000, '2022-12-01', '2023-11-30')\n",
        "print(f'最终市值:{final_value:.2f}元')\n",
        "print(f'投资收益:{profit:.2f}元')\n",
        "print(f'投资收益率:{profit / 10000:.2f}%')"
      ]
    },

    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "logout success!\n",
            "登出响应代码:0\n",
            "登出响应信息:success\n"
          ]
        }
      ],
      "source": [
        "# 登出并显示返回信息\n",
        "lg = bs.logout()\n",
        "print('登出响应代码:'+lg.error_code)\n",
        "print('登出响应信息:'+lg.error_msg)"
      ]
    }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}