新浪财经独立董事
本文作者:李朋冲
文字编辑:杨长青
技术总编:司海涛
好消息!!!爬虫俱乐部将于2019年7月5日至7月8日在武汉举行首期Python编程技术定制培训。本次培训采用理论与案例相结合的方式,旨在帮助零基础学员轻松入门Python,由浅入深学习和掌握Python爬虫技术,并明确未来更进一步的学习方向。
详细培训大纲及报名方式,请点击文末阅读原文呦~
2019年5月20日上午,中南财经政法大学会计学院举行了第68期学术午餐会,澳大利亚麦考瑞大学应用金融系田钢教授给大家讲述了他的论文——Courtesy Calls for Reciprocity: Appointment of Unqualified Independent Directors in China,文章主要探索了不合规的独立董事(UIDs)与高管的互惠关系,文章得出结论:UIDs很少投反对票,并且拥有UIDs的上市公司有更多的财务侵占和更大的信息不透明度。
田钢教授文中的“Unqualified Independent Directors”主要是指,在担任该公司独立董事时,其并未取得证监会所要求的独立董事资格。中国证监会在《关于在上市公司建立独立董事制度的指导意见》中指出,独立董事及拟担任独立董事的人士应当按照中国证监会的要求,参加中国证监会及其授权机构所组织的培训,一般来讲,需参加独立董事资格考试。
在学术讨论现场,众多老师对此提出质疑:一个公司提名独立董事,需向证监会备案,如果有未取得任职资格的独立董事,证监会会向提名公司提出质询;并且,不合格的独立董事(绝大多数情况下是首次担任独立董事),也会在任职后的短期内,取得任职资格,而文中所用数据是firm-year观测值,一个短期不合规行为是否会对公司的长期财务状况及信息披露状况造成影响,值得商榷。笔者也在现场提出了自己的意见:中国证监会要求,独立董事任期每届不超过三年,连续任期不得超过六年;如连续任期已经达到六年但还要继续担任该公司独立董事,需要至少一年的间隔期。但笔者通过爬取新浪财经独立董事信息发现,有些独立董事的连续任期已超过六年,这是否也是一种“Unqualified”行为?对此,田钢教授也表示会考虑这种情况,以补充已有不合规行为。
值得一提的是,田钢教授已过花甲之年,仍专心学术,讨论现场大家提出的各种质疑或者意见,他都很谦和地为大家解释,并写到了笔记本上,笔者对这个细节感触颇深:这不正是我们做学术需要有的态度吗?在此,我们向田钢教授致以深深的敬意!
下面,我们就使用Stata去获取独立董事的任职信息。笔者对比了几个常用网站:同花顺、东方财富网、和讯网和新浪财经,在公司高管信息方面,新浪财经最完整,既有在任高管,也有历任高管任职情况。我们在此只做一次爬虫(高管任职情况),二次爬虫(高管个人简历信息)留待以后补充。
单个公司的高管信息
我们首先获得一个公司的高管信息,在此以长江电力(股票代码:600900)为例,逐步给大家介绍如何获得任职信息。
首先进入新浪财经网站(https://finance.sina.com.cn/),输入股票代码——600900,点击左下方“公司高管”,即可看到如下界面:
网址中,我们看到了“600900”的数字,这就是股票代码,我们可以对此做循环,不足六位的股票代码,我们将其补齐(如万科:000002),该部分将在循环中讲述。注意到,陈国庆先生是长江电力总经理,当任起始任职日期是2018-07-05,终止日期未显示,这些就是我们需要的信息,我们来看源代码(右键,查看网页源代码或者查看源,浏览器不同,相应菜单名称也不尽相同)。
我们在源代码中查找“陈国庆”(ctrl+F),可看到如下信息:
在整个页面中,“陈国庆”出现了8次,注意蓝色方框,所在行依次显示:姓名、职务、起始日期、终止日期,这些是我们需要的信息。红色方框所在的行,是大标题(粗体,有“</strong>”这个HTML标签),这是我们不需要的信息,我们来看如下这两个截图:
以上第一个截图中,我们使用了“</div></td>”这个标签,结果把黑体标题所在的行也匹配了出来(绿框),同时我们也发现有空白信息行(红框),而这是我们不需要的信息。我们再看第二个截图,“</strong></div></td>”标签匹配出了12行(这是因为在原网页中有3个table,每个table的4个标题各占一行)。我们可以通过负向向后查找来匹配左边不是“</strong>”的“</div></td>”,代码如下:
clear
cap mkdir e:/独立董事
cd e:/独立董事
cap copy "http://vip.stock.finance.sina.com.cn/corp/go.php/vCI_CorpManager/stockid/600900.phtml" ///
temp.txt, replace
while _rc != 0 {
sleep 5000
cap copy "http://vip.stock.finance.sina.com.cn/corp/go.php/vCI_CorpManager/stockid/600900.phtml" ///
temp.txt, replace
}
infix strL v 1-100000 using temp.txt, clear
replace v = ustrfrom(v, "gb18030", 1) //转码
keep if ustrregexm(v, "(?<!</strong>)</div></td>")
这样,我们保留了高管信息所在的行,下一步就是把姓名、职务、起始日期、终止日期提取出来。观察下面的链接,
“/corp/view/vCI_CorpManagerInfo.php?stockid=600900&Name=陈国庆”
是一个链接的后半部分,加上“http://vip.stock.finance.sina.com.cn”就构成了一个完整的链接——
“http://vip.stock.finance.sina.com.cn/corp/view/vCI_CorpManagerInfo.php?stockid=600900&Name=陈国庆”,我们可以查看高管的个人简历,这部分内容牵扯到二次爬虫,我们在此不做展开。链接后面是所需信息——“陈国庆”,再后面是“</a></div></td>”标签,我们使用正则表达式的懒惰模式将尖括号及其中间的内容替换为空,代码如下:
replace v = ustrregexra(v, "<.*?>", "") //贪婪模式
观察上面的截图,第1、5、9行是高管姓名(4n+1);第2、6、10行是职务(4n+2);第3、7、11行是起始日期(4n+3);第4、8、12行是终止日期(4n)。我们使用如下代码,将每四行信息变作一行:
gen v1 = v[_n + 1]
gen v2 = v[_n + 2]
gen v3 = v[_n + 3]
上图第1、5行是我们需要的正确信息:
keep if mod(_n, 4) == 1
rename _all (姓名 职务 起始日期 终止日期)
gen stkcd = 600900
save "600900.dta", replace
这样,我们得到了长江电力的高管任职信息,通过循环,我们可以得到沪深两市3700多家上市公司的高管信息。下面我们进入到第二层次。
所有上市公司的高管信息
使用爬虫俱乐部优质产品——cnstock,我们可以获取沪深两市的股票代码,然后对股票代码进行循环。当然,放入局部宏以后,股票代码000002会变成2,我们需要把它补齐,然后再进行循环,中间过程与第一层次一样。由于股票代码较多,循环过程需要耗费一些时间,在此我们只给出代码,具体结果读者运行程序后自行查看。
clear
cnstock all
erase cnstock.dta
levelsof stkcd, local(stkcd)
foreach stk in `stkcd' {
while length("`stk'") < 6 {
local stk = "0`stk'"
}
cap copy "http://vip.stock.finance.sina.com.cn/corp/go.php/vCI_CorpManager/stockid/`stk'.phtml" ///
temp.txt, replace
while _rc != 0 {
sleep 5000
cap copy "http://vip.stock.finance.sina.com.cn/corp/go.php/vCI_CorpManager/stockid/`stk'.phtml" ///
temp.txt, replace
}
infix strL v 1-100000 using temp.txt, clear
replace v = ustrfrom(v, "gb18030", 1)
keep if ustrregexm(v, " (?<!</strong>)</div></td>")
replace v = ustrregexra(v, "<.*?>", "")
gen v1 = v[_n + 1]
gen v2 = v[_n + 2]
gen v3 = v[_n + 3]
keep if mod(_n, 4) == 1
rename _all (姓名 职务 起始日期 终止日期)
gen stkcd = `stk'
save "`stk'.dta", replace
}
获得了3700多家上市公司的高管信息,然后纵向合并,保留独立董事的观测值,程序如下:
clear
cd e:\独立董事
local files : dir "." files "*.dta"
foreach file in `files' {
append using `file'
}
foreach x in "起始日期" "终止日期" {
gen `x'1 = date(`x',"YMD")
drop `x'
rename `x'1 `x'
format %tdCY-N-D `x'
}
order stkcd 姓名 职务 起始日期 终止日期
keep if ustrregexm(职务, "(?<!非)独立董事") //只保留独立董事的观测值
save 独立董事.dta, replace //数据中没有证券名称
读者也可以用cnstock命令获得股票代码及名称,然后和独立董事.dta横向合并:
cnstock all
use 独立董事.dta, clear
merge m:1 stkcd using cnstock.dta, keep(match) nogen
order stkcd stknm 姓名 职务 起始日期 终止日期
compress
save 独立董事1.dta, replace // 加上了证券名称
限于篇幅,每个独立董事总的任职届数与时长,我们就不再讲述了,如果过程中大家遇到什么问题,欢迎与我们联系!
对爬虫俱乐部的推文累计打赏超过1000元我们即可给您开具发票,发票类别为“咨询费”。用心做事,只为做您更贴心的小爬虫!
往期推文推荐
关于我们
微信公众号“爬虫俱乐部”分享实用的stata命令,欢迎转载、打赏。爬虫俱乐部是由李春涛教授领导下的研究生及本科生组成的大数据分析和数据挖掘团队。
此外,欢迎大家踊跃投稿,介绍一些关于stata的数据处理和分析技巧。
投稿邮箱:statatraining@163.com
投稿要求:
1)必须原创,禁止抄袭;
2)必须准确,详细,有例子,有截图;
注意事项:
1)所有投稿都会经过本公众号运营团队成员的审核,审核通过才可录用,一经录用,会在该推文里为作者署名,并有赏金分成。
2)邮件请注明投稿,邮件名称为“投稿+推文名称”。
3)应广大读者要求,现开通有偿问答服务,如果大家遇到关于stata分析数据的问题,可以在公众号中提出,只需支付少量赏金,我们会在后期的推文里给予解答。