社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python:基于selenium爬取科创板审核问询

连享会 • 1 年前 • 281 次点击  

👇 连享会 · 推文导航 | www.lianxh.cn

连享会课程 · 基于机器学习的因果推断方法

作者:范思妤 (南京大学)
邮箱:fansiyu@smail.nju.edu.cn

温馨提示: 文中链接在微信中无法生效。请点击底部「阅读原文」。或直接长按/扫描如下二维码,直达原文:


目录

  • 1. 科创板审核问询披露

  • 2. 爬虫实战

    • 2.1 安装 selenium 及浏览器驱动

    • 2.2 浏览器驱动配置使用

    • 2.3 分析网页结构

    • 2.4 获取 url 列表

    • 2.5 遍历 url 列表爬取审核问询披露

  • 3. 相关推文



1. 科创板审核问询披露

科创板试点的注册制改革,强调“以信息披露为核心”。为在发行上市审核坚持以信息披露为核心,把好上市企业入口质量关,上交所主要采用公开化问询式审核方式,即交易所提出问询的问题,发行人对这些问题进行回复和说明,中介机构对问询事项的核查过程和结论,以“一问一答”的方式及时向市场公开。

本文重点关注如何通过 Python 的 selenium 库爬取上述审核问询流程披露的原始文件,并在发行公司层面对问询与回复情况进行汇总统计。

2. 爬虫实战

2.1 安装 selenium 及浏览器驱动

在本文中,我们使用 Python 中的 selenium 库对网页进行爬取。selenium 通过创建模拟浏览器的方式进行爬取,可以完全模拟真实用户的动态操作。在配置好 Python 环境后,打开命令提示符,并键入如下命令安装 selenium 库:

pip install selenium

或者使用国内镜像源安装 selenium 库:

pip install selenium -i http://pypi.douban.com/simple/ --trusted-host  pypi.douban.com

接下来,我们需要安装浏览器驱动。此处我们主要讲解 Windows 系统安装 chrome 浏览器驱动的步骤和方法。其他系统及其他浏览器驱动的安装,请自行网络搜索相关教程。

首先,我们需要确定 chrome 浏览器版本。打开 chrome 浏览器,在新标签页输入 chrome://settings/help 进入设置界面,查看目前的版本信息。

在获取浏览器版本信息后,需下载对应版本的浏览器驱动。打开 chrome 浏览器 驱动下载地址,找到和目前版本最接近的驱动,Windows 系统用户需下载 win32 版本。

解压驱动文件包得到 chromedriver.exe,保存到指定路径,并务必将当前路径添加到环境变量中(我的电脑 右键属性 高级系统设置 高级 环境变量 系统变量 Path)。

2.2 浏览器驱动配置使用

我们使用如下代码创建浏览器驱动对象。此外,我们可以根据爬虫目标网页调整浏览器选项,提供稳定运行环境。

from selenium import webdriver
from selenium.webdriver import ChromeOptions

CHROME_OPTIONS = webdriver.ChromeOptions()
prefs = {"profile.managed_default_content_settings.images":2}   # 1代表显示图片,2代表不显示图片
CHROME_OPTIONS.add_experimental_option("prefs", prefs)
CHROME_OPTIONS.add_experimental_option("excludeSwitches",["enable-automation"]) 
CHROME_OPTIONS.add_experimental_option("useAutomationExtension"False)
CHROME_OPTIONS.add_argument('--disable-blink-features=AutomationControlled')
CHROME_DRIVER = r'D:/chromedriver/chromedriver.exe'  # 此处为chromedriver.exe所在路径

# 声明浏览器
driver = webdriver.Chrome(executable_path=CHROME_DRIVER, options=CHROME_OPTIONS)  

2.3 分析网页结构

首先,我们需要浏览待爬取的页面结构,并检查网页确定网页类型。

上交所科创板股票审核页面如下。最上方一行分类列示已递交申请的发行人的状态,我们的样本仅爬取已经有注册结果的发行人(即已经走完整个科创板审核问询流程。

所有发行人按照时间由近及远的顺序列示在页面表格中。每行发行人全称对应一个超链接,点击即进入发行人审核问询的详情页面。因此,我们整体的爬虫思路如下:

  • 获取全部发行人对应的链接列表;
  • 遍历目标链接列表爬取某一页面的特定信息,并储存到本地。

2.4 获取 url 列表

如上所述,我们仅爬取已经有注册结果的发行人。因此,首先需要用 selenium 自动化模拟鼠标点击操作,以点击页面上方的“注册结果”,获取跳转后的页面上的信息。

# 导入所需要的包
from selenium.webdriver.common.by import By
import time

# 请求页面
url = "http://kcb.sse.com.cn/renewal/"
driver.get(url)           

# 通过Xpath定位到“注册结果”,并点击,等待页面加载
driver.find_element(By.XPATH,'//*[@id="select5"]/a/span[2]').click()
time.sleep(3)

通过加载后的检查页面,我们发现 url 就包含在发行人全称的 href 属性中。我们进行如下步骤的操作以获取完整的发行人 url 列表:

  • 定位发行人全称,并提取元素的 href 属性值;
  • 定位“下一页”按钮,模拟鼠标操作进行翻页;
  • 重复以上两步,直至最后一页。

selenium 提供了多种元素定位方式,如 xpath,id,name 等。在本文中,我们用 Xpath 定位元素。具体步骤如下图,得到对应元素的 Xpath 后,通过语法 @href 选取元素属性。

代码如下:

# Xpath定位元素
all_url_list = []
url_xpath = '//*[@id="dataList1_container"]/tbody/tr/td[2]/a/@href'
next_page = '//*[@id="dataList1_container_next"]/span'

# 获取发行人url列表
for i in range(1,28):        # 最大页数是28
    print("正在抓取第%s页的内容" %i)
    html = driver.page_source
    tree = etree.HTML(html)
    url_list = tree.xpath(url_xpath)
    all_url_list.extend(url_list)
    time.sleep(1)
    if i<=26 : 
        # 点击下一页
        driver.find_element(By.XPATH,next_page).click()   
        time.sleep(2)        # 等待页面加载
    else:         
        pass                 # 最后一页没有“下一页”
driver.quit()

# 打印最后10个url,检查是否所有url爬取成功
for url in all_url_list[-10:]:
    print(url)

2.5 遍历 url 列表爬取审核问询披露

在获取了全部发行人 url 列表之后,我们需要遍历 url 列表,获取特定发行人审核问询的详情页面。在这一页面,我们需要爬取的关键内容如下:

发行人基本信息:

  • 公司全称(fullname)
  • 公司简称(shortname)
  • 保荐机构(broker)
  • 会计师事务所(auditor)
  • 律师事务所(lawyer)
  • 受理日期(acceptdate)
  • 首次问询日期(inquirydate)
  • 上市委会议通过日期(passdate)

审核问询披露文件:

  • 发行人及保荐机构回复意见
  • 会计师回复意见
  • 法律意见书

代码如下:


#————爬取每个url对应的科创板发行上市公司页面————#

# 导入所需的包
import time
from lxml import etree
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
import requests
import os
from urllib.request import urlretrieve

# 在路径下创建文件夹
os.mkdir(r'./companies')

# 浏览器驱动配置及使用
CHROME_OPTIONS = webdriver.ChromeOptions()
prefs = {"profile.managed_default_content_settings.images":2}   # 1代表显示图片,2代表不显示图片
CHROME_OPTIONS.add_experimental_option("prefs", prefs)
CHROME_DRIVER = r'D:/chromedriver/chromedriver.exe'
driver = webdriver.Chrome(executable_path=CHROME_DRIVER, options=CHROME_OPTIONS)

# 发行人基本信息的Xpath定位
fullname_xpath        = '//*[@id="issuer_full"]'
shortname_xpath       = '//*[@id="issuer_sec"]'
broker_xpath          = '//*[@id="sponsor_org"]/a'
auditor_xpath         = '//*[@id="accounts_org"]/a'
lawyer_xpath          = '//*[@id="law_firm"]/a'
acceptdate_xpath      = '//*[@id="step1F"]/div'
inquirydate_xpath     = '//*[@id="step2F"]/div'
passdate_xpath        = '//*[@id="step3F"]/div'

# 注册制审核问询披露文件的Xpath定位
SECLetter_xpath       = '//*[@id="yjhf"]/tbody/tr/td[2]/a'

# 创建空列表以盛放后续爬虫信息
all_fullname    = []
all_shortname   = []
all_broker      = []
all_auditor     = []
all_lawyer      = []
all_acceptdate  = []
all_inquirydate = []
all_passdate    = []
all_num_letter1 = []
all_num_letter2 = []
all_num_letter3 = []

for url_item in all_url_list:
    url = 'http://kcb.sse.com.cn' + url_item
    driver.get(url) 
    time.sleep(2)
    html = driver.page_source
    tree = etree.HTML(html)
    
    # 获取发行人基本信息的文本节点
    fullname_list    = tree.xpath(fullname_xpath + '/text()')
    all_fullname.append(fullname_list[0])

    shortname_list   = tree.xpath(shortname_xpath+ '/text()')
    all_shortname.append(shortname_list[0])
    
    broker_list      = tree.xpath(broker_xpath+ '/text()')
    content          = ';'.join(broker_list)  # 可能会有多个保荐机构,用分号链接,下同
    all_broker.append(content)
    
    auditor_list     = tree.xpath(auditor_xpath+ '/text()')
    content          = ';'.join(auditor_list)
    all_auditor.append(content)
    
    lawyer_list      = tree.xpath(lawyer_xpath+ '/text()')
    content          = ';'.join(lawyer_list)
    all_lawyer.append(content)
    
    acceptdate_list  = tree.xpath(acceptdate_xpath+ '/text()')
    all_acceptdate.append(acceptdate_list[0])
    
    inquirydate_list = tree.xpath(inquirydate_xpath+ '/text()')
    all_inquirydate.append(inquirydate_list[0])
    
    passdate_list    = tree.xpath(passdate_xpath+ '/text()')
    all_passdate.append(passdate_list[0])

    # 获取问询与回复信息的文本节点
    SECLetter_list   = tree.xpath(SECLetter_xpath  + '/text()')
    # 获取问询与回复的href属性,为后续下载对应文件pdf做准备
    SECLetter_url    = tree.xpath(SECLetter_xpath  + '/@href')
    
    # 为每一公司创建子文件夹,用以盛放分类后的文件pdf
    os.mkdir('./companies/'+fullname_list[0])
    os.mkdir('./companies/'+fullname_list[0] + '/问询函和回函/')
    os.mkdir('./companies/'+fullname_list[0] + '/审计意见/')
    os.mkdir('./companies/'+fullname_list[0] + '/法律意见/')
    os.mkdir('./companies/'+fullname_list[0] + '/其他/')
    
    # 注册制审核问询披露文件的计数算子
    count_letter1 = 0  
    count_letter2 = 0
    count_letter3 = 0
    
    #获取pdf文件方法1,用requests.get:
    for i in range(len(SECLetter_list)):
        url = SECLetter_url[i]
        r = requests.get('http:'+ url)

        # 通过文件名称对文件进行分类,并计数
        if  ('发行人'in SECLetter_list[i]) or ('落实函' in SECLetter_list[i])           \
            and  ('会计' not in SECLetter_list[i]) and ('律师'not in SECLetter_list[i]) \
            and ('法律' not in SECLetter_list[i]):
            with open('./companies/'+fullname_list[0] + '/问询函和回函/'+SECLetter_list[i]+'.pdf''wb+'as f:
                f.write(r.content)
                count_letter1 += 1
        elif  '会计' in SECLetter_list[i]:  
            with open('./companies/'+fullname_list[0] + '/审计意见/'+SECLetter_list[i]+'.pdf''wb+'as f:
                f.write(r.content)
                count_letter2 += 1
        elif ('律师'in SECLetter_list[i]) or ('法律' in SECLetter_list[i]):
            with open('./companies/'+fullname_list[0] + '/法律意见/'+SECLetter_list[i]+'.pdf''wb+'as f:
                f.write(r.content)
                count_letter3 += 1
        else :
            with open('./companies/'+fullname_list[0] + '/其他/'+SECLetter_list[i]+'.pdf''wb+'as f:
                f.write(r.content)
    
    all_num_letter1.append(count_letter1)
    all_num_letter2.append(count_letter2)
    all_num_letter3.append(count_letter3)
        
    '''
    # 获取pdf文件方法2,用urlretrieve:
    # 以下代码仅做方法思路展示,未对pdf进行进一步分类
    for i in range(len(SECLetter_list)):
        url = 'http:' + SECLetter_url[i]
        urlretrieve(url,filename = './科创板/companies/'+fullname_list[0]+'/'+SECLetter_list[i]+'.pdf')
    '''

driver.quit()

# 将爬虫信息储存到本地excel文件
file = r".\科创板注册制信息披露.xlsx"
final_data = [all_fullname,all_shortname,all_broker,all_auditor,all_lawyer,all_acceptdate, \
    all_inquirydate,all_passdate,all_num_letter1,all_num_letter2,all_num_letter3]
df = pd.DataFrame(final_data).T
df.columns = ["公司全称""公司简称""保荐机构""会计师事务所""律师事务所","受理日期", \
    "开始问询日期","上市委通过日期","问询与回函数量","审计意见数量","律师意见数量"]
df.to_excel(file, index = None)

最终,我们可以得到如下审核问询情况汇总及审核问询披露文件的原始 pdf:

注意:以上代码在计算“问询与回函数量”、“审计意见数量”、“律师意见数量”时仅简单地计数页面上所列示的对应文件的数量,但并不代表发行人实际被问询次数。在现实中,若公司在季度末或者年末更新财务报表,则需根据更新后的财务报表向交易所重新提交以上文件。举个例子:《发行人及保荐机构回复意见》和《发行人及保荐机构回复意见(2022年半年报财务数据更新版)》本质上属于同一份回函的两个版本,计算一次问询次数,而不是两次。此处请读者仔细甄别。

3. 相关推文

Note:产生如下推文列表的 Stata 命令为:
lianxh 爬虫, m
安装最新版 lianxh 命令:
ssc install lianxh, replace

  • 专题:专题课程
    • ⏩ 专题课:文本分析-爬虫-机器学习-2022年4月
    • 连享会:助教入选通知-2022文本分析与爬虫
    • ⚽助教招聘:文本分析-爬虫-机器学习
    • 助教入选结果 - 连享会 文本分析与爬虫直播课
  • 专题:文本分析-爬虫
    • Stata爬虫:爬取地区宏观数据
    • Stata爬虫:爬取A股公司基本信息
    • Stata爬虫-正则表达式:爬取必胜客
    • Python爬虫: 《经济研究》研究热点和主题分析
  • 专题:Python-R-Matlab
    • Python:多进程、多线程及其爬虫应用
    • Python爬虫1:小白系列之requests和json
    • Python爬虫2:小白系列之requests和lxml
    • Python爬虫:爬取华尔街日报的全部历史文章并翻译
    • Python爬虫:从SEC-EDGAR爬取股东治理数据-Shareholder-Activism

课程推荐:因果推断实用计量方法
主讲老师:邱嘉平教授
🍓 课程主页https://gitee.com/lianxh/YGqjp

New! Stata 搜索神器:lianxhsongbl  GIF 动图介绍
搜: 推文、数据分享、期刊论文、重现代码 ……
👉 安装:
. ssc install lianxh
. ssc install songbl
👉  使用:
. lianxh DID 倍分法
. songbl all

🍏 关于我们

  • 连享会 ( www.lianxh.cn,推文列表) 由中山大学连玉君老师团队创办,定期分享实证分析经验。
  • 直通车: 👉【百度一下: 连享会】即可直达连享会主页。亦可进一步添加 「知乎」,「b 站」,「面板数据」,「公开课」 等关键词细化搜索。


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/152720
 
281 次点击