python學習之路——爬蟲天氣預報


背景

 抱着《python學習手冊》啃了很久,心里想着要動手寫點東西,但是一直拖延症到最近才真正開始准備。一開始不知道寫點啥好,就從生活中挖掘,發現自己每天查天氣預報查的挺頻繁的,那就爬一波天氣預報吧。

技術概覽

  1. selenium
  2. time
  3. re
  4. calendar
     爬取網頁數據的過程中就用到以上4個模塊,其中calendar是可選的,最初的版本是只爬取當月的每日天氣所以不需要calendar,后續新增了可選月份的功能,才加上calendar。本次爬蟲是用類來實現的,但實際上用簡單函數即可,但抱着學以致用的想法還是用類吧,雖然類有很多很強大的功能(繼承、多態、定制等),但在我這幾十行的代碼里並不能體現出來。我的環境是win10+python27,因為工作需要所以個人PC也是python2的,后續會更新成python3。

具體內容

class Weather(object):
    def __init__(self):
        self.day = time.strftime('%d', time.localtime(time.time()))
        self.month = time.strftime('%m', time.localtime(time.time()))

 在類的開始定義了一個構造函數,里面有兩個屬性:day和month分別是今天的日期和當月月份,這里就用到time模塊中的time、localtime、strftime。詳細的介紹可以看菜鳥教程,我們現場實踐下。

 這里先導入time模塊,然后time.time()是當前時間的一個時間戳,需要用localtime()格式化成time.struct_time類型的對象。(struct_time是在time模塊中定義的表示時間的對象)然后再用strftime()接收struct_time對象,返回一個字符串表示的時間值,如圖所示,我碼字的時候正是5月23號。

    def get_weather_html(self):
       #打開中國天氣網的40天天氣預報的網頁,爬取數據
       url = 'http://www.weather.com.cn/weather40d/101020100.shtml'
       driver = webdriver.Chrome()
       driver.get(url)
       driver.maximize_window()
       driver.implicitly_wait(3)
       month_input = raw_input('please input month:')
       if month_input != '' and month_input != self.month:
           self.select_month(month_input,driver)

 如果想用selenium的話,要先下個驅動下載地址我用的是谷歌瀏覽器,所以對應的驅動是cromedriver.exe,注意驅動跟版本強相關,沒有下對版本的話就會報錯。驅動放在python.exe同目錄下即可。上面代碼主要做的事情就是:

  1. 創建瀏覽器的實例driver
  2. 打開中國天氣網
  3. 將瀏覽器最大化
  4. 等待頁面加載完成
  5. 等待用戶輸入期待爬蟲的月份
  6. 如果輸入的月份不是當前月份或者不是空字符串(不輸入月份直接回車就是空字符串),則調用select_month

 這里要強調下implicitly_wait作為隱式等待可以在頁面元素找到的時間提前退出等待,但是這里設置的全局的等待時候,假設代碼里的路徑有錯或者其他什么問題,會導致調試時間變長。也可以使用time.sleep(3)這種顯式等待,設3s就是等待3s,不管元素找到與否。selenium-python里有很多selenium的詳細介紹,關於顯示等待和隱式等待的也有。

    def select_month(self, month, driver):
        driver.find_element_by_xpath('//*[@class="zong"]').click()
        month_path = '//div[@class="w_nian"]/ul[2]/li['+month+']'
        driver.find_element_by_xpath(month_path).click()

  1. 點擊打開網頁上選擇月份的彈框
  2. 創建用戶需要的月份的路徑
  3. 點擊該月份
    xpath安裝詳見左邊鏈接,親測可用。這里需要用到xpath相關的知識,w3school、菜鳥教程上有很多很詳細的這里就不贅述。讓我們動手實踐下:

    圖片文字顯示不全,1、打開開發者模式,用這個小箭頭點你要的元素。2、這里找到元素對應的位置。
for week_index in range(2,8):
    for day_index in range(1,8):
        week = str(week_index)
        day = str(day_index)
        day_path = '//*[@id="table"]/tbody/tr['+week+']/td['+day+']/h2/span[2]'
        max_temp_path = '//*[@id="table"]/tbody/tr['+week+']/td['+day+']/div[1]/p/span[1]'
        min_temp_path = '//*[@id="table"]/tbody/tr['+week+']/td['+day+']/div[1]/p/span[2]'
        date = driver.find_element_by_xpath(day_path)
        date = date.text
        try:
            date_int = int(date)
        except(ValueError):
            break
        if month_input == '':
            month = self.month
        else:
            month = month_input
        if date_int == 01 and week_index != 2:
            print u'19年{}月每日溫度情況如下'.format(month)
            break
        max_temp = driver.find_element_by_xpath(max_temp_path)
        min_temp = driver.find_element_by_xpath(min_temp_path)
        max_temp = re.findall('\d+', max_temp.text)
        min_temp = re.findall('\d+', min_temp.text)
  1. 循環獲取數據
  2. 創建日期,最高溫,最低溫的xpath路徑
  3. 獲取路徑里的文本信息
  4. 路徑獲取不到正確的信息或者匹配到下個月的1號就跳出循環
  5. 用正則把溫度中的數字取出來
     emm這里就是爬數據處理數據的主要邏輯了,因為天氣預報上40天的顯示是按日歷來的,跟周數,星期數相關,於是這里用了嵌套的循環,但是因為月份的不同,周數有變化,我這里就寫死range(2,8)(html數據從2開始到6或者7,range不包括8),有的月份沒有第6周會獲取不到元素的時候,強行int會報ValueError,等捕捉到這個錯就是數據已經全部獲取完可以break了。這里我用字典(key=value)存放日期和溫度的對應,因為字典的key是唯一的,所以即使會獲取到上個月30號的溫度,也會被覆蓋。這里還有個問題就是溫度顯示方式有多種,因此獲取溫度的xpath不是唯一的,於是我把這部分用try包起來,except捕捉到異常后用另一個xpath去獲取,直到取到數據。詳見下圖
if __name__ == '__main__':
    weather = Weather()
    dict,month = weather.get_weather_html()
    count = 0
    for key in dict.keys():
        monthRange = calendar.monthrange(2019, int(month))
        print u"{name:10d}號:{max}~{min}℃".format(name=key,max=dict[key][0],min=dict[key][1])
        count += 1
        if count == monthRange[-1]:
            break

 最后我們就可以創建類實例,運行方法,打印數據了。提醒下cmd下運行的話,中文字符前要加個u轉成Unicode,不然會報錯。下圖就是最后的運行結果啦。

 其實獲取html的方法有很多種頁面獲取方法,我一開始用了urllib和request都失敗了,因為天氣預報的頁面是動態加載的,而這兩個庫只能幫助我們獲取靜態頁面,然后我就想試試自己模擬請求,實現動態加載,無奈才疏學淺,最后只能用萬能牌selenium。selenium會模擬用戶操作,真的在你電腦上打開瀏覽器然后點點點,這樣雖然萬能,但是就很慢吶。 以上就是我代碼的大部分了,源碼的話可以去我的github獲取。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM