自科基金项目信息爬取
本文作者:宁刘莹
文字编辑:钱梦璇
技术总编:薛 原
众所周知,国家自然科学基金作为我国支持基础研究的主渠道之一,竞争十分激烈,若想中标,必然需要做好充分的准备,了解领域前沿,从过往项目中总结经验,这样才能有的放矢,提高自己项目的中标率。
但我们知道,若想使用国家自然科学基金委员会官方的查询系统(https://isisn.nsfc.gov.cn/egrantindex/funcindex/prjsearch-list)来查询项目信息,有几个选项是必填的,如资助类别和申请代码等,并不能查询某一类别的全部项目或者某个机构单位的全部项目等。
好消息是,近期小编发现了一个查询基金的网站,可以查询近20万个国家基金项目,网址为:http://fund.zsci.com.cn/fund/Index/index.html。这个网站的好处在于,可以根据项目名称、负责人、依托单位、学科分类等不同的属性进行检索,这就方便了我们批量获取项目信息。
在浏览器输入网址,可以看到主页面如下:
目前该网站收录的项目只有生命科学类和医学科学类,这里小编以河南大学为例,在依托单位输入“河南大学”点击查询,就可以看到河南大学全部的这两类基金项目了,共有271条结果(结果数量会实时更新):
点进去任意一条,可以看到该项目的详细信息:
其中红框内的部分是项目的主要信息,那么今天我们就来爬取全部271个项目的主要信息。
我们观察这些项目的链接,发现它的前半部分http://fund.zsci.com.cn/是固定不变的,只有后半部分中“id/”后面的数字有变化,也就是项目的id号:
因此,我们就需要先从上一级页面中得到全部项目的id号,这样就能获取这些项目的网页链接。
那么怎么获取id呢?观察网页源代码可以发现这样一个规律,所有项目的后半部分链接都位于<a>标签的href属性里面:
通过观察,不难发现,我们只需要获取所有该节点的href属性的属性值,就可以得到想要的信息了。
我们发现查询结果共有14页,先以一页为例,爬取第一页所有项目含有id的字符内容。我们在第一页按F12键(有些电脑需要按Fn+F12),在开发者工具里选择Network,此时刷新一下页面,可以看到该网页的请求方式是get:
往下拉我们发现了我们需要的请求头信息:
整理好思路,我们就可以开始爬虫了。
第一步,先导入第三方库并输入请求头中的信息
import requests
from lxml import etree
import pandas as pd
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Cookie": "PHPSESSID=4fkgfj0akrrdc63o7bc3ncn321",
"Host": "fund.zsci.com.cn",
"Referer": "http://fund.zsci.com.cn/Index/index.html",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
}
url="http://fund.zsci.com.cn/Index/index/danwei/%E6%B2%B3%E5%8D%97%E5%A4%A7%E5%AD%A6/start_year/0/end_year/0/xmid/0/search/1/p/1.html"
html=requests.get(url, headers= headers)
tree=etree.HTML(html.text) # 将源代码转换成element对象
url_xpath = '//ul/a/@href' # 获取url的xpath
url_list=tree.xpath(url_xpath) # 从源代码中提取信息
print(url_list)
这样,这些项目的后半部分链接文本就被抓取到了:
接下来我们就可以利用抓到的链接来获取第一页里每个项目的具体信息了。由于我们已经将第一页所有的项目链接放进了列表url_list中,因此对列表中每个链接循环获取项目信息:
title_xpath = '//h2/text()' # 获取项目标题的xpath
content_xpath = '//tbody/tr/td/div/text()' # 获取项目信息的xpath
keyword_xpath = "//div[@class='layui-table-body layui-table-main']/div/text()" # 获取项目关键词的xpath
for url in url_list :
eachhtml= requests.get("http://fund.zsci.com.cn"+url, headers= headers)
eachtree = etree.HTML(eachhtml.text) # 将源代码转换成element对象
con1_list = eachtree.xpath(title_xpath) # 从源代码中提取项目标题
con2_list = eachtree.xpath(content_xpath) # 从源代码中提取项目信息
con3_list = eachtree.xpath(keyword_xpath) # 从源代码中提取项目的关键词
con_list = con1_list+con2_list+con3_list
print(con_list)
fin1_list=[x.split(':') for x in con_list]
分割后的列表如图所示:
我们只需要保留每个子列表的最后一个元素,再次配合列表生成式:
fin2_list=[i[-1] for i in fin1_list]
此时得到的列表如图所示:
这里我们所写的两行命令也可以合并为一行,使代码更加简洁:
con_list = [x.split(':')[-1] for x in con_list]
到这里我们对一个项目的url的处理就结束了,但如果循环也到此结束,那么在下一次循环开始的同时,上一次的结果就不会被保留,因此要将最终列表拼接起来,这就需要在循环开始之前生成一个空列表:
整段程序代码如下:
all_con = []
for url in url_list :
eachhtml= requests.get("http://fund.zsci.com.cn"+url, headers= headers)
eachtree = etree.HTML(eachhtml.text) # 将源代码转换成element对象
con1_list = eachtree.xpath(title_xpath) # 从源代码中提取项目标题
con2_list = eachtree.xpath(content_xpath) # 从源代码中提取项目信息
con3_list = eachtree.xpath(keyword_xpath) # 从源代码中提取项目的关键词
con_list = con1_list+con2_list+con3_list
con_list = [x.split(':')[-1] for x in con_list]
all_con.append(con_list)
到这里我们就已经把搜索结果里第一页中包含的所有项目爬下来了,若要爬取全部271条结果,只需要对14页查询结果页面进行循环即可,这里为避免重复生成空列表,我们将空列表all_con放至最外层循环之前,爬取全部项目信息的程序如下:
all_con = []
for i in range(1,15):
url= f"http://fund.zsci.com.cn/Index/index/danwei/%E6%B2%B3%E5%8D%97%E5%A4%A7%E5%AD%A6/start_year/0/end_year/0/xmid/0/search/1/p/{i}.html"
html= requests.get(url, headers= headers)
tree = etree.HTML(html.text)
url_list = tree.xpath(url_xpath)
for url in url_list :
eachhtml= requests.get("http://fund.zsci.com.cn"+url, headers= headers)
eachtree = etree.HTML(eachhtml.text) # 将源代码转换成element对象
con1_list = eachtree.xpath(title_xpath) # 从源代码中提取项目标题
con2_list = eachtree.xpath(content_xpath) # 从源代码中提取项目信息
con3_list = eachtree.xpath(keyword_xpath) # 从源代码中提取项目的关键词
con_list = con1_list+con2_list+con3_list
con_list = [x.split(':')[-1] for x in con_list]
all_con.append(con_list)
最后,只需要将列表输出即可,这里我们使用Pandas库中的DataFrame函数创建二维表:
file = "D:/河大自科项目信息.xlsx"
varname_list = ['项目名称','批准号','批准年度','学科分类','项目负责人','负责人职称','依托单位','资助金额','项目类别','研究期限','中文主题词','英文主题词'] # 生成表头列表作为列名
df = pd.DataFrame(columns = varname_list, data = all_con)
df.to_excel(file)
让我们看看最终得到的表格吧:
完整的程序如下所示:
import requests
from lxml import etree
import pandas as pd
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Cookie": "PHPSESSID=4fkgfj0akrrdc63o7bc3ncn321",
"Host": "fund.zsci.com.cn",
"Referer": "http://fund.zsci.com.cn/Index/index.html",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
}
url_xpath = '//ul/a/@href'
title_xpath = '//h2/text()'
content_xpath = '//tbody/tr/td/div/text()'
keyword_xpath = "//div[@class='layui-table-body layui-table-main']/div/text()"
all_con = []
for i in range(1,15):
url = f"http://fund.zsci.com.cn/Index/index/danwei/%E6%B2%B3%E5%8D%97%E5%A4%A7%E5%AD%A6/start_year/0/end_year/0/xmid/0/search/1/p/{i}.html"
html= requests.get(url, headers= headers)
tree = etree.HTML(html.text)
url_list = tree.xpath(url_xpath)
for url in url_list :
eachhtml= requests.get("http://fund.zsci.com.cn"+url, headers= headers)
eachtree = etree.HTML(eachhtml.text) # 将源代码转换成element对象
con1_list = eachtree.xpath(title_xpath) # 从源代码中提取项目标题
con2_list = eachtree.xpath(content_xpath) # 从源代码中提取项目信息
con3_list = eachtree.xpath(keyword_xpath) # 从源代码中提取项目的关键词
con_list = con1_list+con2_list+con3_list
con_list = [x.split(':')[-1] for x in con_list]
all_con.append(con_list)
file = "D:/河大自科项目信息.xlsx"
varname_list = ['项目名称','批准号','批准年度','学科分类','项目负责人','负责人职称','依托单位','资助金额','项目类别','研究期限','中文主题词','英文主题词']
df = pd.DataFrame(columns = varname_list, data = all_con)
df.to_excel(file)
但是小编在爬虫过程中发现该数据存在一些问题,比如下面这个项目存在着明显的类别划分错误,因此大家可以仅把该网站作为一个参考。
关于我们
微信公众号“Stata and Python数据分析”分享实用的stata、python等软件的数据处理知识,欢迎转载、打赏。我们是由李春涛教授领导下的研究生及本科生组成的大数据处理和分析团队。
1)必须原创,禁止抄袭;
2)必须准确,详细,有例子,有截图;
注意事项:
1)所有投稿都会经过本公众号运营团队成员的审核,审核通过才可录用,一经录用,会在该推文里为作者署名,并有赏金分成。
2)邮件请注明投稿,邮件名称为“投稿+推文名称”。
3)应广大读者要求,现开通有偿问答服务,如果大家遇到有关数据处理、分析等问题,可以在公众号中提出,只需支付少量赏金,我们会在后期的推文里给予解答。