本节实验中将学习和实践如下知识点:css
安装 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
在开始爬取以前,必须建立一个新的Scrapy项目。进入您打算存储代码的目录中,运行下列命令:web
$ scrapy startproject weather
若是正常,效果如图所示:数据库
这些文件分别是:json
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
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>
咱们能够看到:
所以,咱们的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
到这里为止,咱们须要验证一下爬虫是否能正常工做(即可否取到咱们想要的数据),验证的方法就是在命令行(重要:在项目的scrapy.cfg文件同级目录运行命令,下同)中运行下面的代码:
$ scrapy crawl myweather -o wea.json
这行命令的意思是,运行名字为 myweather 的爬虫(咱们在上一步中定义的),而后把结果以json格式保存在wea.json文件中。命令运行结果以下:
而后,咱们查看当前目录下的wea.json文件,正常状况下效果以下:
咱们看到,wea.json中已经有数据了,只是数据是以unicode方式编码的。
上面只是把数据保存在json文件中了,若是咱们想本身保存在文件或数据库中,如何操做呢?
这里就要用到 Item Pipeline 了,那么 Item Pipeline 是什么呢?
当Item在Spider中被收集以后,它将会被传递到Item Pipeline中,一些组件会按照必定的顺序执行对Item的处理。
每一个item pipeline组件(有时称之为“Item Pipeline”)是实现了简单方法的Python类。他们接收到Item并经过它执行一些行为,同时也决定此Item是否继续经过pipeline,或是被丢弃而再也不进行处理。
item pipeline的典型应用有:
每一个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基础课。
写好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
到如今为止,代码主要部分已经写完了。
在项目的scrapy.cfg同级目录下用下面的命令运行爬虫:
$ scrapy crawl myweather
正常状况下,效果以下:
而后,在当前目录下会多一个 wea.txt 文件,内容以下:
到此咱们基于scrapy的天气数据采集就完成了。
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,数据以下:
若是是晚上运行爬虫,当天的白每天气是没有的(已通过去了),针对这部分建议本身优化。
本实验的代码能够经过下面这个连接获取:
http://git.shiyanlou.com/shiyanlou/scrapy-weather