一.主題式網絡爬蟲設計方案
1.主題式網絡爬蟲名稱:全國疫情累計及福建省疫情爬蟲
2.主題式網絡爬蟲爬取的內容與特征分析:
a.內容:全國疫情爬蟲的內容為截至目前的各省的累計確診,累計治愈,累計死亡數據,福建省疫情爬蟲為1月22日至4月22日的每日新增確診,每日新增疑似,每日新增境外輸入確診,每日境外輸入疑似數據。
b.特征分析:全國疫情爬蟲的數據特征分析,全國疫情數據的內容為json格式,可以通過字典的形式進行訪問,福建省疫情數據為每日發布,通過html頁面獲得,多為數據格式不統一
3.主題式網絡爬蟲設計方案概述:
首先確定網址,以便於確定數據的來源,全國疫情數據選擇網址為(https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=316578012887), 福建省疫情數據選擇為(http://wjw.fujian.gov.cn/was5/web/search?)全國疫情網站不需要向服務器提交數據,福 建省疫情網站需要像網站提交數據。全國疫情數據返回的是一個json文件,將獲得到的文件內容保存,之后通過字典的訪問形式獲得對應數據。福建省疫情數據返回的是包含了子網頁的網址,將子網址的網址通過正則表達式匹配出來,之后進行訪問,該網站采取 的是XHR加載網頁,所以通過向服務器提交數據以便於獲得數據。
二.主題頁面的結構特征分析
A.全國疫情數據
B福建省疫情信息
三.網絡爬蟲設計
from bs4 import BeautifulSoup #導入beautifulsoup用來解析網頁 import requests #導入requests用於網頁訪問 import matplotlib.pyplot as plt #對數據畫圖,散點圖,柱狀圖等 import re #正則表達式用來匹配文章內容,過濾信息 import pandas as pd #修改格式,寫入excel import json #json文件獲取 plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標簽 plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負號 def GetPage():# 獲取網頁源碼 urllist = [] #保存匹配出來的網址 for i in range(1,7): URL = 'http://wjw.fujian.gov.cn/was5/web/search?' # 目標網頁地址 formdata = { "sortfield": "-docreltime,-docorderpri,-docorder", "templet": "docs.jsp", "channelid": "285300", "classsql": "chnlid=38586", "prepage": "20", "page": i, "r": "0.2607847720437618" } #post數據 header = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome / 53.0.2785.143Safari / 537.36', 'Connection': 'keep-alive', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Cookie': '_gscu_1005022938=86859196iyievf18; _gscbrs_1005022938=1' } html = requests.post(URL, data=formdata,headers=header) # 獲取網頁源碼 html.raise_for_status() p = re.compile(r'"url":"(http.*?)"') #正則匹配出跳轉網址 temp = re.findall(p,html.content.decode('UTF-8')) #匹配 urllist+=temp #保存跳轉網址 for i in urllist: #輸出網址 print(i) return urllist#返回網址 def GetMsg(urllist): header = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome / 53.0.2785.143Safari / 537.36', 'Connection': 'keep-alive', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Cookie': '_gscu_1005022938=86859196iyievf18; _gscbrs_1005022938=1' } for url in urllist: html = requests.get(url,headers=header) #獲取網頁源碼 soup = BeautifulSoup(html.content, 'html.parser') #使用html。parser解釋器 f = open('word.txt', 'a', encoding='UTF-8') #寫入文件 divs = soup.find_all('div',{'id':'detailContent'}) #匹配主要信息 for div in divs: #將匹配到的信息 afont = div.find_all('font') #找到所有文字 for font in afont: #對於每個匹配到的句子 str = font.text #得到文字 str = str.lstrip().rstrip() #去除左右空格 f.write(str)#寫入文件 f.write('\n') f.close() def Get_time_outland():#匹配信息 f = open('word.txt', 'r',encoding='UTF-8') #讀入文件 word = f.readlines()#讀入內容 time_re = re.compile('([0-9]月[0-9]*日)')#用來匹配日期 concern_re_outland = re.compile('新增境外輸入確診病例[0-9]*例|新增境外輸入新型冠狀病毒肺炎確診病例[0-9]*例')#匹配確診 doubt_re_outland = re.compile('新增境外輸入疑似病例[0-9]*例|新增境外輸入新型冠狀病毒肺炎疑似病例[0-9]*例')#匹配疑似 message_outland = {}#保存過濾出來的信息 for str in word:#對於每句文字 time_ = re.findall(time_re, str)#匹配日期 num_re = re.compile('[0-9]+')#匹配數字 concern_outland = re.findall(concern_re_outland, str)#匹配確診 doubt_outland = re.findall(doubt_re_outland, str)#匹配疑似 try: if time_[0] not in message_outland:#如果當前日期第一次出現 message_outland[time_[0]] = [[], []] if len(concern_outland) <=0:#未匹配到內容 message_outland[time_[0]][0] += '0' else: message_outland[time_[0]][0] += concern_outland#將匹配到的內容保存 if len(doubt_outland) <= 0: message_outland[time_[0]][1] += '0' else: message_outland[time_[0]][1] += doubt_outland except: pass for time_time_, str_list in message_outland.items():#對於匹配到的信息 for iterator in str_list: max_num = -1 #只需要確診或者疑似的最大數字,因為在文本中存在某市的數字 for i in iterator: max_num = max(max_num, int(re.findall(num_re, i)[0]))#獲得最大數字 message_outland[time_time_][str_list.index(iterator)]=max_num#講當日的確診疑似固定為最大數字 return message_outland#返回數字 def Get_time_inland():#同國外 f = open('word.txt', 'r',encoding='UTF-8') word = f.readlines() time_re = re.compile('([0-9]月[0-9]*日)') concern_re_inland = re.compile('新增[^境外].?.?.?.?.?.?.?.?確診病例[0-9]*例|新增.?.?.?新型冠狀病毒.?.?.?肺炎確診病例[0-9]*例') doubt_re_inland = re.compile('新增本地疑似病例[0-9]*例|新增.?.?.?新型冠狀病毒.?.?.?肺炎疑似病例[0-9]*例') message_inland = {} for str in word: time_ = re.findall(time_re, str) num_re = re.compile('[0-9]+') concern_inland = re.findall(concern_re_inland, str) doubt_inland = re.findall(doubt_re_inland, str) try: if time_[0] not in message_inland: message_inland[time_[0]] = [[], []] if len(concern_inland) <=0: message_inland[time_[0]][0] += '0' else: message_inland[time_[0]][0] += concern_inland if len(doubt_inland) <= 0: message_inland[time_[0]][1] += '0' else: message_inland[time_[0]][1] += doubt_inland except: pass message_inland['1月22日'][0] += '1'#最初發布信息的時候沒有准確數字,所以查看文字可知 確診一人 ,疑似為0 message_inland['1月22日'][1] += '0' message_inland['1月23日'][0] += '3'#同1月22日 message_inland['1月23日'][1] += '0' for time_time_, str_list in message_inland.items(): for iterator in str_list: max_num = -1 for i in iterator: try: max_num = max(max_num, int(re.findall(num_re, i)[0])) except: pass message_inland[time_time_][str_list.index(iterator)] = max_num return message_inland def write_to_excel(message,mark = 1):#講內容寫進excel xticks = []#用來保存日期 y1ticks = []#用來保存確診 y2ticks = []#用來保存疑似 for i,j in message.items():#內容格式為{日期:[確診,疑似]} 類型為字典 xticks.append(i)#日期 y1ticks.append(j[0])# y2ticks.append(j[1]) dicts = { '時間':xticks, '新增確診':y1ticks, '新增疑似':y2ticks } df = pd.DataFrame(dicts)#保存到excel中 if mark==1: df.to_excel('本地新增確診疑似.xlsx', index=False) else: df.to_excel('境外輸入新增確診疑似.xlsx', index=False) def re_prope(message):#獲得累計各個市內所有確診人數 prope_re = re.compile('..市[0-9]*例')#匹配某某市 total_re = re.compile('累計報告本地確診病例[0-9]*例')#匹配累計確診數量 num_re = re.compile('[0-9]*')#匹配數字 pro_re = re.compile('..市')#匹配市 f = open('word.txt', 'r',encoding='UTF-8')#打開文件 word = f.readlines()#讀取文件 for str in word: result = re.findall(prope_re, str)#匹配出市 result2 = re.findall(total_re, str)#匹配累計確診 break message = {}#保存清洗過后的信息 for i in result: pro = re.findall(pro_re, i)#匹配市 num = re.findall(num_re, i)#匹配數字 if pro[0] not in message:#市第一次出現 message[pro[0]] = 0# 先將人數置為0 for j in num: if len(j)>=1: j = int(j) message[pro[0]] = j #保存有效數字 else: pass temp = re.findall(num_re,result2[0])#匹配數字 for i in temp: if len(i)>=1: total = int(i) else: pass return message,total def draw_fig(message):#繪制折線圖 fig = plt.figure()#建立一個空圖 xticks = []#x軸標簽 y1ticks = []#y軸標簽 確診人數 y2ticks = []#y軸標簽 疑似人數 for i,j in message.items():#分割內容 xticks.append(i) y1ticks.append(j[0]) y2ticks.append(j[1]) xticks.reverse()#反轉內容,因為爬取來的信息是最近日期,所以反過來 y1ticks.reverse() y2ticks.reverse() #折線圖 ax1 = fig.add_subplot(211)#圖分為兩部分 plt.xlabel('時間') plt.ylabel('人數') plt.plot(xticks,y1ticks,label = '新增確診') plt.legend() plt.xticks(rotation=270) ax2 = fig.add_subplot(212) plt.xlabel('時間') plt.ylabel('人數') plt.plot(xticks, y2ticks, color='green',label = '新增疑似') plt.legend() plt.xticks(rotation=270) plt.savefig('福建確診疑似病例折線圖.jpg') def draw_bar(message):#繪制柱形圖 xvalue = []#x軸標簽 yvalue = []#y內容 for i,j in message.items(): xvalue.append(i) yvalue.append(j) plt.title('各市累計確診柱形圖') plt.bar(xvalue, yvalue) plt.savefig('福建各市累計確診柱形圖.jpg') def draw_pie(message,total_):#繪制餅圖 total = total_#總人數 xvalue = [] yvalue = [] for i, j in message.items(): xvalue.append(i) yvalue.append(j) labels = xvalue #餅標簽 share = []#每部分的比例 for i in yvalue: share.append(i/total) plt.title('各市累計確診比例') plt.pie(share, labels=labels,autopct = '%3.1f%%',) plt.savefig('福建各市累計確診比例餅圖.jpg') def nationwide(): url="https://c.m.163.com/ug/api/wuhan/app/data/list-total?t=316578012887" headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36' } try: r = requests.get(url, headers=headers) #獲取網址源碼 r.raise_for_status() r.encoding = r.apparent_encoding#轉碼 r = r.json()#轉換成json b = json.dumps(r,ensure_ascii=False) f2 = open('new_json.json','w',encoding='utf-8') f2.write(b)#保存數據 f2.close() except Exception as e: print('error: ',e) def parser_page(filename): f= open(filename, encoding='utf-8')#加載文件 data = json.load(f)#讀取內容 casedata = data['data']['areaTree'][2]['children']#獲得到國內的內容 result = {}#保存結果 for i in casedata: if i['name'] not in result:#省名字 result[i['name']] = [] result[i['name']].append(i['total']['confirm']) #累計確診 result[i['name']].append(i['total']['suspect']) #現存疑似 result[i['name']].append(i['total']['heal']) #累計治愈 result[i['name']].append(i['total']['dead']) #累計死亡 result[i['name']].append(i['lastUpdateTime'])#最近更新日期 print(result) return result def draw_nation_bar(data):#畫柱狀圖 xvalue = []#x軸標簽 y1value = []#確診人數 y2value = []#治愈人數 y3value = []#死亡人數 for i,j in data.items(): #數據內容為{日期:[累計確診,現存疑似,累計治愈,累計死亡,更新日期] xvalue.append(i) y1value.append(j[0]) y2value.append(j[2]) y3value.append(j[3]) fig = plt.figure() #空圖 ax1 = fig.add_subplot(311)#分成三份 plt.bar(xvalue[1:], y1value[1:]) plt.title("各省累計確診(除湖北)") ax2 = fig.add_subplot(312) plt.bar(xvalue[1:], y2value[1:]) plt.title("各省累計治愈(除湖北)") ax3 = fig.add_subplot(313) plt.bar(xvalue[1:], y3value[1:]) plt.title("各省累計死亡(除湖北)") plt.savefig('各省比例.jpg') def count_nation_pie(data): #繪制餅圖 xvalue = []#標簽 y1value = []#累計確診 y2value = []#累計治愈 y3value = []#累計死亡 total1 = 0 #確診總數 total2 = 0#治愈總數 total3 = 0#死亡總數 for i,j in data.items(): xvalue.append(i) y1value.append(j[0]) y2value.append(j[2]) y3value.append(j[3]) total1 += j[0] total2 += j[2] total3 += j[3] xvalue.remove(xvalue[0])#去除湖北, 因為湖北數據比例過大,影響直觀表現,所以去除湖北 total1 -= y1value[0] total2 -= y2value[0] total3 -= y3value[0] share1 = [] #個省份的比例 share2 = [] share3 = [] for i in y1value[1:]: share1.append(i/total1) for i in y2value[1:]: share2.append(i / total2) for i in y3value[1:]: share3.append(i / total3) print(xvalue, share1, share2, share3) return xvalue,share1,share2,share3 def draw_natiom_bar(share, labels,mark): fig = plt.figure() plt.pie(share, labels=labels, autopct='%3.1f%%') title = ['累計確診比例(除湖北)','累計治愈比例(除湖北)','累計死亡比例(除湖北)'] plt.title(title[mark-1]) plt.savefig('{}.jpg'.format(title[mark-1])) if __name__ == '__main__': urllist = GetPage() GetMsg(urllist) message_inland = Get_time_inland() message_outland = Get_time_outland() draw_fig(message_inland) draw_fig(message_outland) write_to_excel(message_inland, mark=1) #寫入本地確診疑似excel write_to_excel(message_outland, mark=2) #寫入境外確診疑似excel message,total = re_prope(message_inland) # draw_bar(message) draw_pie(message,total) nationwide() data = parser_page('new_json.json') draw_nation_bar(data) x,y1,y2,y3 = count_nation_pie(data) draw_natiom_bar(y1, x,1) draw_natiom_bar(y2, x,2) draw_natiom_bar(y3, x,3)
程序小結:
本程序通過post數據以獲得數據,同時也解決了爬取網址不會變化的問題,同時也實現了數據永久化的保存包括爬取下來的所有信息,通過txt文件,json文件,jpg圖像等將數據通過餅圖折線圖如柱狀圖,將數據更好的呈現出來,以便於用戶更好的獲得信息,通過獲得的這些信息,我也意識到了中國人民在面對疫情情況下的團結一心,一線工作人員的盡心竭力,這些數據的背后都是大家的努力,同時也希望大家勤洗手,多通風,不聚集,戴口罩,讓治愈數字越來越多,讓死亡和確診數字越來越少,加油福建,加油中國