前言
大二下學期的大數據技術導論課上由於需要獲取數據進行分析,我決定學習python爬蟲來獲取數據。由於對於數據需求量相對較大,我最終選擇爬取
天氣后報網,該網站可以查詢到全國各地多年的數據,而且相對容易爬取。
需求分析:
(1)需要得到全國各大城市的歷史天氣數據集。
(2)每條天氣信息應該包含的內容包括城市名、日期、溫度、天氣、風向。
(3)以城市名分類,按日期存儲在可讀的文件中。
(4)存儲信息類型應該為字符型。
整體解決方案:
第一步:選擇適合進行信息爬蟲的網頁。
第二步:對該網頁相關信息所在的url進行獲取。
第三步:通過解析url對應的網頁獲取信息並存儲。
詳細解決方案:
第一步:選擇適合進行信息爬蟲的網頁。
(1)由於要獲取的是歷史天氣信息,我們不考慮常見的天氣預報網頁,最后選擇了“天氣后報網”作為目標網站。如下圖,該網站天氣信息按條分布,符合我們的爬蟲需求。
(2)我們查看了該網站的robots協議,通過輸入相關網址,我們沒有找到robots.txt的相關文件,說明該網站允許任何形式的網頁爬蟲。
(3)我們查看了該類網頁的源代碼,如下圖所示,發現其標簽較為清晰,不存在信息存儲混亂情況,便於爬取。
第二步:對該網頁相關信息所在的url進行獲取。
(1)對網頁的目錄要清晰的解析
為了爬取到全年各地各個月份每一天的所有天氣信息,我們小組首先先對網頁的層次進行解析,發現網站大體可以分為三層,第一層是地名的鏈接,通向各個地名的月份鏈接頁面,第二層是月份鏈接,對應各個網頁具體天數,第三個層次則是具體每一天的天氣信息,與是我們本着分而治之的原則,對應不同網頁的不同層次依次解析對應的網頁以獲取我們想要的信息。
天氣后報網的第一層層次-地名鏈接
天氣后報網第二層次-時間鏈接
天氣后報第三層次具體時間的信息
(1)解析地名網頁層次結構-獲取地名鏈接
第一個層次的網頁代碼
進過觀察第一個層次的網頁代碼,我們發現我們要提取的地名鏈接在class=”citychk”的div標簽的子孫節點<a>標簽的href的屬性中,於是我們調用beutifulsoup庫的findall()方法將a標簽以及它的內容放到一個集合中,依次遍歷將對應的鏈接寫道web_link.txt文件中。
解析地名鏈接的核心代碼
成功爬取的第一部分地名的鏈接
(2)解析地名網頁層次結構-獲取月份鏈接
進過我們觀察發現,我們想要的月份鏈接存儲在class=”box_pcity”的div標簽下的li內的a標簽的href屬性內,於是我們采用同樣的方法,依次遍歷提取我們想要的月份鏈接,並按照不同的地名,保存在對應地名的文件中。
第三步:通過解析url對應的網頁獲取信息並存儲。
(1)設置請求登錄網頁功能,根據不同的url按址訪問網頁,若請求不成功,拋出HTTPError異常。
(2)設置獲取數據功能,按照網頁源代碼中的標簽,設置遍歷規則,獲取每條數據。注意設置“encoding=‘gb18030’”,改變標准輸出的默認編碼, 防止控制台打印亂碼。
(3)設置文件存儲功能,將爬取的數據按年份和月份分類,分別存儲在不同的CSV文件中。注意文件名的設置為城市名加日期,方便后期整理。
4. 程序流程圖
5. 運行測試截圖
(1)url獲取
(2)解析網頁
附錄代碼:
爬蟲部分代碼
-------------------------------------------------------
get_all_city_info.py文件是為了獲取每個地方按照月份的鏈接的代碼
get_city_weather_link.py是為了獲取地名連接的代碼
get_city_weather_content.py是為了獲取具體信息的代碼
get_all_city_info.py
import urllib.request import urllib.parse from bs4 import BeautifulSoup import os def get_city_link():#將讀取到的文件轉化為字典 web_link={} f=open('web_link.txt') line=f.readline() while line: split_line=line.split(',') key=split_line[0] value=split_line[1] value=value.replace('\n','') web_link[key]=value line=f.readline() f.close() return web_link def get_place_name(web_link):#得到所有的地名 place=[] for key in web_link: place.append(key) return place def get_html(url):#依次訪問這些網站得到這些網站的html代碼 header = { 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)' } # 偽裝瀏覽器 request = urllib.request.Request(url, headers=header) response = urllib.request.urlopen(request) html = response.read() soup=BeautifulSoup(html,'html.parser') pretty_html=soup.prettify() return pretty_html def resolve_html(html,city): dict={} soup=BeautifulSoup(html,'html.parser') for div in soup.find_all('div',class_='box pcity'): for a in div.find_all('a'): new_time='' link='http://www.tianqihoubao.com'+a.attrs['href'] time=str(a.string) for i in range(len(time)): if time[i]!=' ' and time[i]!='\n': new_time=new_time+time[i] if time.find(city)>=0: dict[new_time]=link return dict def trans_dict_to_file(dict,city,file_path):#將爬取到的網址依次存放到文件中 file_path=file_path+city+'.txt' for key in dict: with open(file_path,'a') as f: f.write(key+','+dict[key]+'\n') def main(): path='D:\\face\weatherspider\\time_link\\' web_link=get_city_link() citys=get_place_name(web_link) count=0 for city in citys: try: count=count+1 print("正在抓取第%d個頁面..."%(count)) html=get_html(web_link[city])#獲取該城市的頁面 print("抓取第%d個頁面成功!"%(count)) keyword=city+'天氣' print('正在解析%d個網頁...'%(count)) dict=resolve_html(html,keyword) trans_dict_to_file(dict,city,path) print('解析第%d個網頁成功!'%(count)) except: continue main()
get_city_weather_content.py
# coding=utf-8 import io import sys import requests from bs4 import BeautifulSoup import numpy as np import pandas as pd import csv import time import urllib sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='gb18030') # 改變標准輸出的默認編碼, 防止控制台打印亂碼 def get_soup(year, month): url = 'http://www.tianqihoubao.com/lishi/' + 'changzhou/' + 'month' + '/' + str(year) + str(month) + '.html' try: r = requests.get(url, timeout=30) r.raise_for_status() # 若請求不成功,拋出HTTPError 異常 # r.encoding = 'gbk' soup = BeautifulSoup(r.text, 'lxml') return soup except HTTPError: return "Request Error" def saveTocsv(data, fileName): ''' 將天氣數據保存至csv文件 ''' result_weather = pd.DataFrame(data, columns=['date', 'tq', 'temp', 'wind']) result_weather.to_csv(fileName, index=False, encoding='gbk') print('Save all weather success!') def get_data(): soup = get_soup(year, month) all_weather = soup.find('div', class_="wdetail").find('table').find_all("tr") data = list() for tr in all_weather[1:]: td_li = tr.find_all("td") for td in td_li: s = td.get_text() data.append("".join(s.split())) res = np.array(data).reshape(-1, 4) return res if __name__ == '__main__': years = ['2011','2012','2013','2014','2015','2016','2017','2018','2019'] months = ['01','02','03','04','05','06','07','08','09','10','11','12'] for year in years: for month in months: data = get_data() saveTocsv(data, '常州'+str(year)+str(month)+'.csv')
get_city_weather_link.py
import urllib.request import urllib.parse from bs4 import BeautifulSoup def get_html(url): header = { 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)' } # 偽裝瀏覽器 request = urllib.request.Request(url, headers=header) response = urllib.request.urlopen(request) html = response.read() return html def main(): url = 'http://www.tianqihoubao.com/lishi/index.htm' html = get_html(url) pretty_html=parse_home_page(html) get_city_link(pretty_html) def parse_home_page(html): #將網頁熬成一鍋湯 soup=BeautifulSoup(html,'html.parser') pretty_html=soup.prettify() return pretty_html def get_city_link(html): website={} url='http://www.tianqihoubao.com' soup=BeautifulSoup(html,'html.parser') div_city=soup.find_all('div',class_='citychk') city_html=str(div_city[0]) city_soup=BeautifulSoup(city_html,'html.parser') for k in city_soup.find_all('a'): index=return_index(k['title']) place=k['title'][0:index] website[place]=url+k['href'] for key in website:#將各地天氣鏈接存到文件中 with open('web_link.txt','a') as f: f.write(key+','+website[key]+'\n') def return_index(s): for i in range(len(s)): if s[i]=='歷': break return i main()