上市公司重要公告集锦抓取

在金融机构工作,每天早晨的晨会不可避免,晨会主要的环节是了解当天的上市公司公告。本文提供一个简易实现,自动抓取公告集锦,并邮件及时推送。

爬虫主要功能结构

notice_montage 可以做到拆箱即用。主要包括如下功能:

  • 代码实现
    • 初始化配置文件及日志
    • 使用requests抓取网页页面
    • 解析入口页,解析公告页内容
    • 分析公告页内容并进行一些业务处理
    • 邮件通知推送
  • 代码部署
    • windows机器使用定时计划自动执行
    • 阿里云服务自动执行

代码实现

1.1 初始化配置及日志

项目含有邮件地址等敏感信息,将其独立出来形成配置文件,更利于项目部署。对配置文件利用ConfigParser模块进行解析,代码如下:

<pre>
def get_config_parser():
config_file_path = "notice_montage.ini"
cf = ConfigParser.ConfigParser()
cf.read(config_file_path)
return cf

解析配置

def init_config():
cf = get_config_parser()
global DEBUG, INTERVAL, WEBSITE
INTERVAL = int(cf.get("timeconf", "interval"))
DEBUG = cf.get("urlconf", "debug") == 'True'
WEBSITE = cf.get("urlconf", "website")
</pre>

配置文件notice_montage.ini,内容如下:

<pre>
[urlconf]
website = http://www.ccstock.cn/meiribidu/jiaoyitishi/
debug = True

[timeconf]
interval = 10

[mailconf]

接收通知的邮箱,多个邮箱使用,分割

to_list = xxx@foxmail.com ,xxx@qq.com

设置服务器

mail_host = smtp.exmail.qq.com

替换为发件邮箱用户名

mail_username = xxx

发件邮箱用户名

mail_user = xxx

发件邮箱口令密码

mail_pass = xxx

发件箱的后缀

mail_postfix = xxx
</pre>

爬虫部署后,自动运行,适当的记录一些文件日志,有利于追踪爬虫的运作运行状况,做到有迹可查。日志记录利用logging模块。

<pre>

日志记录器

logger = logging.getLogger()
def init_log():
if DEBUG:
handler = logging.StreamHandler()
else:
handler = logging.FileHandler("notice_montage.log")
formatter = logging.Formatter(
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
</pre>

测试情况下,日志输出到控制台,会更方便调试;正式部署后,输出到文件,要进行切换,直接修改notice_montage.ini中的debug值。

1.2 使用requests抓取网页页面

<pre>
def download_get_html(url, charset="utf-8", timeout=10, num_retries=3):
UA = random.choice(user_agent_list)
headers = {
'User-Agent': UA,
'Content-Type': 'text/html; charset=' + charset
}
try:
response = requests.get(url, headers=headers,
timeout=timeout)
response.encoding = charset
if response.status_code == 404:
logger.debug('get 404: %s ', url)
return None
else:
logger.debug('get : %s ', url)
return response.text
except:
if num_retries > 0:
time.sleep(10)
logger.debug('正在尝试,10S后将重新获取倒数第 %d 次', num_retries)
return download_get_html(url, charset, timeout, num_retries - 1)
else:
logger.debug('尝试也不好使了!取消访问')
return None
</pre>

download_get_html 是一个和业务无关的工具函数,主要使用request的get方法下载指定URL的内容。为了让下载更友好,模拟了User-Agent,并自动尝试3次。

1.3 使用BeautifulSoup解析页面内容

使用download_get_html获取页面内容后,使用BeautifulSoup对其进行解析,获取需要的信息。

解析入口列表页内容

入口页,是所有交易公告的列表清单,使用chrome的开发者工具,查看页面代码如下图:

Paste_Image.png

列表按照时间进行倒序排列,晨报只需要最新的一条公告集锦页面,所以只需要找到class为listMain的div的第一个li类型子元素,代码如下:

<pre>

获取当期集锦的url

def parser_list_page(html_doc, now):
soup = BeautifulSoup(html_doc, 'lxml', from_encoding='utf-8')
# 只找第一个标签
tag = soup.find("div", class_="listMain").find("li")
link_tag = tag.find("a")
span_tag = tag.find("span")
page_url = link_tag['href']
# 截取日期
date_string = span_tag.string[0:10]
if date_string == now:
return page_url
else:
return None
</pre>

now参数为当期的时间,用于核对当期公告是否更新。已更新则返回次日集锦链接,未更新则返回空。

解析内容页内容

获取公告集锦页地址后,继续使用download_get_html获取内容页内容,如下图:

Paste_Image.png

只需要找到id为newscontent的div即可,代码如下:

<pre>
def parser_item_page(html_doc, now):
soup = BeautifulSoup(html_doc, 'lxml', from_encoding='utf-8')
# title = soup.find("h1").string
newscontent = soup.find("div", id="newscontent")
html = newscontent.prettify()
return html
</pre>

1.4 分析页面内容

公告中数字都采用统计技术,并不利用阅读,我们利用正则将其进行处理。
例如:
<pre>
杰克股份(603337)7月23日晚间公告,截至2017年7月21日,公司2017年员工持股计划通过二级市场买入方式增持公司股票3,468,534股,占公司已发行股本1.68%。成交合计金额133,786,450.45元,成交均价38.57元。
</pre>

将其中的股数和成交金额进行转换,转换后如下:

<pre>
杰克股份(603337)7月23日晚间公告,截至2017年7月21日,公司2017年员工持股计划通过二级市场买入方式增持公司股票3,468,534股(346.8534万股),占公司已发行股本1.68%。成交合计金额133,786,450.45元(1.3378645045亿元),成交均价38.57元。
</pre>

这样阅读起来更符合习惯。格式化股份数代码如下:

<pre>

格式化股份计数

def transform_gu(lineText):
p = re.compile(u"[\d,]\d+股")
searchObj = re.findall(p, lineText)
if searchObj:
for x in xrange(0, len(searchObj)):
s1 = searchObj[x]
ns = filter(lambda ch: ch in '0123456789', s1)
nb = float(ns)
if nb >= 100000000:
s2 = str(nb / 100000000) + "亿股"
lineText = lineText.replace(s1, s1 + "(" + s2 + ")")
elif nb >= 10000:
s2 = str(nb / 10000) + "万股"
lineText = lineText.replace(s1, s1 + "(" + s2 + ")")
return lineText
</pre>

核心正则代码<code>u"[\d,]\d+股"</code>,表示至少含有任意个数字+,,然后连接至少一个数字,以“股”字结尾,匹配上3,468,534股这样的字符
串。

还可以对公告集锦匹配自选股池,做重点提醒;对大股东增持的利好消息,进行特殊提示。篇幅有限,涉及具体业务,处理方法也比较一致,本文就不进行展示。

1.5 邮件通知推送

公告获取后,立即推送给目标用户,做到快人一步。

<pre>
def send_notice_mail(html, now):
cf = get_config_parser()
to_list = cf.get("mailconf", "to_list").split(",")
mail_host = cf.get("mailconf", "mail_host")
mail_username = cf.get("mailconf", "mail_username")
mail_user = cf.get("mailconf", "mail_user")
mail_pass = cf.get("mailconf", "mail_pass")
mail_postfix = cf.get("mailconf", "mail_postfix")
me = "AStockMarketNoticeWatcher" + "<" +
mail_username + "@" + mail_postfix + ">"
msg = MIMEMultipart()
subject = now + ' 日 - 二级市场重要公告集锦'
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = me
msg['To'] = ";".join(to_list)
mail_msg = html
# 邮件正文内容
msg.attach(MIMEText(mail_msg, 'html', 'utf-8'))

try:
    server = smtplib.SMTP()
    server.connect(mail_host)
    server.ehlo()
    server.starttls()
    server.login(mail_user, mail_pass)
    server.sendmail(me, to_list, msg.as_string())
    server.close()
    logger.debug('sent mail successfully')
except smtplib.SMTPException, e:
    logger.debug('Error: 无法发送邮件 %s ', repr(e))

</pre>

代码部署

2.1 windows 系统下定时任务自动执行

windows的定时任务,需要先编写bat脚本,代码非常简单,类似<code>python notice_montage.py</code>,autorun.bat 代码如下:

<pre>
:: 自动运行脚本
:: 需更换为本机路径
c:\python27\python.exe D:\xampp\htdocs\ding\morning\notice_montage.py %*
</pre>

在自动执行前,记得切换notice_montage.ini的debug值为False,留下日志文件,方便跟踪结果。

打开【计划任务程序】,选择右侧【操作】中的【创建任务】

Paste_Image.png

需要注意的是,勾选【不管用户是否登录都要运行】,这样系统待机状态下也可以执行。【触发器】页签中,新建触发器:

Paste_Image.png

根据需要,设置成每天定点到10点即可。(实际代码中,进行了自动尝试,如果公告10点没更新,会10分钟后尝试一次,自动尝试3次)

【操作】页签中,新建操作:

Paste_Image.png

程序和脚本,选择前面创建的autorun.bat

确定后,就可以自动运行了,每天收到公告。

2.2 阿里云服务器自动执行

服务器部署好python/virtualenv后,编写crontab命令

<pre>
[root@iZ253amoxhcZ morning]# crontab -l
00 22 * * 0-5 /uy/flask-venv/bin/python notice_montage.py
</pre>

其中<code>/uy/flask-venv/bin/python</code>是virtualenv的路径,<code>00 22 * * 0-5</code> 表示周日-周五每天22点调度。

最后,爬虫源代码在(叮)[https://github.com/thebe2/ding] ,喜欢的请给个星。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,848评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 利用网上现成的的接口,用python练习了下(又碰到了中文编码问题了:获取的页面源码 编码就是整不成中文) 天气查...
    倔强的潇洒小姐阅读 1,279评论 6 4
  • 色彩是一种奇怪的东西,它是美丽而丰富的,它能唤起人类的心灵感知。一般来说,红色是火的颜色,热情、奔放;也是血的颜色...
    曹阳CY阅读 362评论 0 0