获取本项目代码,请扫描下方二维码,发送关键字『基金』,即可获取!
qrcode_for_gh_a047d112702c_344.jpgimage.png

导言

最近股市有点牛市的味道,没有投资股票的朋友,一般也会投资基金,那一般是定投的吧

定投的好处就是不需要额外的时间去管理,自动扣款就好了

然而基金投资有一句话叫做:“越低越投”,基金定投有一个不好的地方就是,如果某一天基金净值大跌了,但恰好这一天又不是你的定投日,那么这个时候加点仓也许是个不错的选择,比如上这周的周五(2020-07-24),大部分基金净值跌了超过 4% ,以这个为例。
image.png

那么,我们是否可以写一个程序,在交易日的时候自动采集基金的涨跌情况,设定阈值,然后通知我们,我们再手动决定是否加仓或者卖出

images.jpg

笔者尝试了一下,所以形成了这篇文章,文末有本文代码获取的方法,或者大家可以留言告诉我你想关注的基金代码和你的邮箱(留言我会在后台看到,不会展示出来!),我会在交易日每天 14.50 的时候发邮件提醒你。

下面开始我的表演~~~~

邮箱配置

想要使用自动发邮件的功能,那么就必须有一个发件方,这个发件方的配置信息可以在
邮箱设置中进行配置(这里使用的是 QQ 邮箱,其他邮箱的配置类似)

配置过程如下所示:
image.png
生成授权码后复制下来,后面会用到

image.png

这里说一个 QQ 邮箱的使用技巧,QQ 邮箱是可以一邮多号的,什么意思呢?
就是同一个邮箱可以有不同的账号,但是最终收件的地方都是同一个,大家可以在 QQ 邮箱/设置/账户中进行配置,我就是 @qq.com@foxmail.com 配合使用的,非常方便。

image.png

基金信息获取

配置好邮箱后,我们就来获取我们想要的信息,我们想要每天监控某个基金的涨跌幅信息,可以从基金网站上用爬虫获取,我们使用的是新浪财经http://stocks.sina.cn/fund/?code=160119&vt=4#

image.png

我们按 F12 进行调试,我们只需要涨跌幅信息,所以爬取起来也比较简单

image.png

所以我们只在这个网站上 http://stocks.sina.cn/fund/?code=160119&vt=4# 抓取基金的涨跌幅信息,要获取基金的其他信息,如基金的具体名称的话,我们使用这个网站:http://fund.eastmoney.com/js/fundcode_search.js ,里面涵盖大部分基金的文本信息
image.png

代码实现

代码的基本思路是,抓取若干个基金(可以进行配置)的涨跌幅信息,然后设置一个阈值(如涨或者跌超过 1%)就提醒我们。

  1. import smtplib
  2. import email
  3. # 负责构造文本
  4. from email.mime.text import MIMEText
  5. # 负责构造图片
  6. from email.mime.image import MIMEImage
  7. # 负责将多个对象集合起来
  8. from email.mime.multipart import MIMEMultipart
  9. from email.header import Header
  10. import requests
  11. from bs4 import BeautifulSoup
  12. import re
  13. import time
  14. def get_fund_info(fund_code):
  15. """
  16. 获取基金涨跌幅信息:信息来源(新浪财经 http://stocks.sina.cn/fund/)
  17. fund_code:为基金代码,若该基金不存在,返回 False,否则返回 涨跌幅比例
  18. """
  19. headers = {
  20. "Cookie": 'ustat=__14.28.56.65_1590560309_0.89300000; genTime=1590560309; SINAGLOBAL=4905578110989.475.1590560312465; Apache=3574138427052.4756.1595941642394; ULV=1595941642397:5:2:1:3574138427052.4756.1595941642394:1594733801049; sinaH5EtagStatus=y; vt=99; historyRecord={"href":"http://stocks.sina.cn/fund/","refer":""}',
  21. "Host": "stocks.sina.cn",
  22. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"
  23. }
  24. url = url = "http://stocks.sina.cn/fund/?code={}&vt=4#".format(fund_code)
  25. try:
  26. r = requests.get(url, headers)
  27. r.encoding="UTF-8"
  28. soup = BeautifulSoup(r.text, "html.parser")
  29. result = soup.findAll(attrs={"class": "j_fund_valExt"})
  30. if len(result) == 1:
  31. pattern = "(?<=>)(.+)(?=<)"
  32. result = re.findall(pattern, str(result[0]))[0]
  33. return float(result.split("%")[0])
  34. else:
  35. return False
  36. except:
  37. return False
  38. def send_mail(fund_code, receiver_mail, thresh=1,user=None):
  39. """
  40. fund_code:基金代码
  41. receiver_mail:收件人邮箱
  42. thresh:阈值
  43. user:用户名标志
  44. """
  45. # SMTP服务器,这里使用qq邮箱
  46. mail_host = "smtp.qq.com"
  47. # 发件人邮箱
  48. mail_sender = "xxx@qq.com"
  49. # 邮箱授权码
  50. mail_license = "xxxxx"
  51. # 收件人邮箱,可以为多个收件人
  52. mail_receivers = [receiver_mail] # ["******@qq.com","******@foxmail.com"]
  53. print(mail_receivers)
  54. # 构建 MIMEMultipart 对象代表邮件本身,可以往里面添加文本、图片、附件等
  55. mm = MIMEMultipart('related')
  56. # 邮件主题
  57. subject_content = """基金"""
  58. # 设置发送者,注意严格遵守格式,里面邮箱为发件人邮箱
  59. mm["From"] = "sender_name<xxx@qq.com>"
  60. # 设置接受者,注意严格遵守格式,里面邮箱为接受者邮箱
  61. # 多个接受者 "receiver_1_name<******@qq.com>,receiver_2_name<******@foxmail.com>"
  62. mm["To"] = "receiver_1_name<{}>".format(receiver_mail)
  63. # 设置邮件主题
  64. mm["Subject"] = Header(subject_content, 'utf-8')
  65. body_content = "现在是 {} \n\n".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
  66. for code in fund_code:
  67. rate = get_fund_info(code)
  68. # print(date, rate)
  69. # 邮件正文内容
  70. if rate:
  71. ##### 根据基金代码获取基金信息
  72. headers = {
  73. "Cookie":
  74. "qgqp_b_id=f8b59df051caea02b176f6d76db75887; EMFUND1=null; EMFUND2=null; EMFUND3=null; EMFUND4=null; EMFUND5=null; EMFUND6=null; EMFUND7=null; st_si=92310565820236; st_asi=delete; searchbar_code=160119; EMFUND0=null; EMFUND8=07-14%2021%3A54%3A31@%23%24%u5357%u65B9%u4E2D%u8BC1500ETF@%23%24510500; EMFUND9=07-25 23:07:36@#$%u5357%u65B9%u4E2D%u8BC1500ETF%u8054%u63A5A@%23%24160119; ASP.NET_SessionId=5ljqqn1s20zpfryhuw5fx4jw; st_pvi=06954122844047; st_sp=2020-05-20%2007%3A32%3A46; st_inirUrl=https%3A%2F%2Fwww.google.com%2F; st_sn=2; st_psi=20200725230736417-0-5210348614",
  75. "User-Agent":
  76. "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"
  77. }
  78. url = "http://fund.eastmoney.com/js/fundcode_search.js"
  79. r = requests.get(url, headers)
  80. info = re.findall("(\[.*?\])", r.text[9:-2])
  81. fund_info = list(filter(lambda x:x.replace("\"", "").replace("[", "").replace("]", "").split(",")[0]==code, info))[0]
  82. if abs(rate) > thresh:
  83. temp = """{} 基金 \n 涨幅为 {}%, \n \t \t ### 涨跌幅超过阈值!!! ### \n\n""".format(fund_info, rate)
  84. else:
  85. temp = """{} 基金 \n 涨幅为 {}% \n\n""".format(fund_info, rate)
  86. else:
  87. temp = """代码为 {} 基金 \n 不存在!!!""".format(code)
  88. body_content += temp
  89. # 构造文本,参数1:正文内容,参数2:文本格式,参数3:编码方式
  90. message_text = MIMEText(body_content, "plain", "utf-8")
  91. # 向MIMEMultipart对象中添加文本对象
  92. mm.attach(message_text)
  93. # 创建SMTP对象
  94. stp = smtplib.SMTP()
  95. # 设置发件人邮箱的域名和端口,端口地址为25
  96. stp.connect(mail_host, 25)
  97. # set_debuglevel(1)可以打印出和SMTP服务器交互的所有信息
  98. stp.set_debuglevel(0)
  99. # 登录邮箱,传递参数1:邮箱地址,参数2:邮箱授权码
  100. stp.login(mail_sender, mail_license)
  101. # 发送邮件,传递参数1:发件人邮箱地址,参数2:收件人邮箱地址,参数3:把邮件内容格式改为str
  102. stp.sendmail(mail_sender, mail_receivers, mm.as_string())
  103. print("邮件发送成功")
  104. # 关闭SMTP对象
  105. stp.quit()
  106. # 基金编码,收件邮箱,阈值,用户名(做标记)
  107. send_mail(fund_code=["511010", "160119"], receiver_mail="xxxxx@foxmail.com", thresh=1, user="LSJ")

服务器部署

写好程序后,我们自然想让他自动操作,所以,我们就把他部署到服务器中,在指定时间给我们发邮件就好了。

笔者本来打算使用 Docker 镜像进行部署的,但是基本的 Python 环境的 Dockers 镜像都好几百 M,运行我们这个程序实在有点浪费,再者,我们程序所用到的包都是基本的 Python 包,不会对我们的环境产生影响,所以,我们就只单纯的使用 Linux 命令来配置程序的定时运行。

笔者使用的是腾讯云服务器,本以为把程序上传到服务器运行就可以了,但是,发现自己还是太年轻
但笔者把程序放到服务器时,发现根本发不了邮件,找了一下原因,是因为发邮件的端口(25)被腾讯封了,如果要使用邮件系统的话,必须解封,其次,腾讯要求不能使用云主机之间 SMTP 发邮件,否则就永久封禁 25 端口,这不就是告诉大家不准在本地架邮件服务器吗。。。。

image.png

为了之前那么辛苦调试程序,我决定拿出我的另外一台服务器,秒秒钟解决问题

其实我们的部署步骤非常的简单,只需要在服务器上设置在 周一到周五 14.50 左右运行一下程序即可(周一到周五才有交易,且 15 前交易为当天的净值)

首先,需要安装一下基本的 Python 包

pip install requests
pip install bs4

其次,我们需要设置一下定时任务,使用到的命令是:crontab

一些基本命令如下

export EDITOR="/usr/bin/vim"  # 设置 Linux 环境下使用 vim 编辑器编辑 crontab 文件
crontab -e # 创建定时任务
crontab -l # 查看定时任务

service cron restart # 重启crontab 定时任务 
sudo /etc/init.d/cron start # 开启定时任务
sudo /etc/init.d/cron stop  # 停止定时任务
service cron status  # 查看定时任务状态
  • 创建定时任务
    crontab -e
    
    然后修改下面的信息
    50 14 * * 1-5 bash /root/fund/fund_code.sh > /root/fund/log.txt 2>&1  # 这句话的意思是 每周一到周五14点50运行一下脚本程序,并把输入写入 log 文件中
    
    image.png
    其中 fund_code.sh 是脚本命令,内容如下, fund_code.py 是我们爬取信息发邮件的程序,/root/anaconda3/bin/python3 的意思是使用 anaconda 的 python 环境来运行我们的程序
    注意:设计脚本的时候,涉及到路径的,最好使用绝对路径
    image.png

上面程序中 50 14 * * 1-5 每个值的含义如下:

*    *    *    *    *
-    -    -    -    -
|    |    |    |    |
|    |    |    |    +----- 星期中星期几 (0 - 7) (星期天 为0)
|    |    |    +---------- 月份 (1 - 12) 
|    |    +--------------- 一个月中的第几天 (1 - 31)
|    +-------------------- 小时 (0 - 23)
+------------------------- 分钟 (0 - 59)

同时我们也可以使用 网站 https://crontab.guru/ 来帮助我们设计合适的时间
image.png

  • 重启一下定时任务
    service cron restart
    
    注意:笔者在调试的时候,遇到定时任务不执行,大家如果也遇到,首先看看 python 程序是否正确,还是不行的话,可以参考:https://blog.csdn.net/qfikh/article/details/78202449 进行解决

参考:

Python自动发邮件.md