一、PSP表格
PSP | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|
计划 | 10 | 10 |
估计这个任务需要多少时间 | 5 | 6 |
开发 | 720 | 900 |
需求分析 | 600 | 720 |
生成设计文档 | 20 | 30 |
设计复审 | 5 | 4 |
代码规范 | 2 | 2 |
具体设计 | 60 | 180 |
具体编码 | 360 | 280 |
代码复审 | 60 | 30 |
测试 | 30 | 180 |
报告 | 120 | 110 |
测试报告 | 0 | 0 |
计算工作量 | 20 | 30 |
事后总结 | 30 | 60 |
合计 | 2012 | 2542 |
二、任务要求的实现
1.项目设计与技术栈
这一次任务被我拆分成了6个环节,分别是:
1.从零学习python与爬虫
2.通过爬虫获取要爬取的所有页面的url
3.通过爬虫爬取目的文本
4.对目的文本的处理,提取疫情数据
5.每日热点的实现
6.数据的可视化处理
完成各个环节的方法:
123.通过上网查询相关书籍、代码、视频学习爬虫
4.学习python处理爬到的文本,用到了beautifulsoup和正则表达式
5.根据所得数据分析,结合实际自主设计实现每日热点
6.学习使用excel等工具构建数据可视化动态展示
技术栈:JavaScript+Python+Excel+Pycharts
2.爬虫与数据处理
本次在爬虫上我所用的时间最长,遇到的主要问题是:所爬网站存在反爬机制,对于我这个之前从没用过爬虫的小萌新来说是个不小的挑战。
爬虫的实现:
因为反爬机制,用request指令经常爬不到正常的网页信息,返回412和爬到一堆乱码是两大难以解决的问题,因此我选择使用selenium来编写爬虫,部分代码如下:
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
def get_request(url): #selenium爬虫,返回网页源码
while 1:
driver = webdriver.Chrome(options=chrome_options) # 设置引擎为Chrome,在后台默默运行
driver.get(url)
EC_title = EC.title_contains('日') #爬取成功检测
print(EC_title(driver))
if EC_title(driver):
break
print('ERROR')
driver.close()
print('succeed!')
# print(con.text)
return driver.page_source
通过循环访问页面,直到判断语句判定爬取了正确的内容后,返回所爬页面的源码。运用了selenium模拟访问网页,这种方法的优点是稳定,不会因为爬取到错误代码而导致程序中止;缺点是selenium耗时较长。
顺带附上我爬取所有网址的部分代码,使用的是request方法:
def parse_single_html(html):#爬取链接
soup=BeautifulSoup(html.text,'html.parser')
datas=[]
title_nodes=(#返回了一个列表
soup
.find("ul",class_="zxxx_list")
.find_all("a")
)
for atitle in title_nodes:
link = atitle["href"]
datas.append(link)
return datas
def download_all_htmls():
htmls=[]
for idx in range (41):
if idx==0:
url = 'http://www.nhc.gov.cn/xcs/yqtb/list_gzbd'
else:
url=f"http://www.nhc.gov.cn/xcs/yqtb/list_gzbd_{idx+1}"
print("craw html:",url)
while 1:
con=requests.get(url,headers=headers)
con.encoding = 'utf-8'
print(con)#获取并输出
if con.status_code ==200:#爬取成功则跳出循环
break
time.sleep(0.5)#重爬间隔
htmls= htmls+parse_single_html(con)
print(parse_single_html(con))
print(htmls)
time.sleep(0.5)
f = open("htmls.txt", "w")
for ahtml in htmls: # 将所得所有url导入txt文档中
f.write('http://www.nhc.gov.cn')
f.write(ahtml + '\n')
f.close()
return htmls
数据处理
数据处理先利用BeautifulSoup处理网页源码,获取到网页中正文的文本信息(实现很方便),然后利用正则表达式提取出带有关键字词、目标格式的信息。部分代码如下:
def deal_data():
datas = open("data.txt", "w")#data.txt中存放提取的数据
htmls=[]
f=open("htmls.txt","r",encoding='utf-8')#读取txt文件中存储的网址
line=f.readline()
while line:
htmls.append(line.rstrip('\n'))#输入网页时不能带换行符,这里将其删去
line=f.readline()
f.close()
iu=0
for urls in htmls:
iu=iu+1
if iu>972:
break
summary= len(htmls)
print(urls)
print('爬取网址',iu,'共有',summary,'个')
url = urls
con = get_request(url) #爬取爬取
texts = con
result = BeautifulSoup(texts, 'lxml') # html.parser
div1 = result.find('title')#提取网页标题,目的是获取日期信息
print(div1)
div_date=re.search('[0-9]+月[0-9]+日',str(div1))
print(div_date.group())#成功提取日期
div2 = result.find('div', attrs={'class': 'con'})
div_def = div2.text.replace('\xa0' * 4, '\n\n\xa0\xa0')#处理网页正文部分
div_def = div_def.replace('(注:媒体引用时,请标注“信息来自国家卫生健康委员会官方网站”。)','')
div_def = div_def.replace('分享到','')
div_x=(re.search('新增确诊病例([\s\S]*?)\n',str(div_def)) or NoMatch)#通过正则提取
div_x=(re.search('本土([\s\S]*?))',str(div_x.group())) or NoMatch)
print(div_x.group())
div_bentuxinzeng=re.findall('河北[0-9]+|山西[0-9]+|辽宁[0-9]+|吉林[0-9]+|黑龙江[0-9]+|江苏[0-9]+|浙江[0-9]+|安徽[0-9]+|福建[0-9]+|江西[0-9]+|山东[0-9]+|河南[0-9]+|湖北[0-9]+|湖南[0-9]+|广东[0-9]+|海南[0-9]+|四川[0-9]+|贵州[0-9]+|云南[0-9]+|陕西[0-9]+|甘肃[0-9]+|青海[0-9]+|台湾[0-9]+|内蒙古[0-9]+|广西[0-9]+|西藏[0-9]+|宁夏[0-9]+|新疆[0-9]+|北京[0-9]+|天津[0-9]+|上海[0-9]+|重庆[0-9]+|香港[0-9]+|澳门[0-9]+|例[0-9]+例(在[\s\S]*?)',str(div_x.group()))
print(div_bentuxinzeng)
numlist = re.findall('[0-9]+', str(div_bentuxinzeng)) ##分离列表
prolist = re.findall(
'河北|山西|辽宁|吉林|黑龙江|江苏|浙江|安徽|福建|江西|山东|河南|湖北|湖南|广东|海南|四川|贵州|云南|陕西|甘肃|青海|台湾|内蒙古|广西|西藏|宁夏|新疆|北京|天津|上海|重庆|香港|澳门|台湾',
str(div_bentuxinzeng))
pro1list=(prolist)
num1list=(numlist)
print(pro1list)
print(num1list)#最终得到两个一一对应的列表
for i in range(len(pro1list)):#将数据写入txt
datas.write(div_date.group())
datas.write(',')
datas.write(pro1list[i])
datas.write(',')
datas.write(num1list[i]+'\n')
div_x = (re.search('新增无症状([\s\S]*?)\n', str(div_def)) or NoMatch)#同上,提取本土新增无症状
div_x = (re.search('本土([\s\S]*?))', str(div_x.group())) or NoMatch)
print(div_x.group())
div_wuzhengzhuangxinzeng=re.findall('河北[0-9]+|山西[0-9]+|辽宁[0-9]+|吉林[0-9]+|黑龙江[0-9]+|江苏[0-9]+|浙江[0-9]+|安徽[0-9]+|福建[0-9]+|江西[0-9]+|山东[0-9]+|河南[0-9]+|湖北[0-9]+|湖南[0-9]+|广东[0-9]+|海南[0-9]+|四川[0-9]+|贵州[0-9]+|云南[0-9]+|陕西[0-9]+|甘肃[0-9]+|青海[0-9]+|台湾[0-9]+|内蒙古[0-9]+|广西[0-9]+|西藏[0-9]+|宁夏[0-9]+|新疆[0-9]+|北京[0-9]+|天津[0-9]+|上海[0-9]+|重庆[0-9]+|香港[0-9]+|澳门[0-9]+|例[0-9]+例(在[\s\S]*?)',str(div_x.group()))
print(div_wuzhengzhuangxinzeng)
numlist = re.findall('[0-9]+', str(div_wuzhengzhuangxinzeng)) ##分离列表
prolist = re.findall(
'河北|山西|辽宁|吉林|黑龙江|江苏|浙江|安徽|福建|江西|山东|河南|湖北|湖南|广东|海南|四川|贵州|云南|陕西|甘肃|青海|台湾|内蒙古|广西|西藏|宁夏|新疆|北京|天津|上海|重庆|香港|澳门|台湾',
str(div_wuzhengzhuangxinzeng))
pro2list = (prolist)
num2list = (numlist)
print(pro2list)
print(num2list)
for i in range(len(pro2list)):
datas.write(div_date.group())
datas.write(',,,')#控制格式导入excel中
datas.write(pro2list[i])
datas.write(',')
datas.write(num2list[i] + '\n')
数据处理的难点在于一些早些网页的文本格式和近期不同,要额外考虑,导致代码冗长。
3.数据统计接口部分的性能改进
本次作业在花费时间方面,爬虫耗时在我对其“改进”后成倍增长(反爬机制确实遭不住啊),时间关系,在本次作业中我没有多的时间去探索一个更好的爬虫方法,不过今后我还要探索更加省时还能正常爬取网页的方法。
消耗最大的函数:deal_data()中的get_request(url)
4.每日热点的实现思路
我通过分析每一天所爬的数据,人为制定出一个标准,主要通过判断语句对数据进行分析,对当日中国本土以及各个省份的疫情情况作出简单的判断,与疫情数据一起写入excel中。部分代码如下:
summm1 = 0 #每日热点模块,根据人数对疫情进行分析研判
summm2 = 0
for i in range(len(pro1list)):
summm1 = summm1 + int(num1list[i])
if int(num1list[i]) > 40:
datas.write(div_date.group())
datas.write(',,,,,,,')
datas.write(pro1list[i] + '省')
datas.write('新增本土确诊人数为:')
datas.write(num1list[i])
datas.write('。防疫形势严峻。\n')
for i in range(len(pro2list)):
summm2 = summm2 + int(num2list[i])
if int(num2list[i]) > 200:
datas.write(div_date.group())
datas.write(',,,,,,,')
datas.write(pro2list[i] + '省')
datas.write('新增本土无症状感染者人数为:')
datas.write(num2list[i])
datas.write('。防疫形势严峻。\n')
if summm1==0:
datas.write(div_date.group())
datas.write(',,,,,,,')
datas.write('好耶!无本土新增确诊!\n')
if summm2==0:
datas.write(div_date.group())
datas.write(',,,,,,,')
datas.write('好耶!无本土新增无症状感染者!\n')
if summm1 > 0:
datas.write(div_date.group())
datas.write(',,,,,,,')
datas.write('新增本土确诊人数为:')
datas.write(str(summm1))
if summm1 < 25:
datas.write('。总体态势良好。\n')
elif summm1 < 50:
datas.write('。仍有潜在危险。\n')
elif summm1 < 200:
datas.write('。形势并不乐观。\n')
else:
datas.write('。防疫形势严峻。\n')
if (int(num1list[0]) >= summm1 / 2):
datas.write(div_date.group())
datas.write(',,,,,,,')
datas.write(pro1list[0])
datas.write('为重点防控省份。\n')
if summm2 > 0:
datas.write(div_date.group())
datas.write(',,,,,,,')
datas.write('新增本土无症状感染者人数为:')
datas.write(str(summm2))
if summm2 < 50:
datas.write('。总体态势良好。\n')
elif summm2 < 200:
datas.write('。仍有潜在危险。\n')
elif summm2 < 1000:
datas.write('。形势并不乐观。\n')
else:
datas.write('。防疫形势严峻。\n')
if (int(num2list[0]) >= summm2 / 2):
datas.write(div_date.group())
datas.write(',,,,,,,')
datas.write(pro2list[0])
datas.write('为重点防控省份。\n')
这种实现方法的优点:直观,有严格的判定方法。
缺点:不够全面,过于直板。
5.数据可视化界面的展示
数据可视化我选择的是利用基于Pycharts的WPS-excel,操作简单,内容直观。通过利用excel所提供的数据透视表和透视图技术,能够实现数据的动态化展示和一定的交互功能。界面展示如下:
页面左侧是每日新增确诊Top10和每日新增无症状Top15的省份,能够直观地看出全国的防疫重心应该放在哪个位置。右侧是对某个省份的新增确诊和无症状感染者的趋势图,利用折线统计图实现了对某个省疫情情况有记录的近10天的趋势。
中间部分是日期的选取和省份的选取,分别对应左右两侧的功能。
港澳台因为其特殊性,我单独列出置于中下部,今日热点置于右下方,会根据所选日期变化。
三、心得体会
本次个人编程任务我所耗时间略高于预计时间,其实我的估计时间已经放很宽了(个人认为),但实际用时更长,其主要原因是我在编程过程中遇到了比预想中更多的问题,从对抗网页的反爬机制到数据处理、数据可视化,我的实际用时都远超预计。我也是第一次感觉到即使熬夜学习,时间还是略显紧凑的感觉。
发现自己的问题:学习容易精力不集中,有些效率低下,导致时间消耗过多。
今天回头看看,我这用时约有四十多小时的作业虽然并不是很符合我最初的预想,但我还是成就感满满。在这个过程中,我所用到的所有技术几乎都是从零学起的。这是从无到有的过程,我真觉得这四十多小时,我学到的东西比以前一个月的还多(以前太懒)。我学会了网页爬取数据处理,这对我今后的发展肯定是大有所益的。