需求
从指定网站中获取不同分类下的所有国自然项目数据,如优青、杰青、面上、重大等分类中的项目数据。
依赖包
- urllib3
- time
- csv
代码
整个过程分为数据获取和数据处理两部分,具体代码如下。
1.网页原始数据获取
import urllib3
import time
from urllib.parse import unquote
def download_content(url):
'''
根据链接下载数据
:param url: 链接
:return: 页面数据
'''
http = urllib3.PoolManager()
# 不登录只能查看20页,所以总页数需要通过查询条件限制在20页以内,建议通过起止时间来限制,便于分类和获取
body = {'startTime': '2016', 'endTime': "2021",
'addcomment_s1': 'D',
'searchsubmit': 'true',
'subcategory': '优秀青年基金项目'}
# letpub网站是POST请求,izaiwen网站(已停用)是GET请求
response = http.request('POST', url, fields=body)
# 从返回结果中获取页面内容,编码一般是utf-8
response_data = response.data
html_content = response_data.decode('utf-8')
unquote(html_content)
# print(html_content)
return html_content
def save_to_file(filename, content):
'''
保存数据到本地文件
:param filename: 文件名
:param content: 文件内容
:return: 本地文件
'''
fo = open(filename, 'w', encoding='utf-8')
fo.write(content)
fo.close()
if __name__ == '__main__':
# url需要根据在letpub网站中点击查询时,控制台中显示的实际地址`Request URL`修改
url_prefix = 'https://www.letpub.com.cn/nsfcfund_search.php?mode=advanced&datakind=list¤tpage='
# 总页数根据网页查询结果调整
page_total = 18
# 保存路径名建议根据查询条件按年份区分
path_prefix = './2016-2021/page-'
path_suffix = '.html'
# 从第一页开始获取,中间加休眠5~10秒左右,防止访问被禁
page_number = 1
while page_number <= page_total:
# 每页的url
url = url_prefix + str(page_number)
# 每页的原始数据
page_data = download_content(url)
# 每页的保存路径
save_path = path_prefix + str(page_number) + path_suffix
# 保存每页的数据
save_to_file(save_path, page_data)
print('page ' + str(page_number) + ' end')
# 休眠10秒
time.sleep(10)
# 访问下一个页面
page_number += 1
2.数据处理
import csv
def save_to_file(filename, content):
fo = open(filename, "w", encoding="utf-8")
fo.write(content)
fo.close()
def replace_string(content):
'''
页面数据处理方法(根据网站页面内容自定义,以letpub网站举例)
:param content: 页面内容
:return: 处理后的页面内容
'''
'''
页面数据分割的形式,可以打开原始的页面数据文件看看,找易于分割出需要内容的字符串,像letpub网站有下面两行明显标识,很好分割
'''
content = content.split('<!-- ################## start content ################## -->')[1]
content = content.split('<!-- ################## end content ################## -->')[0]
content = content.split('TR>')[2]
'''
替换页面中重复多次出现的标签
'''
# 此处标签替换为换行符\n是为了区分不同的项目(通常是每页10个项目)
content = content.replace('<tr style="background:#EFEFEF;">', '\n')
content = content.replace(
'<td style="border:1px #DDD solid; border-collapse:collapse; text-align:left; padding:8px 8px 8px 8px;">', '')
content = content.replace(
'style="border:1px #DDD solid; border-collapse:collapse; text-align:left; padding:8px 8px 8px 8px; font-size:13px; color:#333333;">',
'')
content = content.replace(
'<td style="border:1px #DDD solid; border-collapse:collapse; text-align:left; padding:8px 8px 8px 8px; font-size:13px; color:#333333;"',
'')
# 项目小标题对于实际内容获取没有帮助,直接替换掉;若需要保留,可以只替换</td>标签
content = content.replace('题目</td>', '')
content = content.replace('学科分类</td>', '')
content = content.replace('学科代码</td>', '')
content = content.replace('执行时间</td>', '')
content = content.replace('中文关键词</td>', '')
content = content.replace('英文关键词</td>', '')
content = content.replace('结题摘要</td>', '')
# 此处的替换字符串@@可以修改为任意其他的可以唯一区分的字符串(如##、#%),不建议使用空格、反斜杠等容易出现内容歧义的字符
content = content.replace('</td>', '@@')
content = content.replace('<td', '')
content = content.replace(
'style="border:1px #DDD solid; border-collapse:collapse; text-align:left; padding:8px 8px 8px 8px; color:#3b5998; font-weight:bold;">',
'')
content = content.replace('colspan="6">', '')
content = content.replace('</tr>', '')
content = content.replace('<tr>', '')
'''
单独处理页面中剩余的非通用标签,如下方的表头
'''
content = content.split('批准年份')[1]
content = content.replace('</th>', '')
content = content.replace('<', '')
content = content.replace('</th>', '')
content = content.replace(' ', '')
content = content.replace(' ', '')
# 返回结果
return content
if __name__ == '__main__':
'''
以下参数需要手动指定,以按年份查询区分为例
'''
# 页面文件的存储路径
origin_path_name = './2016-2021/'
# 文件前缀、后准、页面数量
file_name_prefix = 'page-'
file_name_suffix = '.html'
file_number = 18
# 结果输出的目标路径
target_path_name = './flushed_data/2016-2021/'
# 处理后文件的前缀、后缀
target_file_name_prefix = 'flushed-page-'
target_file_name_suffix = '.txt'
'''
需要从页面数据中获取的常用的信息
'''
# 负责人
charge_list = []
# 单位
department_list = []
# 金额 (万)
money_list = []
# 项目编号
project_number_list = []
# 项目类型
project_type_list = []
# 所属学部
xuebu_list = []
# 批准年份
pass_year_list = []
# 题目
title_list = []
# 学科分类
subject_type_list = []
# 学科代码
subject_code_list = []
# 执行时间
execution_time_list = []
# 记录所有页面中的每一行(因为预处理时不可以替换的换行符\n的存在,会出现空行,通过长度判断筛选掉)
lines = []
# 从第1个页面开始处理
page_number = 1
while page_number <= file_number:
# 处理后的页面内容
content = ''
# 页面文件名
file_name = origin_path_name + file_name_prefix + str(page_number) + file_name_suffix
# 打开文件获取数据,按行追加到页面内容中
with open(file_name, 'r+', encoding='utf-8') as f:
for line in f:
content += line
f.close()
# 页面数据处理(方法内容自定义,需要根据页面实际内容调整)
content = replace_string(content)
# print(content)
# 保存各处理后的页面内容到文件中
save_path = target_path_name + target_file_name_prefix + str(page_number) + target_file_name_suffix
save_to_file(save_path, content)
# 打开上一步保存的处理后的页面数据文件
with open(save_path, 'r', encoding='utf-8') as f:
# 将符合筛选要求的行内容添加到列表中(因为预处理时不可以替换的换行符\n的存在,会出现空行,通过长度判断筛选掉)
for line in f:
if (len(line) > 5):
line = line.replace('\n', '')
lines.append(line)
f.close()
# 下一个页面
page_number += 1
# 处理列表信息,分到各个单独的列表中
for line in lines:
# 可能因为‘结题摘要’等小项的存在,导致出现一些冗余行,先打印结果看看,在循环中加个筛选
if line.count('@@') >= 9:
# 页面数据处理方法中人为添加的分隔符做分割,按照网页数据中固定顺序分到不同的列表中
items = line.split('@@')
# print(items)
charge_list.append(items[0])
department_list.append(items[1])
money_list.append(items[2])
project_number_list.append(items[3])
project_type_list.append(items[4])
xuebu_list.append(items[5])
pass_year_list.append(items[6])
title_list.append(items[7])
subject_type_list.append(items[8])
subject_code_list.append(items[9])
execution_time_list.append(items[10])
# 把上方的列表合并为一个table
content_to_table = zip(charge_list, department_list, money_list, project_number_list, project_type_list, xuebu_list,
pass_year_list, title_list, subject_type_list, subject_code_list, execution_time_list)
# 把数据保存到表格中(csv文件如果用word打开乱码,可以用wps打开)
output_table_path = origin_path_name.split('/')[1] + '.csv'
with open(output_table_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
for row in content_to_table:
writer.writerow(row)
f.close()
本文作者:
whtli
本文链接: https://hexo.whtli.cn/archives/4017ce11.html
版权声明: 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
本文链接: https://hexo.whtli.cn/archives/4017ce11.html
版权声明: 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。