鱼C论坛

 找回密码
 立即注册
查看: 6382|回复: 6

[作品展示] 爬取A股实时信息

[复制链接]
发表于 2021-6-24 19:39:50 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 fc5igm 于 2021-6-29 01:29 编辑
  1. # coding:utf-8
  2. import urllib.request as ulr
  3. from relpath import *
  4. import pymysql
  5. import threading

  6. '''version 3.0'''


  7. def stocks(switch):

  8.         def data_request(market, stockid, switch):
  9.                 # 连接数据库
  10.                 conn = pymysql.connect(
  11.                         host='localhost',
  12.                         port=3306,
  13.                         user='root',
  14.                         password='你的密码',
  15.                         db='你的数据库'
  16.                 )
  17.                 # 拿到游标
  18.                 cursor = conn.cursor()

  19.                 table = {}
  20.                 stop=False
  21.                 # 数据获取
  22.                 # iplist=ip.get_ip()
  23.                 # iplist=['39.106.223.134:80','58.240.52.114:80','218.16.62.152:3128']
  24.                 header = {
  25.                         'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36 Edg/91.0.864.48'}
  26.                 # url='http://quote.eastmoney.com/sz000001.html'
  27.                 url = f'http://11.push2his.eastmoney.com/api/qt/stock/kline/get?cb=jQuery112404820468452785698_1624813216413&secid={market}.{stockid}&ut=fa5fd1943c7b386f172d6893dbfba10b&fields1=f1%2Cf2%2Cf3%2Cf4%2Cf5%2Cf6&fields2=f51%2Cf52%2Cf53%2Cf54%2Cf55%2Cf56%2Cf57%2Cf58%2Cf59%2Cf60%2Cf61&klt=101&fqt=2&end=20500101&lmt=120&_=1624813216485'
  28.                 opener = ulr.build_opener()
  29.                 request = ulr.Request(url, headers=header)
  30.                 response = opener.open(request).read().decode('utf-8', 'ignore')  # decode(dw(url),'ignore')

  31.                 # 数据处理 - 后复权
  32.                 data = response.split(sep=',')

  33.                 try:
  34.                         table['d0'] = data[-11][-10:]  # 日期
  35.                         table['d1'] = data[7][8:12]  # 名称
  36.                         table['d2'] = float(data[-10])  # 开盘价
  37.                         table['d3'] = float(data[-9])  # 收盘
  38.                         table['d4'] = float(data[-8])  # 最高
  39.                         table['d5'] = float(data[-7])  # 最低
  40.                         table['d6'] = float(data[-3])  # 涨跌幅
  41.                         table['d7'] = float(data[-2])  # 涨跌额
  42.                         table['d8'] = int(data[-6])  # 成交量
  43.                         table['d9'] = float(data[-5])  # 成交额
  44.                         table['d10'] = float(data[-4])  # 振幅
  45.                         table['d11'] = float(data[-1].split(sep='"')[0])  # 换手率
  46.                 except ValueError:
  47.                         stop=True

  48.                 if stop==False:
  49.                         # 设置表名前缀
  50.                         if market == '0':
  51.                                 pre = 'sz'
  52.                         elif market == '1':
  53.                                 pre = 'sh'
  54.                         elif market == '2':
  55.                                 pre = 'zs'

  56.                         # 读取表数据
  57.                         sql0 = f'select date from {pre}{stockid} order by date desc limit 1;'
  58.                         # 创建表
  59.                         sql1 = f'CREATE TABLE {pre}{stockid}(date DATE primary key, name char(5), latest_price FLOAT not null default"-1", latest_price_before FLOAT not null default"-1", average_price FLOAT not null default"-1", increase_percentage FLOAT not null default"-1", increase_value FLOAT not null default"-1", trade_times BIGINT not null default"-1", trade_amount FLOAT not null default"-1", amplitude FLOAT not null default"-1", exchange_percentage FLOAT not null default"-1", highest FLOAT not null default"-1", lowest FLOAT not null default"-1", today_start FLOAT not null default"-1", buyer_active BIGINT not null default"-1", seller_active BIGINT not null default"-1")'
  60.                         # 表添加数据行
  61.                         sql2 = f"insert into {pre}{stockid}(date,name,today_start,latest_price,highest,lowest,increase_percentage,increase_value,trade_times,trade_amount,amplitude,exchange_percentage) values('{table['d0']}','{table['d1']}','{table['d2']}','{table['d3']}','{table['d4']}','{table['d5']}','{table['d6']}','{table['d7']}','{table['d8']}','{table['d9']}','{table['d10']}','{table['d11']}');"
  62.                         # 更新表数据
  63.                         sql4 = f"update {pre}{stockid} set latest_price={table['d3']},increase_percentage={table['d6']},increase_value={table['d7']},trade_times={table['d8']},trade_amount={table['d9']},amplitude={table['d10']},exchange_percentage={table['d11']},highest={table['d4']},lowest={table['d5']},today_start={table['d2']} where date='{table['d0']}';"

  64.                         try:
  65.                                 # 对数据库最后一条数据获取
  66.                                 cursor.execute(sql0)
  67.                                 lastdate = cursor.fetchone()
  68.                                 # 与当前日期比对并执行
  69.                                 if lastdate == None or str(lastdate[0]) != table['d0']:
  70.                                         cursor.execute(sql2)
  71.                                 else:
  72.                                         cursor.execute(sql4)
  73.                         except pymysql.err.ProgrammingError:
  74.                                 # 如报错提示不存在表,则创建并填写数据
  75.                                 cursor.execute(sql1)
  76.                                 cursor.execute(sql2)

  77.                         '''插入before'''

  78.                         def before(market, stockid, specified_date):

  79.                                 table = {}

  80.                                 url = f'http://push2.eastmoney.com/api/qt/stock/get?ut=fa5fd1943c7b386f172d6893dbfba10b&invt=2&fltt=2&fields=f43,f57,f58,f169,f261,f170,f46,f44,f51,f168,f47,f164,f163,f116,f60,f45,f52,f50,f48,f167,f117,f71,f161,f49,f530,f135,f136,f137,f138,f139,f141,f142,f144,f145,f147,f148,f140,f143,f146,f149,f55,f62,f162,f92,f173,f104,f105,f84,f85,f183,f184,f185,f186,f187,f188,f189,f190,f191,f192,f107,f111,f86,f177,f78,f110,f262,f263,f264,f267,f268,f250,f251,f252,f253,f254,f255,f256,f257,f258,f266,f269,f270,f271,f273,f274,f275,f127,f199,f128,f193,f196,f194,f195,f197,f80,f280,f281,f282,f284,f285,f286,f287,f292&secid={market}.{stockid}'
  81.                                 # proxy_support=ulr.ProxyHandler({'http':random.choice(iplist)})
  82.                                 # opener=ulr.build_opener(proxy_support)
  83.                                 opener = ulr.build_opener()
  84.                                 request = ulr.Request(url, headers=header)
  85.                                 response = opener.open(request).read().decode('utf-8',
  86.                                                                               'ignore')  # decode(dw(url),'ignore')

  87.                                 # 数据处理 - 前复权
  88.                                 response = response.replace('"', '').replace('{', '').replace('}', '').replace('[', '').replace(
  89.                                         ']', '').replace('\\', '').replace('-', '-1')
  90.                                 data = response.split(sep=',')

  91.                                 for n in range(len(data)):
  92.                                         data[n] = (f'{data[n]}'.split(sep=':'))
  93.                                 # if market in ('0','1','2'): #沪深股票
  94.                                 table['d1'] = float(data[5][2])  # 最新价
  95.                                 table['d2'] = float(data[20][1])  # 均价
  96.                                 table['d15'] = int(data[11][1])  # 外盘
  97.                                 table['d16'] = int(data[54][1])  # 内盘

  98.                                 # 更新表数据
  99.                                 sql4 = f"update {pre}{stockid} set latest_price_before={table['d1']},average_price={table['d2']},buyer_active={table['d15']},seller_active={table['d16']} where date='{specified_date}';"

  100.                                 cursor.execute(sql4)

  101.                         if switch == 0:
  102.                                 pass
  103.                         else:
  104.                                 before(market, stockid, table['d0'])

  105.                         conn.commit()
  106.                 else:
  107.                         pass
  108.                 cursor.close()
  109.                 conn.close()
  110.         with open(relpath('stock_id.txt')) as f:
  111.                 stocks=[]
  112.                 for line in f:
  113.                         stockid=str(line).replace('\n','')
  114.                         if stockid[0]=='0' or stockid[0]=='2' or stockid[0]=='3':
  115.                                 market='0'
  116.                         elif stockid[0]=='5' or stockid[0]=='6' or stockid[0]=='7':
  117.                                 market='1'
  118.                         elif stockid[0]=='9' or stockid[0]=='H':
  119.                                 market='2'
  120.                         elif stockid[0]=='#':
  121.                                 continue
  122.                         #table={'d1':'-1','d2':'-1','d3':'-1','d4':'-1','d5':'-1','d6':'-1','d7':'-1','d9':'-1','d10':'-1','d11':'-1','d15':'-1','d16':'-1'}
  123.                         stocks.append((market,stockid,switch))
  124.                 # 多线程
  125.                 threads=[]
  126.                 for stock in stocks:
  127.                         if len(threads)<=100:
  128.                                 threads.append(
  129.                                         threading.Thread(target=data_request,args=(stock[0],stock[1],stock[2]))
  130.                                 )
  131.                         else:
  132.                                 for thread in threads:
  133.                                         thread.start()
  134.                                 for thread in threads:
  135.                                         thread.join()
  136.                                 threads.clear()
  137.                                 threads.append(
  138.                                         threading.Thread(target=data_request, args=(stock[0], stock[1], stock[2]))
  139.                                 )
  140.                                 continue

复制代码


relpath模块代码(非原创)
  1. def relpath(file):
  2.     """
  3.     Always locate to the correct relative path.
  4.     >>> relpath('proxy_ip.txt')
  5.     'D:\\python\\works\\project\\proxy_ip\\proxy_ip.txt'
  6.     """
  7.     from sys import _getframe
  8.     from pathlib import Path
  9.     frame = _getframe(1)
  10.     curr_file = Path(frame.f_code.co_filename)
  11.     return str(curr_file.parent.joinpath(file).resolve())
复制代码



思路:
最开始想的是用美味汤爬取数据,结果发现数据为js格式,无法直接爬取。
后面又用了selenium爬取数据,结果还是废弃了,因为程序速度太慢。
最后选择了还是直接爬js程序的地址比较好。
selenium爬取数据的话,一支票大概要10秒。
直接对着js的地址爬则不到0.2秒
不过考虑到网上那么多已经废弃无法使用的程序,后续东方财富可能会对js的链接地址做出改变
届时本程序也将会无法使用。
对于此。。如果我还能想起来,到时候我会更新的



用法:
程序配备了网络代理的部分,不过源于免费且好用的代理ip极为稀少,且东方财富网也不太管ip的连续重复访问,默认是关闭的。
如想使用,需要自行开启。
使用本程序之前需要先自行准备一个文本文件,名字改为stock_id.txt,每一行为一个股票代码。将这个文本文件与本程序放于同一目录。
之所以没有将股票池与程序写在一起,主要是为了方便自定义使用。比如如果你想获得全市场的股票信息,那你就做一个包含全市场股票id的文本文件。
如果你只想获取某几个股票的实时信息,那就将该文本精简为只存在该几个股票的代码即可。
如果你使用的是全市场的股票代码文件,那么有可能会有部分股票出现报错。
报错的原因一般是因为该股已经退市或者还未上市导致东方财富网不存在该股信息。
请自行在报错后于运行页输入stockid,查看出现错误的股票代码。对于这些代码你可以选择将他们从你的股票池文件中删除,或者在代码前加一个#
本程序使用了MYSQL数据库,换言之,如想使用本程序,请先自行安装MYSQL数据库。
并于代码中标注了“#连接数据库”的部分填写你的数据库信息。
本程序运行后将获取当前最新的实时股票数据,如当日已经获取过一次,那当天后续所有的数据获取都将是对本条数据的修改,而不是每一次获取都新增一条数据。
如需循环运行的,请自行套一个循环。

更新
1.将爬取的主要数据从前复权改为了后复权,但提供前复权的当日价格
2.添加多线程机制,现在爬取一次全市场股票信息只需要不到30秒了(以我自己的机子为例)
3.以上速度其实还可以继续提升,不过需要使用者提供稳定高速可靠的代理ip。将len(threads)<=100:中的100调高即可获得更快地速度。

题外话
首先是先庆贺,虽然后面的tinker和pygame还没有学,虽然python除了小甲鱼教的这些还有很多,但是通过不到一个月的学习时间完成了如上目标还是觉得值得庆贺。
比较遗憾的是以上代码撰写中没有用到类(小甲鱼类的作业中曾说发一个作品展示),想来我这样应该还不能算是完成了作业。
后续如果有机会,我再修改吧!看看能不能把这个程序整个做成一个类。
当前程序虽然可以正常运行,但是于我本身来说还是不太满意的。打算接下来自修一下多线程的教学,之后再对代码进行优化。
也算是本程序接下来的版本计划吧。
1.自学Xparth,对比urllib的爬取速度的差别,如Xparth更高会重写爬取相关的的代码
2.后续使用多线程机制优化代码
3.阅读numba英文原版帮助文档,看看能不能对第三方模块进行加速
4.当以上三条都完成之后,会将程序整体改为一个类

最后,想要感谢在我学习过程中,论坛里为我耐心解惑的大家,尤其是版主@Twilight6
没有你们的帮助,不会有我今日对python的认知
十分感谢你们。
以上

评分

参与人数 3荣誉 +11 鱼币 +9 贡献 +6 收起 理由
rsj0315 + 1 + 1 感谢楼主无私奉献!
Daniel_Zhang + 5 + 3 + 3 鱼C有你更精彩^_^
昨非 + 5 + 5 + 3

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2021-6-24 19:51:04 | 显示全部楼层
太强了吧,一个月学到这么多
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-6-24 19:51:51 | 显示全部楼层
是0基础的么,我学了两个月才刚到爬虫,太难了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-6-24 19:56:57 | 显示全部楼层
fish_nian 发表于 2021-6-24 19:51
是0基础的么,我学了两个月才刚到爬虫,太难了

是的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-6-24 20:46:21 | 显示全部楼层

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2021-6-29 01:00:42 | 显示全部楼层
更新!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2021-6-29 15:56:58 | 显示全部楼层
一个月,挺厉害的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-21 00:09

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表