上市公司公告大全抓取

东方财富网提供了上市公司的公告大全,将其抓取出来,进行整理后,提供给基金经理做投资决策。本文介绍了该项目的python爬虫实现过程。

  • 需求分析
    • 业务分析
    • 页面数据分析
    • 程序结构设计
  • 编码实现
    • 基础工具函数
    • 逻辑实现

需求分析

1.1 业务分析

分析公告汇总页面,发现沪深A股公告中小板公告创业板公告,页面表现比较一致,而三板公告实现方式有所变化,先抓取沪深A股公告、中小板公告和创业板公告

1.2 页面数据分析

翻页页面,发现地址栏链接不变,数据是采用ajax动态加载。

Paste_Image.png

在chrome的Network中找到数据请求地址:
<pre>
http://data.eastmoney.com/notices/getdata.ashx?StockCode=&FirstNodeType=0&CodeType=1&PageIndex=2&PageSize=50&jsObj=LYqzjWvf&SecNodeType=0&Time=&rt=50032558
http://data.eastmoney.com/notices/getdata.ashx?StockCode=&FirstNodeType=0&CodeType=1&PageIndex=3&PageSize=50&jsObj=maFEjDLX&SecNodeType=0&Time=&rt=50032562
</pre>

同时在网页源代码中也发现:
<pre>
dataurl: "/notices/getdata.ashx?StockCode=&FirstNodeType=0&CodeType=3&PageIndex={page}&PageSize={pageSize}&jsObj={jsname}{param}",

</pre>

对比大概可以肯定如下参数:
<pre>
CodeType 公告板块分类
PageIndex 页面序号
PageSize 页面大小
</pre>
不太确定的是<code>jsObj</code>和<code>rt</code>这2个参数。

继续分析网络请求,发现有一个load_table_data.js的请求,看起来比较可疑。在load_table_data.js中搜索jsObj未发现,但是搜索&rt发现下面代码:
<pre>
update: function () {
var _t = this;
if (_t.options.beforeupdate(_t))
return;
var jsname = _t.getCode(8),
_url = _t.parperUrl();
_t.options.code = jsname;
_url = _url.replace("{jsname}", jsname);
_url += (_url.indexOf('?') > -1) ? "&rt=" : "?rt=";
_url += parseInt(parseInt(new Date().getTime()) / 30000);
_t.loadThead();
_t.scorllTop();
_t.showLoading();
_t.tools.loadJs(_url, _t.options.charset,
function () {
if (typeof (_t.options.load_div) != "undefined") {
if (_t.options && _t.options.nodetemp) {
_t.options.nodetemp.style.position = "";
}
_t.options.load_div.style.display = "none"
}
if (!(eval("typeof " + jsname) == "undefined") || eval("typeof " + jsname == null)) {
var loaddata = eval(jsname);
if (jsname != _t.options.code) {
return
}
_t.options.data = loaddata;
_t.display()
} else {
// alert("数据加载失败,请刷新页面重新尝试!")
if (console && console.log) {
console.log("tools.loadJs挂了稍后再改");
}
}
})
},
</pre>
大概就是这里了,在257行,加上断点调试一下(如果对前端不太熟悉,调试方式是在chrome开发者工具的【Sources】标签里,找到load_table_data.js ,鼠标点击257行)

Paste_Image.png

这样就确认了链接里的<code>jsObj</code>和<code>rt</code>的实现方法。
jsObj生成函数如下:
<pre>
getCode: function (num) {
var str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var codes = str.split('');
num = num || 6;
var code = "";
for (var i = 0; i < num; i++) {
code += codes[Math.floor(Math.random() * 52)]
}
return code
},
var jsname = _t.getCode(8),
</pre>

rt生成代码如下
<pre>
parseInt(parseInt(new Date().getTime()) / 30000)
</pre>

1.3 程序结构设计
Paste_Image.png

(直接使用有道云笔记md画的图,有点渣)

编码实现

基础工具函数
# load_table_data.js getCode
def getCode(num=6):
    s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    codes = list(s)
    code = ""
    for x in xrange(0, num):
        idx = int(math.floor(random() * 52))
        code += codes[idx]
    return code
  • rt 模拟函数
# _url += parseInt(parseInt(new Date().getTime()) / 30000);
def getRightTime():
    r = int(time.time() / 30)
    return r
    mail_msg = read_html(now)
    # 邮件正文内容
    # msg.attach(MIMEText(now+" 日,二级市场公告信息。详情请见附件excel", 'plain', 'utf-8'))
    msg.attach(MIMEText(mail_msg, 'html', 'utf-8'))

    # 构造附件2,传送当前目录下的 xls 文件
    att2 = MIMEText(open(fileName, 'rb').read(), 'base64', 'utf-8')
    att2["Content-Type"] = 'application/octet-stream'
    # 解决中文附件下载时文件名乱码问题
    att2.add_header('Content-Disposition', 'attachment', filename='=?utf-8?b?' +
                    base64.b64encode(fileName.encode('UTF-8')) + '?=')
    msg.attach(att2)
  • 日期比较
def time_compare(notice_date):
    tt = time.mktime(time.strptime(notice_date, "%Y-%m-%d"))
    # 得到公告的时间戳
    if noticeCate == 1:
        # A股公告取当日
        # 得到本地时间(当日零时)的时间戳
        st = time.strftime("%Y-%m-%d", time.localtime(time.time()))
    else:
        # 新三板公告取前日
        # 得到本地时间(当日零时)的时间戳
        st = time.strftime(
            "%Y-%m-%d", time.localtime(time.time() - 60 * 60 * 24))
    t = time.strptime(st, "%Y-%m-%d")
    now_ticks = time.mktime(t)
    # 周一需要是大于
    if tt >= now_ticks:
        return True
    else:
        return False
  • excel读写
# 写excel
def write_sheet(workbook, sheetName, rows):
    worksheet = workbook.add_sheet(sheetName)
    worksheet.write(0, 0, label="代码")
    worksheet.write(0, 1, label="名称")
    worksheet.write(0, 2, label="公告标题")
    worksheet.write(0, 3, label="公告类型")
    for x in xrange(0, len(rows)):
        row = rows[x]
        for y in xrange(0, 4):
            if y == 2:
                alink = 'HYPERLINK("%s";"%s")' % (row[4], row[2])
                worksheet.write(x + 1, y, xlwt.Formula(alink))
            else:
                item = row[y]
                worksheet.write(x + 1, y, item)
# 打开excel
def open_excel(file='file.xls'):
    try:
        data = xlrd.open_workbook(file)
        return data
    except Exception, e:
        logger.debug(str(e))
  • 数据循环下载
def do_notice(notices, plate):
    for page in xrange(1, 10):
        rt = getRightTime()
        code = getCode(8)
        url = getUrl(apiurl, plate["codeType"], page, code, rt)
        jsdata = download_get_html(url)
        if jsdata != None:
            json_str = jsdata[15:-1]
            datas = json.loads(json_str)["data"]
            for data in datas:
                # 公告日期
                notice = parser_data(data)
                if notice != None:
                    notices.append(notice)
                else:
                    logger.debug("page end notices %s %d"& (plate["name"], len(notices)))
                    return
        else:
            logger.debug("no  notices %s %d"& (plate["name"], len(notices)))
            return
  • 数据解析
def parser_data(data):
    temp = data["CDSY_SECUCODES"][0]
    noteicedate = data["NOTICEDATE"]
    date = noteicedate[0:noteicedate.index('T')]
    code = temp["SECURITYCODE"]
    name = temp["SECURITYSHORTNAME"]
    title = data["NOTICETITLE"]
    typeName = '公司公告'
    if data["ANN_RELCOLUMNS"] and len(data["ANN_RELCOLUMNS"]) > 0:
        typeName = data["ANN_RELCOLUMNS"][0]["COLUMNNAME"]
    namestr = unicode(name).encode("utf-8")
    detailLink = baseurl + '/notices/detail/' + code + '/' + \
        data["INFOCODE"] + ',' + \
        base64.b64encode(urllib.quote(namestr)) + '.html'
    # print date,code,name,title,typeName,detailLink
    if time_compare(date):
        return [code, name, title, typeName, detailLink, date]
    else:
        return None
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • 一. Java基础部分.................................................
    wy_sure阅读 3,785评论 0 11
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • Spark SQL, DataFrames and Datasets Guide Overview SQL Dat...
    Joyyx阅读 8,319评论 0 16