文档章节

python抓取动态数据 A股上市公司基本信息

ssshen
 ssshen
发布于 2016/10/07 17:48
字数 1525
阅读 104
收藏 0

1.背景

之前写的抓取A股所有上市公司信息的小程序在上交所网站改版后,需要同步修改

pyton2.7.9

2.分析过程

以抓取宇通客车【600066】信息为例

打开网址http://www.sse.com.cn/assortment/stock/list/info/company/index.shtml?COMPANY_CODE=600066

红框中的内容是需要抓取的信息,查看网页源码

可以看到公司信息并没有直接写到html中,使用chrome “开发者工具”快捷键F12,查看浏览器与服务器的交互过程(在这一步走了弯路,使用selenium+plantomjs模拟浏览器然后分析html以及使用ghost.py+beautifulsoup都没有成功)

可以在标红线的url上看到返回的公司信息,剩下的就是模拟浏览器请求这个url了,request header中的refer一定不能省略,不然会报403

返回的信息是json格式的,可以使用python自带的json库转换为dict,可以参考searchJ.js来获得想要的信息

具体见github网址https://github.com/shenyanf/AShareListedCompanyList

代码:

myutil.py
# -*- coding: utf-8 -*- 
'''
Created on 2016年10月08日
@author: shenyanf
'''

import urllib2
import json
from time import sleep

class JSONObject:
    def __init__(self, d):
        self.__dict__ = d

class MyUtil:
    @classmethod
    def getDatas(cls, url, referer=None):
        '''获取指定地址的html内容 .'''
        
        request = urllib2.Request(url)

        request.add_header('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8')
        request.add_header('Accept-Encoding', 'gzip, deflate, sdch')
        request.add_header('Accept-Language', 'zh-CN,zh;q=0.8,en;q=0.6')
        request.add_header('Cache-Control', 'max-age=0')
        request.add_header('Connection', 'keep-alive')
        request.add_header('Host', 'query.sse.com.cn')
        request.add_header('Upgrade-Insecure-Requests', '1')
        request.add_header('Referer', referer)
        request.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36')
        
        # 尝试5次,如果每次都是timeout,打印提示信息,返回none 
        maxNum = 5
        for i in range(maxNum):
            try:    
                response = urllib2.urlopen(url=request, timeout=15)
                # 慢一点 不然被屏蔽
                sleep(1)
                break
            except:
                pass
            
            if i < maxNum - 1:
                continue
            else:
                print 'URLError: <urlopen error timed out> All times is failed '
                return None
        
        response.encoding = 'utf-8'
        result = response.read()
#         print result
        
        str2JsonData = str(result).split('(')[1].split(')')[0]
        pythonObjData = json.loads(str2JsonData, object_hook=JSONObject)
        
#         print pythonObjData
        return pythonObjData
achievestockinfo.py

# -*- coding: utf-8 -*- 
'''
Created on 2016年10月08日
@author: shenyanf
'''

from myutil import MyUtil

class JSONObject:
    def __init__(self, d):
        self.__dict__ = d

class AchieveSSEStockInfo:
    '''获得上海证卷交易所股票信息.'''
    
    # 指标的方法,顺序已经排好,请不要乱动
    __public__ = ['getCompanyCode', 'getCompanyShortName', 'getCompanyName', 'getCompanyEnlishName', 'getIpoAddress', 'getASharesCode',
                  'getASharesShortName', 'getASharesIPODate', 'getASharesTotalCapital', 'getASharesOutstandingCaptial', 'getBSharesCode',
                  'getBSharesShortName', 'getBSharesIPODate', 'getBSharesTotalCapital', 'getBSharesOutstandingCaptial', 'getArea', 'getProvince', 'getCity', 'getTrade', 'getWebsite']
    
    achieveIndexFromURLA = ['CHANGEABLE_BOND_ABBR', 'OFFICE_ZIP', 'AREA_NAME_DESC', 'FULL_NAME_IN_ENGLISH', 'COMPANY_CODE', 'CSRC_MIDDLE_CODE_DESC', 'SECURITY_ABBR_A', 'COMPANY_ADDRESS', 'SECURITY_CODE_A', 'SECURITY_CODE_B', 'SECURITY_30_DESC', 'COMPANY_ABBR', 'OFFICE_ADDRESS', 'CHANGEABLE_BOND_CODE', 'ENGLISH_ABBR', 'LEGAL_REPRESENTATIVE', 'REPR_PHONE', 'E_MAIL_ADDRESS', 'FOREIGN_LISTING_ADDRESS', 'STATE_CODE_A_DESC', 'SSE_CODE_DESC', 'FOREIGN_LISTING_DESC', 'SECURITY_CODE_A_SZ', 'CSRC_GREAT_CODE_DESC', 'WWW_ADDRESS', 'CSRC_CODE_DESC', 'STATE_CODE_B_DESC', 'FULLNAME']
    
    '''
    all indexs as follow:
        companyCode     公司代码
        companyShortName     公司简称
        companyName      公司全称
        companyEnlishName      英文名称
        ipoAddress      注册地址
        aSharesCode      A股代码
        aSharesShortName      A股简称
        aSharesIPODate      A股上市日期
        aSharesTotalCapital      A股总股本
        aSharesOutstandingCaptial      A股流通股本
        bSharesCode      B股代码
        bSharesShortName      B股简称
        bSharesIPODate      B股上市日期 
        bSharesTotalCapital       B股总股本  
        bSharesOutstandingCaptial      B股流通股本
        area      地区 
        province      省份
        city      城市
        trade      所属行业
        website      公司网址
        
        status A股状态/B股状态
    '''
    
    def getCompanyCode(self):
        return self.__getBasicValue('COMPANY_CODE')
    
    def getStatus(self):
        v = self.__getBasicValue('STATE_CODE_A_DESC') + '/' + self.__getBasicValue('STATE_CODE_B_DESC')
#         print v
        if v == '-/-' or u'摘牌' in v:
            return False
        else:
            return True
    
    def getCompanyShortName(self):
        return self.__getBasicValue('COMPANY_ABBR') + '/' + self.__getBasicValue('ENGLISH_ABBR')
    
    def getCompanyName(self):
        return self.__getBasicValue('FULLNAME')
    
    def getCompanyEnlishName(self):
        return self.__getBasicValue('FULL_NAME_IN_ENGLISH')
    
    def getIpoAddress(self):
        return self.__getBasicValue('COMPANY_ADDRESS')
    
    def getASharesCode(self):
        return self.__getBasicValue('SECURITY_CODE_A')
    
    def getASharesShortName(self):
        return self.__getBasicValue('COMPANY_ABBR') + '/' + self.__getBasicValue('ENGLISH_ABBR')
    
    def getASharesIPODate(self):
        result = ''
        referer = 'http://www.sse.com.cn/assortment/stock/list/info/company/index.shtml?COMPANY_CODE=' + str(self.stockCode)
        try:
            rsDict = MyUtil.getDatas(self.basicURLB, referer)
            if rsDict == '-' or rsDict is None:
                result = '-'
            else:
                ipoDate = dict((name, getattr(rsDict.result[0], name)) for name in dir(rsDict.result[0]) if not name.startswith('__'))
                print ipoDate
                result = ipoDate.get('LISTINGDATEA')
        except:
            result = '-'
        return result
        

    def getTotalCapital(self):      
        return self.__getCapitalValue('totalShares')
    
    def getASharesTotalCapital(self):
        aShareTotalShare = 0.0
        
        AShareNonFlowShare = self.__getCapitalValue('totalNonFlowShare')
        AShareFlowShare = self.getASharesOutstandingCaptial()
         
        if  AShareNonFlowShare != '-' and  AShareNonFlowShare:
            aShareTotalShare += float(AShareNonFlowShare)
        if AShareFlowShare != '-' and AShareFlowShare:
            aShareTotalShare += float(AShareFlowShare)

        return aShareTotalShare
    
    def getASharesOutstandingCaptial(self):
        return self.__getCapitalValue('AShares')
    
    def getBSharesTotalCapital(self):
        return self.getBSharesOutstandingCaptial()
        
    def getBSharesOutstandingCaptial(self):
        return self.__getCapitalValue('BShares')
    
    def getBSharesCode(self):
        return self.__getBasicValue('SECURITY_CODE_B')
    
    def getBSharesShortName(self):
        if self.getBSharesCode().find('-') != -1:
            return ''
        else:
            return self.getASharesShortName()
    
    def getBSharesIPODate(self):
        result = ''
        referer = 'http://www.sse.com.cn/assortment/stock/list/info/company/index.shtml?COMPANY_CODE=' + str(self.stockCode)
        try:
            rsDict = MyUtil.getDatas(self.basicURLC, referer)
            if rsDict == '-' or rsDict is None:
                result = '-'
            else:
                ipoDate = dict((name, getattr(rsDict.result[0], name)) for name in dir(rsDict.result[0]) if not name.startswith('__'))
                print ipoDate
                result = ipoDate.get('LISTINGDATEB')
        except:
            result = '-'
        return result
        
    def getArea(self):
        return self.__getBasicValue('AREA_NAME_DESC')
    
    def getProvince(self):
        return self.getArea() 
    
    def getCity(self):
        return self.getArea() 
    
    def getTrade(self):
        return self.__getBasicValue('SSE_CODE_DESC')
#    CSRC行业(门类/大类/中类)
#    'CSRC_CODE_DESC') + '/' + self.__getBasicValue('CSRC_GREAT_CODE_DESC') + '/' + self.__getBasicValue('CSRC_MIDDLE_CODE_DESC')
    
    def getWebsite(self):
        return self.__getBasicValue('WWW_ADDRESS')
        
    
    def __getBasicValue(self, key):
        '''获得上市公司基本信息的值.'''
        result = ''
        referer = 'http://www.sse.com.cn/assortment/stock/list/info/company/index.shtml?COMPANY_CODE=' + str(self.stockCode)
        try:
            # 首次使用该方法,需要访问url,获取网页内容
            if self.stockBasicInfo == None:
                rsDict = MyUtil.getDatas(self.basicURLA, referer)
                if rsDict == '-' or rsDict is None:
                    result = '-'
                else:
                    # jsonObj 转换为字典类型
                    self.stockBasicInfo = dict((name, getattr(rsDict.result[0], name)) for name in dir(rsDict.result[0]) if not name.startswith('__'))
#                     print self.stockBasicInfo
            result = self.stockBasicInfo.get(key)
        except:
            result = '-'
        
#         print result
        return result
    
    def __getCapitalValue(self, key):
        '''获得上市公司股本信息的值.'''
        result = ''
        referer = 'http://www.sse.com.cn/assortment/stock/list/info/capital/index.shtml?COMPANY_CODE=' + str(self.stockCode)
        try:
            # 首次使用该方法,需要访问url,获取网页内容
            if self.stockCapitalInfo == None:
                rsDict = MyUtil.getDatas(self.capitalURL, referer)
                if rsDict == '-' or rsDict is None:
                    result = '-'
                else:
                    # jsonObj 转换为字典类型
                    self.stockCapitalInfo = dict((name, getattr(rsDict.result, name)) for name in dir(rsDict.result) if not name.startswith('__'))
#                     print self.stockCapitalInfo
            result = self.stockCapitalInfo.get(key)
        except:
            result = '-'
        
#         print result
        return result
    
    def __mergeBasicURL(self, sqlId, stockCode):
        return 'http://query.sse.com.cn/commonQuery.do?jsonCallBack=jsonpCallback12345&isPagination=false&sqlId=' + sqlId + '&productid=' + str(stockCode) + '&_=14555555555552'
    
    def __init__(self, stockCode):
        self.stockCode = stockCode
        self.basicURLA = self.__mergeBasicURL('COMMON_SSE_ZQPZ_GP_GPLB_C', stockCode)
        # A股上市时间
        self.basicURLB = self.__mergeBasicURL('COMMON_SSE_ZQPZ_GP_GPLB_AGSSR_C', stockCode)
        # B股上市时间
        self.basicURLC = self.__mergeBasicURL('COMMON_SSE_ZQPZ_GP_GPLB_BGSSR_C', stockCode)
        # 秘书信息
        self.basicURLD = self.__mergeBasicURL('COMMON_SSE_ZQPZ_GP_GPLB_MSXX_C', stockCode)
        self.basicURLE = r'http://query.sse.com.cn/commonSoaQuery.do?jsonCallBack=jsonpCallback46644&isPagination=true&stockCode=' + str(stockCode) + '&tradeBeginDate=19700101&tradeEndDate=20161001&order=tradeBeginDate%7Cdesc&sqlId=PL_SCRL_SCRLB&pageHelp.pageNo=1&pageHelp.beginPage=1&pageHelp.cacheSize=1&pageHelp.endPage=1&pageHelp.pageSize=5&_=1475720975596'
        self.capitalURL = 'http://query.sse.com.cn/security/stock/queryCompanyStockStruct.do?jsonCallBack=jsonpCallback86976&isPagination=false&companyCode=' + str(stockCode) + '&_=1475732919742'
       
        self.stockBasicInfo = None
        self.stockCapitalInfo = None
        pass 
    

if __name__ == '__main__':
    for i in range(600001, 600003):
        a = AchieveSSEStockInfo(600013)
        for j in range(a.__public__.__len__()):
            m = a.__public__[j]
            f = getattr(a, m)
            print m, f()

 

附录:

1.使用requests库抓取页面的时候的编码问题 https://segmentfault.com/q/1010000000341014
2.openpyxl参考手册 http://openpyxl.readthedocs.io/en/default/   http://openpyxl.readthedocs.io/en/default/usage.html
3.urllib2使用 http://zhuoqiang.me/python-urllib2-usage.html#http
4.读写json数据 http://python3-cookbook.readthedocs.io/zh_CN/latest/c06/p02_read-write_json_data.html
5.python中 class 或对象属性转化成dict 、dict转换成对象 http://blog.csdn.net/chenyulancn/article/details/8203763
6.【原创】说说JSON和JSONP,也许你会豁然开朗,含jQuery用例 http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html

7.Applying borders to a cell in OpenPyxl   http://stackoverflow.com/questions/24917201/applying-borders-to-a-cell-in-openpyxl

 

后记:

目前上交所已经提供A股上市公司xls的下载了,虽然信息不太完整,连接地址http://query.sse.com.cn/security/stock/downloadStockListFile.do?csrcCode=&stockCode=&areaName=&stockType=1

© 著作权归作者所有

下一篇: 我的github地址
ssshen
粉丝 2
博文 34
码字总数 12481
作品 0
海淀
程序员
私信 提问
(六)金融知识图谱——基于中财网的图谱搭建(下)

这个部分主要梳理下我的图谱的制作过程。 1.数据来源:中彩网 1.首先我们要分析我们要爬取哪些内容。上面这个是一个主页,一共74页,全都是上市公司,点进其中一个公司看看: 我们首先要做的...

天生smile
2018/12/06
0
0
买《Python从小白到大牛》专题视频课程,送配套纸质图书

经过一年多时间的呕心沥血,Python立体化图书——《Python从小白到大牛》即将与大家见面了。所谓立体化图书包括:电子图书、视频、课件和服务等内容。 《Python从小白到大牛》纸质图书将于9...

tony关东升
2018/07/23
0
0
docker(19):压缩docker python 镜像大小,减少依赖

1,关于python镜像 使用python开发的时候遇到问题。 发现构建之后 镜像还是比较大的。想办法减少点文件啥的。 能让镜像小点,这样发布部署啥的都方便。 2,构建dockerfile python3 在构建pan...

freewebsys
2018/04/16
0
0
Python3爬虫视频学习教程

大家好哈,现在呢静觅博客已经两年多啦,可能大家过来更多看到的是爬虫方面的博文,首先非常感谢大家的支持,希望我的博文对大家有帮助! 最近,主要的任务就是开发性感美女图片大全,使用p...

yangjiyue0520
2017/11/18
0
0
量化工具介绍

做量化要有必要的工具。下面简单介绍一下我用的工具: (一) 软件推荐:python 常用的量化软件有python、matlab、java、C++。从开发难度而言python和matlab都比较容易,java和C++麻烦一些。从运...

JDquant
2017/08/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

MainThreadSupport

MainThreadSupport EventBus 3.0 中的代码片段. org.greenrobot.eventbus.MainThreadSupport 定义一个接口,并给出默认实现类. 调用者可以在EventBus的构建者中替换该实现. public interface ...

马湖村第九后羿
32分钟前
3
0
指定要使用的形状来代替文字的显示

控制手机键盘弹出的功能只能在ios上实现,安卓是实现不了的,所以安卓只能使用type类型来控制键盘类型,例如你要弹出数字键盘就使用type="number",如果要弹出电话键盘就使用type="tel",但这...

前端老手
43分钟前
5
0
总结:Raft协议

一、Raft协议是什么? 分布式一致性算法。即解决分布式系统中各个副本数据一致性问题。 二、Raft的日志广播过程 发送日志到所有Followers(Raft中将非Leader节点称为Follower)。 Followers收...

浮躁的码农
50分钟前
7
0
Flask-admin Model View字段介绍

Model View字段介绍 can_create = True 是否可以创建can_edit = True 是否可以编辑can_delete = True 是否可以删除list_template = 'admin/model/list.html' 修改显......

dillonxiao
今天
5
0
从AnnotationTransactionAspect开始rushSpring事务

0. Spring 事务 with LTW 0.1. Spring 事务 With LTW的原因: Pure Proxy-base mode有缺陷,其失效原因分析及使用方法及运行机制(LoadTimeWeaverBeanDefinitionParser和 AspectJWeavingEnable......

Aruforce
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部