很多网站都提供了浏览者本地的天气信息,这些信息是如何获取到的呢,方法有很多种,大多是利用某些网站提供的天气api获取的,也有利用爬虫采集的。本文就介绍如何用Scrapy来采集天气信息(从新浪天气频道采集:http://weather.sina.com.cn/ )。

先上一张最终的效果截图:

1. 安装Scrapy

安装 scrapy-0.24:

$ sudo pip install -i http://mirrors.aliyuncs.com/pypi/simple scrapy

完成这步后,可以用下面的命令测试一下安装是否正确:

$ scrapy -v

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

2. 创建项目

在开始爬取之前,必须创建一个新的Scrapy项目。进入您打算存储代码的目录中,运行下列命令:

$ scrapy startproject weather

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

这些文件分别是:

  • scrapy.cfg: 项目的配置文件
  • weather/: 该项目的python模块。之后将在此加入代码。
  • weather/items.py: 项目中的item文件.
  • weather/pipelines.py: 项目中的pipelines文件.
  • weather/settings.py: 项目的设置文件.
  • weather/spiders/: 放置spider代码的目录.

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

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()
item['city'] = response.xpath('//*[@id="slider_ct_name"]/text()').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

5. 运行爬虫,对数据进行验证

到这里为止,我们需要验证一下爬虫是否能正常工作(即能否取到我们想要的数据),验证的方法就是在命令行(重要:在项目的scrapy.cfg文件同级目录运行命令,下同)中运行下面的代码:

$ scrapy crawl myweather -o wea.json

这行命令的意思是,运行名字为 myweather 的爬虫(我们在上一步中定义的),然后把结果以json格式保存在wea.json文件中。命令运行结果如下:

然后,我们查看当前目录下的wea.json文件,正常情况下效果如下:

我们看到,wea.json中已经有数据了,只是数据是以unicode方式编码的。

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):
self.file = open('wea.txt', 'w+') def process_item(self, item, spider):
city = item['city'][0].encode('utf-8')
self.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')
)
self.file.write(txt)
return item

代码比较简单,都是python比较基础的语法,如果您感觉比较吃力,建议先去学一下python基础课。

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

到现在为止,代码主要部分已经写完了。

8. 运行爬虫

项目的scrapy.cfg同级目录下用下面的命令运行爬虫:

$ scrapy crawl myweather

正常情况下,效果如下:

然后,在当前目录下会多一个 wea.txt 文件,内容如下:

到此我们基于scrapy的天气数据采集就完成了。

可能遇到的问题

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..tar.gz #安装
$ cd beautifulsoup4-4.3.
$ 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,数据如下:

2、 关于只取到了9天的数据问题

如果是晚上运行爬虫,当天的白天天气是没有的(已经过去了),针对这部分建议自己优化。

Scrapy使用示例的更多相关文章

  1. scrapy爬虫 快速入门

    Scrapy 1. 简介 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取 (更确切来说, 网络 ...

  2. Scrapy计划表

    第一步 Scrapy 一览:理解Scrapy是什么,他能帮到你什么 安装指南:在电脑上安装Scrapy Scrapy 教程:编写第一个Scrapy项目 示例:通过前人写好的Scrapy项目进行学习 基 ...

  3. 『Scrapy』终端调用&选择器方法

    Scrapy终端 示例,输入如下命令后shell会进入Python(或IPython)交互式界面: scrapy shell "http://www.itcast.cn/channel/te ...

  4. 0.Python 爬虫之Scrapy入门实践指南(Scrapy基础知识)

    目录 0.0.Scrapy基础 0.1.Scrapy 框架图 0.2.Scrapy主要包括了以下组件: 0.3.Scrapy简单示例如下: 0.4.Scrapy运行流程如下: 0.5.还有什么? 0. ...

  5. Spider_Man_6 の Scrapy(未完待续)

    一:自我介绍 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取 (更确切来说, 网络抓取 )所 ...

  6. Scrapy的【SitemapSpider】的【官网示例】没有name属性

    Windows 10家庭中文版,Python 3.6.4,Scrapy 1.5.0, 上午看了Scrapy的Spiders官文,并按照其中的SitemapSpider的示例练习,发现官文的示例存在问题 ...

  7. Scrapy官网程序执行示例

    Windows 10家庭中文版本,Python 3.6.4,Scrapy 1.5.0, Scrapy已经安装很久了,前面也看了不少Scrapy的资料,自己尝试使其抓取微博的数据时,居然连登录页面(首页 ...

  8. Scrapy笔记02- 完整示例

    Scrapy笔记02- 完整示例 这篇文章我们通过一个比较完整的例子来教你使用Scrapy,我选择爬取虎嗅网首页的新闻列表. 这里我们将完成如下几个步骤: 创建一个新的Scrapy工程 定义你所需要要 ...

  9. scrapy分布式浅谈+京东示例

    scrapy分布式浅谈+京东示例: 学习目标: 分布式概念与使用场景 浅谈去重 浅谈断点续爬 分布式爬虫编写流程 基于scrapy_redis的分布式爬虫(阳关院务与京东图书案例) 环境准备: 下载r ...

随机推荐

  1. Educational Codeforces Round 14 - F (codeforces 691F)

    题目链接:http://codeforces.com/problemset/problem/691/F 题目大意:给定n个数,再给m个询问,每个询问给一个p,求n个数中有多少对数的乘积≥p 数据范围: ...

  2. jQuery实现网页右下角悬浮层提示

    最近有同事提到类似网页右下角的消息悬浮提示框的制作.我之前也做过一个类似的例子,很简单.是仿QQ消息.现在感觉之前的那个例子只是说了实现原理,整体上给你的感觉还是太丑,今天为大家带来一个新的例子.是D ...

  3. ELF文件中section与segment的区别

    http://blog.csdn.net/joker0910/article/details/7655606 1. ELF中的section主要提供给Linker使用, 而segment提供给Load ...

  4. 2018.8.5 Bootstrap 使用

    Bootstrap的环境搭建 <link rel="stylesheet" type="text/css" href="css/bootstra ...

  5. PHP获取当前页面完整路径URL

    //PHP获取当前页面完整路径URL 1 <?php function getFullUrl(){ # 解决通用问题 $requestUri = ''; if (isset($_SERVER[' ...

  6. Visual Studio 2013 ReportViewer Control

    最近需要给学生讲报表,.NET的自然就是选择RDLC了. 因为学生比赛是用Visual Studio 2013,所以我在自己的笔记本上安装了Visual Studio 2013(平常是用2010),安 ...

  7. C语言中%p,%u,%lu都有什么用处

    %p表示输出这个指针, %d表示后面的输出类型为有符号的10进制整形, %u表示无符号10进制整型, %lu表示输出无符号长整型整数 (long unsigned)

  8. git中.gitignore 文件

    现在项目的根目录放了 .gitignore 文件,并且git远程仓库的项目根目录已经有了 logs文件夹. 由于每次本地运行项目,都会生成新的log文件,但是我并不想提交logs文件夹里面的内容,所以 ...

  9. C#操作Word,写数据,插入图片

    本篇介绍的是如何在C#中往word里面写入数据. 如何在线的操作文档:  c#在线操作文档 关于Aspose.Word控件的介绍,请戳→ 介绍 首先需要去下载这个dll文件,然后引用到你的项目当中.地 ...

  10. SpringBoot学习13:springboot异常处理方式3(使用@ControllerAdvice+@ExceptionHandle注解)

    问题:使用@ExceptionHandle注解需要在每一个controller代码里面都添加异常处理,会咋成代码冗余 解决方法:新建一个全局异常处理类,添加@ControllerAdvice注解即可 ...