爬取年报数据、解析PDF提取数据、分析代码(巨潮 Python)

利用Python爬取巨潮网页上的年报等数据,通过解析下载的PDF文件,提取其中有用的数据,并写入到本地文件中。
主要分为几个模块:
  • 1、从Excel或者列表或者TXT读取股票代码
  • 2、根据股票代码和年份等信息爬取特定网页中的信息,获得年报数据所在的网络地址
  • 3、根据年报数据的网络地址,下载对应的到本地
  • 4、解析本地的PDF文件,通过关键词检索到对应的value
  • 5、添加多线程,一边对股票代码进行解析,一边进行文件的下载,一边进行文件的解析,三者同时进行,节省时间

GitHub:项目代码地址

最终的效果:

(1)Excel文件中部分的股票代码,读取后返回一个股票代码的列表
部分股票代码
(2)下载的部分年报PDF文件,这里设定为2002年到2007年的中文年报数据(后续会边解析边删除文件,不用担心文件过多)
部分下载文件

(3)从PDF文件解析得到的value,通过设置关键词为想要咨询或者中介费用,最终得到对应的值,并写入文件保存,最后一列为该条数据所在的页数。

解析结果

开发过程说明

1、打开Fiddler软件,通过该软件可以更为清晰的捕捉并看到客户端与服务器之间的响应,功能和网页中的network一样。(可以下载在:Fiddler地址

2、首先在浏览器界面打开网址 http://www.cninfo.com.cn/new/disclosure/stock?orgId=gssz0000004&stockCode=000004#

3、当打开网址时,Fiddler就会捕捉到这一事件,此时在Fiddler界面会看到一个对应的链接如图,其中 1 表示从服务器读取数据,双击可以看到,服务器返回的数据可以被解析为 json 数据如 3 所示,从json数据可以很容易的对数据进行字符字典操作,并且 2 中可以看到我们向服务器请求的时候我们发送的都有哪些字符,和字符所代表的含义,这些都可以和程序中我们是设置的关键字对应上。
image1
当点击公告搜索的时候会出现以下界面:
image2
此时再次双击对应的请求,对应的Fiddler界面变为:可以看到body变了,body就相当于我们的请求参数,其中设置pagenum为2,就可以获得第二页的数据了,实现翻页的功能。下面返回的json数据也变了,并且下面还有多个文件的地址,地址可以取出来放在列表中,下面的地址再加上原始网址就是最终该PDF文档所在的地址。
image3
最终的PDF地址:http://static.cninfo.com.cn/finalpage/2019-04-30/1206164079.PDF ,得到了最后的文件地址,我们就可以进行下载了,对应的代码就好理解了。
image4
对应的代码为:文件已经上传到GitHub上了

# 前面的一些参数PLATE CATEGORY等是向服务器请求时要发送过去的参数。
OUTPUT_FILENAME = 'report'
# 板块类型:沪市:shmb;深市:szse;深主板:szmb;中小板:szzx;创业板:szcy;
PLATE = 'szzx;'
# 公告类型:category_scgkfx_szsh(首次公开发行及上市)、category_ndbg_szsh(年度报告)、category_bndbg_szsh(半年度报告)
CATEGORY = 'category_ndbg_szsh;'

URL = 'http://www.cninfo.com.cn/new/hisAnnouncement/query'
# 使用浏览器代理,否则网站检测到是Python爬虫时会自动屏蔽
HEADER = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest'
}
MAX_PAGESIZE = 50
MAX_RELOAD_TIMES = 5
RESPONSE_TIMEOUT = 10


def standardize_dir(dir_str):
    assert (os.path.exists(dir_str)), 'Such directory \"' + str(dir_str) + '\" does not exists!'
    if dir_str[len(dir_str) - 1] != '/':
        return dir_str + '/'
    else:
        return dir_str


# 参数:页面id(每页条目个数由MAX_PAGESIZE控制),是否返回总条目数(bool)
def get_response(page_num,stack_code,return_total_count=False,START_DATE = '2013-01-01',END_DATE = '2018-01-01'):

# 这里就是 body 信息
    query = {
        'stock': stack_code,
        'searchkey': '',
        'plate': '',
        'category': CATEGORY,
        'trade': '',
        'column': '', #注意沪市为sse
#        'columnTitle': '历史公告查询',
        'pageNum': page_num,
        'pageSize': MAX_PAGESIZE,
        'tabName': 'fulltext',
        'sortName': '',
        'sortType': '',
        'limit': '',
        'showTitle': '',
        'seDate': START_DATE + '~' + END_DATE,
    }
    result_list = []
    reloading = 0
    while True:
        try:
            r = requests.post(URL, query, HEADER, timeout=RESPONSE_TIMEOUT)
        except Exception as e:
            print(e)
            continue
        if r.status_code == requests.codes.ok and r.text != '':
            break

# 以下就是开始解析 json 数据,和解析字典类似
    my_query = r.json()
    try:
        r.close()
    except Exception as e:
        print(e)
    if return_total_count:
        return my_query['totalRecordNum']
    else:
        for each in my_query['announcements']:
            file_link = 'http://static.cninfo.com.cn/' + str(each['adjunctUrl'])
            file_name = __filter_illegal_filename(
                str(each['secCode']) + str(each['secName']) + str(each['announcementTitle']) + '.'  + '(' + str(each['adjunctSize'])  + 'k)' +
                file_link[-file_link[::-1].find('.') - 1:]  # 最后一项是获取文件类型后缀名
            )
            if file_name.endswith('.PDF') or file_name.endswith('.pdf'):
                if '取消' not in file_name and '摘要' not in file_name and '年度' in file_name:
                    result_list.append([file_name, file_link])
        return result_list

最终的result_list:
eg:
000002万科A2012年年度报告.(1848k).PDF http://static.cninfo.com.cn/finalpage/2013-02-28/62162993.PDF

接下来就是通过该网址进行文件的下载
然后通过Python的pdfplunber模块进行PDF的解析,提取其中我们想要的数据(这里提取的是审计、咨询费用一项)
代码见项目代码地址

调试经验:

1、try-excep如果发生异常,可在except中捕捉,但是一定要处理该异常,不能简单的pass,可能会造成后续没有参数被赋值等问题,导致后续还会有bug。比如try里面放了一个
pdf_file=open(xxxx.xx),如果发生异常,except中必须对该异常进行处理,否则后续中出现的pdf_file就是一个没有被赋值的参数,调用pdf_file.readlines()等方法肯定会再次报错。可以在except之后至一个标志位(if 语句),表示此时不再继续向下运行代码。
2、当使用多线程进行编程时,一定要考虑到加锁后,锁是否能正常的被释放
3、当频繁的访问某一个网站时,有可能会被网站当成网络攻击而屏蔽掉,发生以下的error:
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。这时候这个error也是可以处理的,可以再次新建一个线程或者回调函数进行再次的网络访问,或者在每次访问之间设置一个sleep 1 s,不访问的太频繁,或者可以建立一个IP池,每次我们使用不同的IP进行访问,这样就抓不到我们了。

  • 46
    点赞
  • 393
    收藏
    觉得还不错? 一键收藏
  • 26
    评论
使用Python爬取年报捐赠数据可以通过以下步骤实现: 1. 寻找目标网站:首先要确定要爬取年报捐赠数据在哪个网站上。可以通过搜索引擎或专业网站找到相关的信息。 2. 安装爬虫框架:Python有很多优秀的爬虫框架,如Scrapy、BeautifulSoup等。选择一个适合自己的框架,然后安装。 3. 编写爬虫代码:打开Python的IDE(如PyCharm)或编辑器,在代码中导入相关的库,如requests、lxml等。然后编写代码,通过发送HTTP请求获取网页的HTML源码。 4. 解析HTML源码:使用BeautifulSoup等库解析获取到的HTML源码,找出捐赠数据所在的标签和属性,提取出所需的数据。 5. 数据存储:将提取出的数据存储到本地文件或数据库中。可以选择Excel、CSV、SQLite等格式进行存储,根据实际需求选择合适的方式。 6. 定时运行爬虫:如果需要定期爬取年报捐赠数据,可以使用定时任务工具(如crontab、APScheduler等)定期运行爬虫脚本,以获取最新数据。 7. 异常处理:在爬取过程中可能会遇到一些异常情况,如网络超时、IP被封禁等。在代码中添加异常处理机制,保证爬虫的稳定运行。 8. 数据清洗和分析:获取到的原始数据可能存在一些噪声和错误,可以使用Pandas等库进行数据清洗和处理。然后可以进行数据分析,统计捐赠总额、捐赠机构排名等信息。 以上是使用Python爬取年报捐赠数据的一般步骤。具体实现细节需要根据目标网站的结构和数据格式进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值