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


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

一、實驗介紹

1.1. 知識點

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

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

二、實驗效果

enter image description here

三、項目實戰

3.1. 安裝Scrapy

安裝 scrapy-0.24:

# 安裝依賴的包 $ 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 

完成這步后,可以用下面的命令測試一下安裝是否正確:

$ scrapy version

如果正常,效果如圖所示:

enter image description here

3.2. 創建項目

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

$ scrapy startproject weather

如果正常,效果如圖所示:

enter image description here

這些文件分別是:

  • 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 文件:

# -*- 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是用戶編寫用於從單個網站(或者一些網站)爬取數據的類。

其包含了一個用於下載的初始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的天氣數據采集就完成了。

四、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天的數據?

如果是晚上運行爬蟲,當天的白天天氣是沒有的(已經過去了),針對這部分建議自己優化。

五、實驗代碼

本實驗的代碼可以通過下面這個鏈接獲取:

http://git.shiyanlou.com/shiyanlou/scrapy-weather


免責聲明!

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



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