基於scrapy爬蟲的天氣數據採集(python)

基於scrapy爬蟲的天氣數據採集(python)

1、實驗介紹

1.1. 知識點

本節實驗中將學習和實踐如下知識點:css

  1. Python基本語法
  2. Scrapy框架
  3. 爬蟲的概念

2、實驗效果

enter image description here

3、項目實戰

3.1. 安裝Scrapy

安裝 scrapy-0.24:html

# 安裝依賴的包 $ sudo apt-get update $ sudo apt-get install python-lxml python-dev libffi-dev # 更新系統默認的 six 包 $ sudo pip install six --upgrade # 安裝指定版本的scrapy $ sudo pip install scrapy==0.24.4 

完成這步後,能夠用下面的命令測試一下安裝是否正確:python

$ scrapy version

若是正常,效果如圖所示:git

enter image description here

3.2. 建立項目

在開始爬取以前,必須建立一個新的Scrapy項目。進入您打算存儲代碼的目錄中,運行下列命令:web

$ scrapy startproject weather

若是正常,效果如圖所示:數據庫

enter image description here

這些文件分別是:json

  • scrapy.cfg: 項目的配置文件
  • weather/: 該項目的python模塊。以後將在此加入代碼。
  • weather/items.py: 項目中的item文件.
  • weather/pipelines.py: 項目中的pipelines文件.
  • weather/settings.py: 項目的設置文件.
  • weather/spiders/: 放置spider代碼的目錄.

3.3. 定義Item

Item 是保存爬取到的數據的容器;其使用方法和python字典相似,而且提供了額外保護機制來避免拼寫錯誤致使的未定義字段錯誤。瀏覽器

首先根據須要從weather.sina.com.cn獲取到的數據對item進行建模。 咱們須要從weather.sina.com.cn中獲取當前城市名,後續9天的日期,天氣描述和溫度等信息。對此,在item中定義相應的字段。編輯 weather 目錄中的 items.py 文件:ruby

# -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # http://doc.scrapy.org/en/latest/topics/items.html import scrapy class WeatherItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() # demo 1 city = scrapy.Field() date = scrapy.Field() dayDesc = scrapy.Field() dayTemp = scrapy.Field() pass 

3.4. 編寫獲取天氣數據的爬蟲(Spider)

Spider是用戶編寫用於從單個網站(或者一些網站)爬取數據的類。bash

其包含了一個用於下載的初始URL如何跟進網頁中的連接以及如何分析頁面中的內容提取生成 item 的方法

爲了建立一個Spider,必須繼承 scrapy.Spider 類, 且定義如下三個屬性:

  • name: 用於區別Spider。該名字必須是惟一的,您不能夠爲不一樣的Spider設定相同的名字。

  • start_urls: 包含了Spider在啓動時進行爬取的url列表。所以,第一個被獲取到的頁面將是其中之一。後續的URL則從初始的URL獲取到的數據中提取

  • parse() 是spider的一個方法。 被調用時,每一個初始URL完成下載後生成的 Response 對象將會做爲惟一的參數傳遞給該函數。 該方法負責解析返回的數據(response data),提取數據(生成item)以及生成須要進一步處理的URL的 Request 對象

咱們經過瀏覽器的查看源碼工具先來分析一下須要獲取的數據網源代碼:

<h4 class="slider_ct_name" id="slider_ct_name">武漢</h4> ... <div class="blk_fc_c0_scroll" id="blk_fc_c0_scroll" style="width: 1700px;"> <div class="blk_fc_c0_i"> <p class="wt_fc_c0_i_date">01-28</p> <p class="wt_fc_c0_i_day wt_fc_c0_i_today">今天</p> <p class="wt_fc_c0_i_icons clearfix"> <img class="icons0_wt png24" src="http://www.sinaimg.cn/dy/weather/main/index14/007/icons_42_yl/w_04_27_00.png" alt="雨夾雪" title="雨夾雪"> <img class="icons0_wt png24" src="http://www.sinaimg.cn/dy/weather/main/index14/007/icons_42_yl/w_04_29_01.png" alt="中雪" title="中雪"> </p> <p class="wt_fc_c0_i_times"> <span class="wt_fc_c0_i_time">白天</span> <span class="wt_fc_c0_i_time">夜間</span> </p> <p class="wt_fc_c0_i_temp">1°C / -2°C</p> <p class="wt_fc_c0_i_tip">北風 3~4級</p> <p class="wt_fc_c0_i_tip">無持續風向 小於3級</p> </div> <div class="blk_fc_c0_i"> <p class="wt_fc_c0_i_date">01-29</p> <p class="wt_fc_c0_i_day ">星期四</p> <p class="wt_fc_c0_i_icons clearfix"> <img class="icons0_wt png24" src="http://www.sinaimg.cn/dy/weather/main/index14/007/icons_42_yl/w_04_29_00.png" alt="中雪" title="中雪"> <img class="icons0_wt png24" src="http://www.sinaimg.cn/dy/weather/main/index14/007/icons_42_yl/w_07_25_01.png" alt="陰" title="陰"> </p> <p class="wt_fc_c0_i_times"> <span class="wt_fc_c0_i_time">白天</span> <span class="wt_fc_c0_i_time">夜間</span> </p> <p class="wt_fc_c0_i_temp">1°C / -2°C</p> <p class="wt_fc_c0_i_tip">無持續風向 小於3級</p> </div> ... </div> 

咱們能夠看到:

  • 城市名能夠經過獲取id爲slider_ct_name的h4元素獲取
  • 日期能夠經過獲取id爲blk_fc_c0_scroll下的class爲wt_fc_c0_i_date的p元素獲取
  • 天氣描述能夠經過獲取id爲blk_fc_c0_scroll下的class爲icons0_wt的img元素獲取
  • 溫度能夠經過獲取id爲blk_fc_c0_scroll下的class爲wt_fc_c0_i_temp的p元素獲取

所以,咱們的Spider代碼以下,保存在 weather/spiders 目錄下的 localweather.py 文件中:

# -*- coding: utf-8 -*- import scrapy from weather.items import WeatherItem class WeatherSpider(scrapy.Spider): name = "myweather" allowed_domains = ["sina.com.cn"] start_urls = ['http://weather.sina.com.cn'] def parse(self, response): item = WeatherItem() #把WeatheItem()實例化成item對象 item['city'] = response.xpath('//*[@id="slider_ct_name"]/text()').extract()#//*:選取文檔中的全部元素。@:選擇屬性 /:從節點選取 。extract():提取 tenDay = response.xpath('//*[@id="blk_fc_c0_scroll"]'); item['date'] = tenDay.css('p.wt_fc_c0_i_date::text').extract() item['dayDesc'] = tenDay.css('img.icons0_wt::attr(title)').extract() item['dayTemp'] = tenDay.css('p.wt_fc_c0_i_temp::text').extract() return item 

代碼中的xpath和css後面括號的內容爲選擇器,關於xpath和css選擇器的內容可參考官方教程:http://doc.scrapy.org/en/0.24/topics/selectors.html

3.5. 運行爬蟲,對數據進行驗證

到這裏爲止,咱們須要驗證一下爬蟲是否能正常工做(即可否取到咱們想要的數據),驗證的方法就是在命令行(重要:在項目的scrapy.cfg文件同級目錄運行命令,下同)中運行下面的代碼:

$ scrapy crawl myweather -o wea.json

這行命令的意思是,運行名字爲 myweather 的爬蟲(咱們在上一步中定義的),而後把結果以json格式保存在wea.json文件中。命令運行結果以下:

enter image description here

而後,咱們查看當前目錄下的wea.json文件,正常狀況下效果以下:

enter image description here

咱們看到,wea.json中已經有數據了,只是數據是以unicode方式編碼的

3.6. 保存爬取到的數據

上面只是把數據保存在json文件中了,若是咱們想本身保存在文件或數據庫中,如何操做呢?

這裏就要用到 Item Pipeline 了,那麼 Item Pipeline 是什麼呢?

當Item在Spider中被收集以後,它將會被傳遞到Item Pipeline中,一些組件會按照必定的順序執行對Item的處理

每一個item pipeline組件(有時稱之爲「Item Pipeline」)是實現了簡單方法的Python類。他們接收到Item並經過它執行一些行爲,同時也決定此Item是否繼續經過pipeline,或是被丟棄而再也不進行處理。

item pipeline的典型應用有:

  • 清理HTML數據
  • 驗證爬取的數據(檢查item包含某些字段)
  • 查重(並丟棄)
  • 將爬取結果保存到文件或數據庫中

每一個item pipeline組件都須要調用 process_item 方法,這個方法必須返回一個 Item (或任何繼承類)對象, 或是拋出 DropItem異常,被丟棄的item將不會被以後的pipeline組件所處理。

咱們這裏把數據轉碼後保存在 wea.txt 文本中。

pipelines.py文件在建立項目時已經自動被建立好了,咱們在其中加上保存到文件的代碼:

# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html class WeatherPipeline(object): def __init__(self): pass def process_item(self, item, spider): with open('wea.txt', 'w+') as file: city = item['city'][0].encode('utf-8') file.write('city:' + str(city) + '\n\n') date = item['date'] desc = item['dayDesc'] dayDesc = desc[1::2] nightDesc = desc[0::2] dayTemp = item['dayTemp'] weaitem = zip(date, dayDesc, nightDesc, dayTemp) for i in range(len(weaitem)): item = weaitem[i] d = item[0] dd = item[1] nd = item[2] ta = item[3].split('/') dt = ta[0] nt = ta[1] txt = 'date:{0}\t\tday:{1}({2})\t\tnight:{3}({4})\n\n'.format( d, dd.encode('utf-8'), dt.encode('utf-8'), nd.encode('utf-8'), nt.encode('utf-8') ) file.write(txt) return item 

代碼比較簡單,都是python比較基礎的語法,若是您感受比較吃力,建議先去學一下python基礎課。

3.7. 把 ITEM_PIPELINES 添加到設置中

寫好ITEM_PIPELINES後,還有很重要的一步,就是把 ITEM_PIPELINES 添加到設置文件 settings.py 中。

ITEM_PIPELINES = { 'weather.pipelines.WeatherPipeline': 1 } 

另外,有些網站對網絡爬蟲進行了阻止(注:本項目僅從技術角度處理此問題,我的強烈不建議您用爬蟲爬取有版權信息的數據),咱們能夠在設置中修改一下爬蟲的 USER_AGENT 和 Referer 信息,增長爬蟲請求的時間間隔。

整個 settings.py 文件內容以下:

# -*- coding: utf-8 -*- # Scrapy settings for weather project # # For simplicity, this file contains only the most important settings by # default. All the other settings are documented here: # # http://doc.scrapy.org/en/latest/topics/settings.html # BOT_NAME = 'Googlebot' SPIDER_MODULES = ['weather.spiders'] NEWSPIDER_MODULE = 'weather.spiders' # Crawl responsibly by identifying yourself (and your website) on the user-agent #USER_AGENT = 'weather (+http://www.yourdomain.com)' USER_AGENT = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36' DEFAULT_REQUEST_HEADERS = { 'Referer': 'http://www.weibo.com' } ITEM_PIPELINES = { 'weather.pipelines.WeatherPipeline': 1 } DOWNLOAD_DELAY = 0.5 

到如今爲止,代碼主要部分已經寫完了。

3.8. 運行爬蟲

在項目的scrapy.cfg同級目錄下用下面的命令運行爬蟲:

$ scrapy crawl myweather

正常狀況下,效果以下:

enter image description here

而後,在當前目錄下會多一個 wea.txt 文件,內容以下:

enter image description here

到此咱們基於scrapy的天氣數據採集就完成了。

4、FAQ

4.1. 結果只出現城市?

scrapy內置的html解析是基於lxml庫的,這個庫對html的解析的容錯性不是很好,經過檢查虛擬機中獲取到的網頁源碼,發現有部分標籤是不匹配的(地區和瀏覽器不一樣取到的源碼可能不一樣),檢查結果如圖:

圖片描述信息

因此致使在spider中取到的日期數據(item['date'])爲空,而後在pilepine代碼中作zip操做後,整個 weaitem 爲空,因此最終只有城市數據了。

既然如此,咱們換個html代碼解析器就能夠了,這裏建議用 BeautifulSoup (官網:http://www.crummy.com/software/BeautifulSoup/bs4/doc/index.html ),這個解析器有比較好的容錯能力,具體用法能夠參考上面的文檔。

BeautifulSoup安裝:

#下載BeautifulSoup $ wget http://labfile.oss.aliyuncs.com/beautifulsoup4-4.3.2.tar.gz #解壓 $ tar -zxvf beautifulsoup4-4.3.2.tar.gz #安裝 $ cd beautifulsoup4-4.3.2 $ sudo python setup.py install 

安裝成功後,優化WeatherSpider代碼,改進後的代碼以下:

# -*- coding: utf-8 -*- import scrapy from bs4 import BeautifulSoup from weather.items import WeatherItem class WeatherSpider(scrapy.Spider): name = "myweather" allowed_domains = ["sina.com.cn"] start_urls = ['http://weather.sina.com.cn'] def parse(self, response): html_doc = response.body #html_doc = html_doc.decode('utf-8') soup = BeautifulSoup(html_doc) itemTemp = {} itemTemp['city'] = soup.find(id='slider_ct_name') tenDay = soup.find(id='blk_fc_c0_scroll') itemTemp['date'] = tenDay.findAll("p", {"class": 'wt_fc_c0_i_date'}) itemTemp['dayDesc'] = tenDay.findAll("img", {"class": 'icons0_wt'}) itemTemp['dayTemp'] = tenDay.findAll('p', {"class": 'wt_fc_c0_i_temp'}) item = WeatherItem() for att in itemTemp: item[att] = [] if att == 'city': item[att] = itemTemp.get(att).text continue for obj in itemTemp.get(att): if att == 'dayDesc': item[att].append(obj['title']) else: item[att].append(obj.text) return item 

而後再次運行爬蟲:

$ scrapy crawl myweather

而後查看 wea.txt,數據以下:

圖片描述信息

4.2. 只取到了9天的數據?

若是是晚上運行爬蟲,當天的白每天氣是沒有的(已通過去了),針對這部分建議本身優化。

5、實驗代碼

本實驗的代碼能夠經過下面這個連接獲取:

http://git.shiyanlou.com/shiyanlou/scrapy-weather
相關文章
相關標籤/搜索