700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 爬取年报数据 解析PDF提取数据 分析代码(巨潮 Python)

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

时间:2023-07-14 20:41:14

相关推荐

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

利用Python爬取巨潮网页上的年报等数据,通过解析下载的PDF文件,提取其中有用的数据,并写入到本地文件中。

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

GitHub:项目代码地址

最终的效果:

(1)Excel文件中部分的股票代码,读取后返回一个股票代码的列表

(2)下载的部分年报PDF文件,这里设定为2002年到的中文年报数据(后续会边解析边删除文件,不用担心文件过多)

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

开发过程说明

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

2、首先在浏览器界面打开网址 /new/disclosure/stock?orgId=gssz0000004&stockCode=000004#

3、当打开网址时,Fiddler就会捕捉到这一事件,此时在Fiddler界面会看到一个对应的链接如图,其中 1 表示从服务器读取数据,双击可以看到,服务器返回的数据可以被解析为 json 数据如 3 所示,从json数据可以很容易的对数据进行字符字典操作,并且 2 中可以看到我们向服务器请求的时候我们发送的都有哪些字符,和字符所代表的含义,这些都可以和程序中我们是设置的关键字对应上。

当点击公告搜索的时候会出现以下界面:

此时再次双击对应的请求,对应的Fiddler界面变为:可以看到body变了,body就相当于我们的请求参数,其中设置pagenum为2,就可以获得第二页的数据了,实现翻页的功能。下面返回的json数据也变了,并且下面还有多个文件的地址,地址可以取出来放在列表中,下面的地址再加上原始网址就是最终该PDF文档所在的地址。

最终的PDF地址:/finalpage/-04-30/1206164079.PDF ,得到了最后的文件地址,我们就可以进行下载了,对应的代码就好理解了。

对应的代码为:文件已经上传到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 = '/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 = 50MAX_RELOAD_TIMES = 5RESPONSE_TIMEOUT = 10def 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 = '-01-01',END_DATE = '-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 = 0while True:try:r = requests.post(URL, query, HEADER, timeout=RESPONSE_TIMEOUT)except Exception as e:print(e)continueif 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 = '/' + 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万科A年度报告.(1848k).PDF /finalpage/-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进行访问,这样就抓不到我们了。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。