基于scrapy爬虫的天气数据采集(python)
基于scrapy爬蟲的天氣數(shù)據(jù)采集(python)
一、實驗介紹
1.1. 知識點
本節(jié)實驗中將學(xué)習(xí)和實踐以下知識點:
二、實驗效果
三、項目實戰(zhàn)
3.1. 安裝Scrapy
安裝 scrapy-0.24:
# 安裝依賴的包 $ sudo apt-get update $ sudo apt-get install python-lxml python-dev libffi-dev# 更新系統(tǒng)默認(rèn)的 six 包 $ sudo pip install six --upgrade# 安裝指定版本的scrapy $ sudo pip install scrapy==0.24.4完成這步后,可以用下面的命令測試一下安裝是否正確:
$ scrapy version如果正常,效果如圖所示:
3.2. 創(chuàng)建項目
在開始爬取之前,必須創(chuàng)建一個新的Scrapy項目。進入您打算存儲代碼的目錄中,運行下列命令:
$ scrapy startproject weather如果正常,效果如圖所示:
這些文件分別是:
- scrapy.cfg: 項目的配置文件
- weather/: 該項目的python模塊。之后將在此加入代碼。
- weather/items.py: 項目中的item文件.
- weather/pipelines.py: 項目中的pipelines文件.
- weather/settings.py: 項目的設(shè)置文件.
- weather/spiders/: 放置spider代碼的目錄.
3.3. 定義Item
Item 是保存爬取到的數(shù)據(jù)的容器;其使用方法和python字典類似,并且提供了額外保護機制來避免拼寫錯誤導(dǎo)致的未定義字段錯誤。
首先根據(jù)需要從weather.sina.com.cn獲取到的數(shù)據(jù)對item進行建模。 我們需要從weather.sina.com.cn中獲取當(dāng)前城市名,后續(xù)9天的日期,天氣描述和溫度等信息。對此,在item中定義相應(yīng)的字段。編輯 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() pass3.4. 編寫獲取天氣數(shù)據(jù)的爬蟲(Spider)
Spider是用戶編寫用于從單個網(wǎng)站(或者一些網(wǎng)站)爬取數(shù)據(jù)的類。
其包含了一個用于下載的初始URL,如何跟進網(wǎng)頁中的鏈接以及如何分析頁面中的內(nèi)容, 提取生成 item 的方法。
為了創(chuàng)建一個Spider,必須繼承 scrapy.Spider 類, 且定義以下三個屬性:
-
name: 用于區(qū)別Spider。該名字必須是唯一的,您不可以為不同的Spider設(shè)定相同的名字。
-
start_urls: 包含了Spider在啟動時進行爬取的url列表。因此,第一個被獲取到的頁面將是其中之一。后續(xù)的URL則從初始的URL獲取到的數(shù)據(jù)中提取。
-
parse() 是spider的一個方法。 被調(diào)用時,每個初始URL完成下載后生成的 Response 對象將會作為唯一的參數(shù)傳遞給該函數(shù)。 該方法負(fù)責(zé)解析返回的數(shù)據(jù)(response data),提取數(shù)據(jù)(生成item)以及生成需要進一步處理的URL的 Request 對象。
我們通過瀏覽器的查看源碼工具先來分析一下需要獲取的數(shù)據(jù)網(wǎng)源代碼:
<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">北風(fēng) 3~4級</p> <p class="wt_fc_c0_i_tip">無持續(xù)風(fēng)向 小于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">無持續(xù)風(fēng)向 小于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 WeatherItemclass 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()#//*:選取文檔中的所有元素。@:選擇屬性 /:從節(jié)點選取 。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后面括號的內(nèi)容為選擇器,關(guān)于xpath和css選擇器的內(nèi)容可參考官方教程:http://doc.scrapy.org/en/0.24/topics/selectors.html
3.5. 運行爬蟲,對數(shù)據(jù)進行驗證
到這里為止,我們需要驗證一下爬蟲是否能正常工作(即能否取到我們想要的數(shù)據(jù)),驗證的方法就是在命令行(重要:在項目的scrapy.cfg文件同級目錄運行命令,下同)中運行下面的代碼:
$ scrapy crawl myweather -o wea.json這行命令的意思是,運行名字為 myweather 的爬蟲(我們在上一步中定義的),然后把結(jié)果以json格式保存在wea.json文件中。命令運行結(jié)果如下:
然后,我們查看當(dāng)前目錄下的wea.json文件,正常情況下效果如下:
我們看到,wea.json中已經(jīng)有數(shù)據(jù)了,只是數(shù)據(jù)是以unicode方式編碼的。
3.6. 保存爬取到的數(shù)據(jù)
上面只是把數(shù)據(jù)保存在json文件中了,如果我們想自己保存在文件或數(shù)據(jù)庫中,如何操作呢?
這里就要用到 Item Pipeline 了,那么 Item Pipeline 是什么呢?
當(dāng)Item在Spider中被收集之后,它將會被傳遞到Item Pipeline中,一些組件會按照一定的順序執(zhí)行對Item的處理。
每個item pipeline組件(有時稱之為“Item Pipeline”)是實現(xiàn)了簡單方法的Python類。他們接收到Item并通過它執(zhí)行一些行為,同時也決定此Item是否繼續(xù)通過pipeline,或是被丟棄而不再進行處理。
item pipeline的典型應(yīng)用有:
- 清理HTML數(shù)據(jù)
- 驗證爬取的數(shù)據(jù)(檢查item包含某些字段)
- 查重(并丟棄)
- 將爬取結(jié)果保存到文件或數(shù)據(jù)庫中
每個item pipeline組件都需要調(diào)用 process_item 方法,這個方法必須返回一個 Item (或任何繼承類)對象, 或是拋出 DropItem異常,被丟棄的item將不會被之后的pipeline組件所處理。
我們這里把數(shù)據(jù)轉(zhuǎn)碼后保存在 wea.txt 文本中。
pipelines.py文件在創(chuàng)建項目時已經(jīng)自動被創(chuàng)建好了,我們在其中加上保存到文件的代碼:
# -*- 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比較基礎(chǔ)的語法,如果您感覺比較吃力,建議先去學(xué)一下python基礎(chǔ)課。
3.7. 把 ITEM_PIPELINES 添加到設(shè)置中
寫好ITEM_PIPELINES后,還有很重要的一步,就是把 ITEM_PIPELINES 添加到設(shè)置文件 settings.py 中。
ITEM_PIPELINES = {'weather.pipelines.WeatherPipeline': 1 }另外,有些網(wǎng)站對網(wǎng)絡(luò)爬蟲進行了阻止(注:本項目僅從技術(shù)角度處理此問題,個人強烈不建議您用爬蟲爬取有版權(quán)信息的數(shù)據(jù)),我們可以在設(shè)置中修改一下爬蟲的 USER_AGENT 和 Referer 信息,增加爬蟲請求的時間間隔。
整個 settings.py 文件內(nèi)容如下:
# -*- 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到現(xiàn)在為止,代碼主要部分已經(jīng)寫完了。
3.8. 運行爬蟲
在項目的scrapy.cfg同級目錄下用下面的命令運行爬蟲:
$ scrapy crawl myweather正常情況下,效果如下:
然后,在當(dāng)前目錄下會多一個 wea.txt 文件,內(nèi)容如下:
到此我們基于scrapy的天氣數(shù)據(jù)采集就完成了。
四、FAQ
4.1. 結(jié)果只出現(xiàn)城市?
scrapy內(nèi)置的html解析是基于lxml庫的,這個庫對html的解析的容錯性不是很好,通過檢查虛擬機中獲取到的網(wǎng)頁源碼,發(fā)現(xiàn)有部分標(biāo)簽是不匹配的(地區(qū)和瀏覽器不同取到的源碼可能不同),檢查結(jié)果如圖:
所以導(dǎo)致在spider中取到的日期數(shù)據(jù)(item['date'])為空,然后在pilepine代碼中做zip操作后,整個 weaitem 為空,所以最終只有城市數(shù)據(jù)了。
既然如此,我們換個html代碼解析器就可以了,這里建議用 BeautifulSoup (官網(wǎng):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安裝成功后,優(yōu)化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,數(shù)據(jù)如下:
4.2. 只取到了9天的數(shù)據(jù)?
如果是晚上運行爬蟲,當(dāng)天的白天天氣是沒有的(已經(jīng)過去了),針對這部分建議自己優(yōu)化。
五、實驗代碼
本實驗的代碼可以通過下面這個鏈接獲取:
http://git.shiyanlou.com/shiyanlou/scrapy-weather總結(jié)
以上是生活随笔為你收集整理的基于scrapy爬虫的天气数据采集(python)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PHP学习笔记1
- 下一篇: 下载煎蛋妹子图python代码[自用]