【复刻论文】企业数字化转型年度报告词频+文本统计

参考文献

[1]吴非,胡慧芷,林慧妍,任晓怡.企业数字化转型与资本市场表现——来自股票流动性的经验证[J].管理世界,2021,37(07)

[2]韩峰,姜竹青.集聚网络视角下企业数字化的生产率提升效应研究[J].管理世界,2023,39(11)

复刻论文缘由

本人为大二学生,上数据科学导论的文本分析课程时,希望顺手锻炼一下学术能力,遂选取以上两篇论文进行参考,希望复刻企业数字化的数据提取过程。

然而在上网搜索的过程中,遇到的很多问题无法得以解决,或者说,有分享出来的都是直接分享爬取好的excel结果,没有看到直接分享代码的;或者说,都是需要收费的。

基于此,本人希望把整个作业结果分享出来,和大家共同研究探讨。

遇到的问题主要如下:

1、如何爬取上市公司年报数据

--> 参考了社区里面另一个大佬的:巨潮资讯网年报爬虫_爬取巨潮资讯网年报-CSDN博客

2、如何将pdf转换成txt

--> 自行探索综合了几个方案,再调整得出的可运行的版本

3、在此基础上,剔除关键词前存在“没”“无”“不”等否定词语的表述,同时也剔除非本公司(包括公司的股东、客户、供应商、公司高管简介介绍在内)的“数字化转型”关键词

--> 本人只剔除了否定表述,对于后者,希望有大佬指点一番

词典

数据说明

本文基于文本挖掘的数字经济词频方法来测算企业数字化水平。

本文根据吴非等(2021)的方法,利用爬虫技术批量搜索了中国沪深A股上市制造业企业2010~2022年期间的年报文本数据,并且使用其词典进行词频统计。

进而借鉴洛克伦和麦克唐纳(2014)及阿西莫格鲁等(2021)的研究,利用上市公司年报文本数据和基于机器学习的“词频—逆文本频率”方法测算企业数字化水平。

企业数字化指标可以表示为:

1、爬取年报

在CSMAR上自行下载所你需要的股票代码,将文件命名为“stockcode.xlsx”,表格内容如图所示:

调整文件夹路径,即可直接运行代码。我这里爬取的是巨潮资讯网的年报数据。 

import os
os.chdir(r"E:\大学课程相关\大二下学期\5 数据科学导论\文本分析")
os.getcwd()
import requests,time,random,json
import pandas as pd

def req(stock,year,org_dict):
    # post请求地址(巨潮资讯网的那个查询框实质为该地址)
    url = "http://www.cninfo.com.cn/new/hisAnnouncement/query"
    # 表单数据,需要在浏览器开发者模式中查看具体格式
    data  = {
        "pageNum":"1",
        "pageSize":"30",
        "tabName":"fulltext",
        "stock":stock + "," + org_dict[stock] ,# 按照浏览器开发者模式中显示的参数格式构造参数
        "seDate":f"{str(int(year)+1)}-01-01~{str(int(year)+1)}-12-31",
        "column":"szse",
        "category":"category_ndbg_szsh",
        "isHLtitle": "true",
        "sortName":"time",
        "sortType": "desc"
        }
    # 请求头
    headers =  {"Content-Length": "201","Content-Type":"application/x-www-form-urlencoded"}
    # 发起请求
    req = requests.post(url,data=data,headers=headers)
    
    if json.loads(req.text)["announcements"]:# 确保json.loads(req.text)["announcements"]非空,是可迭代对象
        for item in json.loads(req.text)["announcements"]:# 遍历announcements列表中的数据,目的是排除英文报告和报告摘要,唯一确定年度报告或者更新版
            if "摘要" not in item["announcementTitle"]:
                if "英文" not in item["announcementTitle"]:
                    if "修订" in item["announcementTitle"] or "更新" in item["announcementTitle"]:
                        adjunctUrl = item["adjunctUrl"] # "finalpage/2019-04-30/1206161856.PDF" 中间部分便为年报发布日期,只需对字符切片即可
                        pdfurl = "http://static.cninfo.com.cn/" + adjunctUrl
                        r = requests.get(pdfurl)
                        f = open("年报" +"/"+ stock + "-" + year + "年度报告" + ".pdf", "wb")
                        f.write(r.content)                       
                        print(f"{stock}-{year}年报下载完成!") # 打印进度
                        break
                    else:
                        adjunctUrl = item["adjunctUrl"] # "finalpage/2019-04-30/1206161856.PDF" 中间部分便为年报发布日期,只需对字符切片即可
                        pdfurl = "http://static.cninfo.com.cn/" + adjunctUrl
                        r = requests.get(pdfurl)
                        f = open("年报" +"/"+ stock + "-" + year + "年度报告" + ".pdf", "wb")
                        f.write(r.content)                       
                        print(f"{stock}-{year}年报下载完成!") # 打印进度
                        break
# 该函数主要是通过http://www.cninfo.com.cn/new/data/szse_stock.json该json数据,找到每个stock对应的orgid,并存储在字典org_dict中
def get_orgid():
    org_dict = {}
    org_json = requests.get("http://www.cninfo.com.cn/new/data/szse_stock.json").json()["stockList"]

    for i in range(len(org_json)):
        org_dict[org_json[i]["code"]] = org_json[i]["orgId"]

    return org_dict
# 获取年报PDF的路径
def load_pdf(code, year):
    file_path = r'E:\大学课程相关\大二下学期\5 数据科学导论\文本分析\年报'  # 自行修改
    file_name = "{}-{}年度报告".format(code,year)
    pdf_path = os.path.join(file_path,file_name)
    return pdf_path
import pandas as pd
import time
import random
import os

if __name__ == "__main__":
    # 读取股票代码信息
    data = pd.read_excel("stockcode.xlsx", converters={'stockcode': str})
    stockcodes = data['stockcode'].tolist()
    org_dict = get_orgid()

    # 年份范围从2010到2022
    years = [str(year) for year in range(2010, 2023)]

    # 遍历股票代码及年份列表
    for stock in stockcodes:
        for year in years:
            pdf_path = load_pdf(stock, year) + ".pdf"
            txt_path = load_pdf(stock, year) + ".txt"

            # 检查对应的PDF文件是否已经存在,如果存在则跳过下载步骤
            if os.path.exists(pdf_path):
                print("PDF文件已存在,跳过下载步骤。")
            else:
                req(stock, year, org_dict)
                time.sleep(random.randint(0, 2))
                

 2、文件转换:将PDF转换成TXT文件

import pandas as pd
import os
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import *
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage,PDFTextExtractionNotAllowed
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
# 获取年报PDF的路径
def load_pdf(code, year):
    file_path = r'E:\大学课程相关\大二下学期\5 数据科学导论\文本分析\年报'  # 自行修改
    file_name = "{}-{}年度报告".format(code,year)
    pdf_path = os.path.join(file_path,file_name)
    return pdf_path
def parsePDF(pdf_path,txt_path):
    # 以二进制读模式打开pdf文档
    fp = open(pdf_path,'rb')

    # 用文件对象来创建一个pdf文档分析器
    parser = PDFParser(fp)

    # pdf文档的对象,与分析器连接起来
    doc = PDFDocument(parser=parser)
    parser.set_document(doc=doc)

    # 如果是加密pdf,则输入密码,新版好像没有这个属性
    # doc._initialize_password()

    # 创建pdf资源管理器 来管理共享资源
    resource = PDFResourceManager()

    # 参数分析器
    laparam=LAParams()

    # 创建一个聚合器
    device = PDFPageAggregator(resource,laparams=laparam)

    # 创建pdf页面解释器
    interpreter = PDFPageInterpreter(resource,device)
    
    # 用来计数页面,图片,曲线,figure,水平文本框等对象的数量
    num_page, num_image, num_curve, num_figure, num_TextBoxHorizontal = 0, 0, 0, 0, 0

    # 获取页面的集合
    for page in PDFPage.get_pages(fp):
        num_page += 1  # 页面增一
        # 使用页面解释器来读取
        interpreter.process_page(page)

        # 使用聚合器来获取内容
        layout = device.get_result()
        # 这里layout是一个LTPage对象 里面存放着 这个page解析出的各种对象 一般包括LTTextBox, LTFigure, LTImage, LTTextBoxHorizontal 等等 想要获取文本就获得对象的text属性,
        for x in layout:
            if isinstance(x,LTImage):  # 图片对象
                num_image += 1
            if isinstance(x,LTCurve):  # 曲线对象
                num_curve += 1
            if isinstance(x,LTFigure):  # figure对象
                num_figure += 1
            if isinstance(x, LTTextBoxHorizontal):  # 获取文本内容
                num_TextBoxHorizontal += 1  # 水平文本框对象增一
                    # 保存文本内容
                with open(txt_path, 'a',encoding='UTF-8',errors='ignore') as f:
                    results = x.get_text()
                    f.write(results + '\n')
# 读取股票代码信息
rawdata = pd.read_excel(r'E:\大学课程相关\大二下学期\5 数据科学导论\文本分析\stockcode.xlsx', sheet_name=0, dtype={'stockcode': str})

# 获取DataFrame的行数
num_rows = len(rawdata)
print(num_rows)

# 年份范围从2010到2022
years = [str(year) for year in range(2010, 2023)]

for iloc in range(num_rows):
    code = rawdata.loc[iloc, 'stockcode']
    print(code)
    
    # 遍历固定的年份范围
    for year in years:
        print(year)
        pdf_path = load_pdf(code, year) + ".pdf"
        txt_path = load_pdf(code, year) + ".txt"
        
        # 检查对应的TXT文件是否已经存在,如果存在则跳过转换步骤
        if not os.path.exists(txt_path):
            print(pdf_path)
            parsePDF(pdf_path, txt_path)
        else:
            print("TXT文件已存在,跳过转换步骤。")

 3、计算指标

import jieba
import pandas as pd
from collections import Counter
import re
import os
import numpy as np

def remove_negations(words):
    negations = set(['非','别','不','没','无','忽','莫','否','没有','还没','毫无','无需','无关'])
    filtered_words = []
    skip = False
    for word in words:
        if word in negations:
            skip = True
        elif skip:
            skip = False
        else:
            filtered_words.append(word)
    return filtered_words

def word_frequency_by_category(text, stopwords, custom_dict, categories):
    words = jieba.cut(text)
    filtered_words = [word for word in words if word not in stopwords]
    filtered_words = remove_negations(filtered_words)
    filtered_words = [word for word in filtered_words if word in custom_dict]
    
    category_freq = Counter()
    for word in filtered_words:
        for category, words_in_category in categories.items():
            if word in words_in_category:
                category_freq[category] += 1
                break
    
    return category_freq

def preprocess_text(text):
    # 去除非中文字符和数字
    text = re.sub(r'[^\u4e00-\u9fa5]', '', text)
    text = re.sub(r'\d+', '', text)
    return text

# 计算指标函数
def calculate_indicators(df):
    # 计算数字化关键词在上市企业 i 第 t 年的年报中的词频
    df['ln(hkit+1)'] = np.log1p(df['词频'].astype(float))
    
    # 计算第 t 年上市企业年报文本总数 Qt
    total_reports = df.groupby('year').size().reset_index(name='Qt')
    df = pd.merge(df, total_reports, on='year', how='left')
    
    # 计算第 t 年包含第 k 个数字化关键词的年报文本数量 qkt
    keyword_counts = df.groupby(['year']).size().reset_index(name='qkt')
    df = pd.merge(df, keyword_counts, on='year', how='left')
    
    # 计算包含第 k 个数字化关键词的逆文本频率 ln(Qt/qkt+1)
    df['ln(Qt/qkt+1)'] = np.log1p(df['Qt'] / (df['qkt'] + 1).astype(float))
    
    # 计算指标 kit =∑k[ln(hkit+1)*ln(Qt/qkt+1)]
    df['k'] = df['ln(hkit+1)'] * df['ln(Qt/qkt+1)']
    kit = df.groupby('year')['k'].sum().reset_index(name='kit')
    df = pd.merge(df, kit, on='year', how='left')
    
    return df

# 读取停用词表
with open('stopwords.txt', 'r', encoding='utf-8') as f:
    stopwords = set(f.read().splitlines())

# 读取自定义词典
custom_dict = set([
    "人工智能", "商业智能", "图像理解投资决策辅助系统", "智能数据分析", "智能机器人", 
    "机器学习", "深度学习", "语义搜索", "生物识别技术", "人脸识别", "语音识别", 
    "身份验证", "自动驾驶", "自然语言处理","大数据", "数据挖掘", "文本挖掘", 
    "数据可视化", "异构数据", "征信", "增强现实", "混合现实", "虚拟现实","云计算", 
    "流计算", "图计算", "内存计算", "多方安全计算", "类脑计算", "绿色计算", 
    "认知计算", "融合架构", "亿级并发", "EB级存储", "物联网", "信息物理系统","区块链", 
    "数字货币", "分布式计算", "差分隐私技术", "智能金融合约","移动互联网", "工业互联网", 
    "移动互联", "互联网医疗", "电子商务", "移动支付", "第三方支付", "NFC支付", 
    "智能能源", "B2B", "B2C", "C2B", "C2C", "O2O", "网联", "智能穿戴", 
    "智慧农业", "智能交通", "智能医疗", "智能客服", "智能家居", "智能投顾", 
    "智能文旅", "智能环保", "智能电网", "智能营销", "数字营销", "无人零售", 
    "互联网金融", "数字金融", "Fintech", "金融科技", "量化金融", "开放银行"
])

# 自定义不同板块及其对应的词语
categories = {
    "人工智能技术": {"人工智能", "商业智能", "图像理解投资决策辅助系统", "智能数据分析", "智能机器人", "机器学习", "深度学习", "语义搜索", "生物识别技术", "人脸识别", "语音识别", "身份验证", "自动驾驶", "自然语言处理"},
    "大数据技术": {"大数据", "数据挖掘", "文本挖掘","数据可视化", "异构数据", "征信", "增强现实", "混合现实", "虚拟现实"},
    "云计算技术":{"云计算", "流计算", "图计算", "内存计算", "多方安全计算", "类脑计算", "绿色计算", "认知计算", "融合架构", "亿级并发", "EB级存储", "物联网", "信息物理系统"},
    "区块链技术":{"区块链", "数字货币", "分布式计算", "差分隐私技术", "智能金融合约"},
    "数据技术运用":{"移动互联网", "工业互联网", "移动互联", "互联网医疗", "电子商务", "移动支付", "第三方支付", "NFC支付", "智能能源", "B2B", "B2C", "C2B", "C2C", "O2O", "网联", "智能穿戴", "智慧农业", "智能交通", "智能医疗", "智能客服", "智能家居", "智能投顾", "智能文旅", "智能环保", "智能电网", "智能营销", "数字营销", "无人零售", "互联网金融", "数字金融", "Fintech", "金融科技", "量化金融", "开放银行"}
}

# 将自定义词典中的词语添加到分词词典中
for word in custom_dict:
    jieba.add_word(word)

# 指定年报文本文件夹路径
folder_path = r'C:\Users\leon\data\03 数据科学导论\第二次小组作业数据\年报'

# 创建一个空的 DataFrame 用于存储所有词频统计结果
all_results_df = pd.DataFrame(columns=['code', 'year', '板块', '词频'])

# 定义预处理函数并计算词频
def preprocess_and_calculate(text):
    text = preprocess_text(text)
    freq_dict = word_frequency_by_category(text, stopwords, custom_dict, categories)
    return freq_dict

# 遍历文件夹中的所有txt文件
for filename in os.listdir(folder_path):
    if filename.endswith(".txt"):
        file_path = os.path.join(folder_path, filename)
        
        code, year_str = filename.split('-')
        year = int(year_str[:4])  # 从文件名中提取年份
        
        with open(file_path, 'r', encoding='utf-8') as f:
            text = f.read()
        
        # 预处理文本并计算词频
        category_freq = preprocess_and_calculate(text)
        
        # 将词频结果存入 DataFrame
        df = pd.DataFrame(category_freq.items(), columns=['板块', '词频'])
        
        # 添加股票代码和年份列
        df['code'] = code
        df['year'] = year
        
        # 调整列的顺序
        df = df[['code', 'year', '板块', '词频']]
        
        # 将结果添加到 all_results_df 中
        all_results_df = pd.concat([all_results_df, df], ignore_index=True)

# 存储指标数据
all_results_with_indicators = calculate_indicators(all_results_df)

# 根据公司代码和年份分组,保留每个公司每年的唯一一个 kit 数据
unique_kit = all_results_with_indicators.groupby(['code', 'year'])['kit'].first().reset_index()

# 存储带有指标的数据
unique_kit.to_excel('指标构建.xlsx', index=False)

欢迎评论区批评指正!

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值