数据来源:excel表格文件
项目需求:从excel表格中批量下载pdf版本的年报,将其命名为"股票代码_公司简称_ 年份"的格式,并全部转为txt文件。
使用语言:python
第三方库:pandas,requests, re , pdfplumber,time等。
实现思路:
由于数据保存在excel文件中,所以第一步从读取数据入手,本文采用pandas库处理excel表格,其他第三库亦可。
import requests
import pandas as pd
import os
import multiprocessing
import pdfplumber
import logging
import re
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 读取Excel文件
try:
df = pd.read_excel('年报_2015.xlsx')
except Exception as e:
logging.error(f"读取失败!! {e}")
return
下图为原始excel文件数据,我们可以用标题名称来作为查找名,并设置对应的变量。
# 读取文件内容并存储为字典
content_dict = ((row['公司代码'], row['公司简称'], row['年份'], row['年报链接']) for _, row in df.iterrows())
# 我们分别用code, name, year, pdf_url四个变量来接受这些值
在设置好变量后,还需要设置文件目录,用来存放下载的pdf文件和我们需要的txt文件,因此我们创建两个文件夹。
# 创建存储文件的文件夹
pdf_dir = 'pdf年报'
txt_dir = 'txt年报'
try:
os.makedirs(pdf_dir, exist_ok=True)
os.makedirs(txt_dir, exist_ok=True)
except Exception as e:
logging.error(f"创建文件夹失败!请检查权限! {e}")
return
前面提到,由于表格中的文件比较庞大,直接运行速度过于缓慢,需要加入多线程来提高效率,而且笔者将下载与转换都一次性处理,在分配一个线程进行,逻辑如下图所示。
多线程具体代码如下,我们开启一个线程池(网络上有很多人讲,这里不赘述),并加入了检查文件是否存在的功能,以防止程序意外中断后,会重复进行下载操作。
如果检测到文件不存在,则调用convert()函数进行下载和转换操作。
#开启多线程
with multiprocessing.Pool() as pool:
for code, name, year, pdf_url in content_dict:
txt_file_name = f"{code}_{name}_{year}.txt"
txt_file_path = os.path.join(txt_dir, txt_file_name)
#检测文件是否已经存在
if os.path.exists(txt_file_path):
logging.info(f"{txt_file_name} 已存在,跳过.")
else:
pool.apply_async(convert, args=(code, name, year, pdf_url, pdf_dir, txt_dir))
pool.close()
pool.join()
接下来的任务写好convert函数即可,这里这直接给出完整代码。
def convert(code, name, year, pdf_url, pdf_dir, txt_dir):
pdf_file_path = os.path.join(pdf_dir, f"{code}_{name}_{year}.pdf")
txt_file_path = os.path.join(txt_dir, re.sub(r'[\\/:*?"<>|]', '', f"{code}_{name}_{year}.txt"))
try:
# 下载PDF文件
if not os.path.exists(pdf_file_path):
with requests.get(pdf_url, stream=True) as r:
r.raise_for_status()
with open(pdf_file_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
# 转换PDF文件为TXT文件
with pdfplumber.open(pdf_file_path) as pdf:
with open(txt_file_path, 'w', encoding='utf-8') as f:
for page in pdf.pages:
text = page.extract_text()
f.write(text)
logging.info(f"{txt_file_path} 已保存.")
except Exception as e:
logging.error(f"出错了- {code}_{name}_{year}. {e}")
else:
# 删除已转换的PDF文件,以节省空间
os.remove(pdf_file_path)
logging.info(f"{pdf_file_path} 已被删除。")
具体来说,第一步是根据输入参数构造出 pdf文件的完整路径和 txt文件的完整路径。这里使用了 Python 的 os.path.join
函数来拼接文件路径,以及 re.sub
函数来去除文件名中的非法字符。
接下来就是用 requests
库从 pdf_url
下载文件,并将其保存在 pdf_file_path
中。下载的过程中,函数会以 8192 字节为一块,依次将文件块写入本地文件。
如果 pdf文件下载成功,我们使用 pdfplumber
库将 其转换为txt文件。
最后,函数会删除已经转换成功的 pdf文件,这样就大功告成了~
经过整理,得到了如下txt文本,可以看到txt文本内容与原内容一致,便于我们之后进行词频分析。
文本挖掘是财经类学术研究的常用方式,特别是借助上市公司年报进行词频分析的应用十分广泛。
若要获取完整代码文件,或者2010-2021的txt年报下载链接,可以关注同名公众号“凌小添”回复‘年报’获取哦~