很多网站都提供了浏览者本地的天气信息,这些信息是如何获取到的呢,方法有很多种,大多是利用某些网站提供的天气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. 随便讲讲自己了解的ajax在JQ中的应用

    首先jQuery 库拥有完整的 Ajax 兼容套件.其中的函数和方法允许我们在不刷新浏览器的情况下从服务器加载数据. 定义和用法 ajax() 方法通过 HTTP 请求加载远程数据. 该方法是 jQu ...

  2. BZOJ 3227: [Sdoi2008]红黑树(tree)

    BZOJ 3227: [Sdoi2008]红黑树(tree) 标签(空格分隔): OI-BZOJ OI-其它 Time Limit: 10 Sec Memory Limit: 128 MB Descr ...

  3. vector的几种初始化和遍历

    随着C++11标准的出现,vector出现了新的初始化和遍历用法,但是vs2010和较高版本并没有能完全支持C++11标准,所以我就将它的所有的用法归纳了一下. vector的初始化 vector基本 ...

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

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

  5. jQuery实现轮播切换以及将其封装成插件(2)

    在上一篇博文中,我们完成了一个简单的轮播切换.它的功能比较简单,仅仅能定时切换图片. 但是有没有这样一种情况.当我们特意的想看某一个轮播显示项时不希望等轮播一圈才能等到,希望通过图上的一些元素,触发某 ...

  6. Laravel 5 : Call to undefined function Illuminate\Foundation\Bootstrap\mb_internal_encoding()

    自己组装的apache,php,mysql phpinfo显示 OpenSSL support     disabled (install ext/openssl) 判断为权限问题,所以修改如下. 1 ...

  7. 第45章 DCMI—OV2640摄像头—零死角玩转STM32-F429系列

    第45章     DCMI—OV2640摄像头 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com ...

  8. 统计文件中的URL

    1. 题目: 一个文本文件中每一行中有一个URL,最多一万行,统计每一个URL的次数,输出到另外一个文件中,每一行前面是URL,后面是个数. 2.代码: package test; import ja ...

  9. esdoc 自动生成接口文档介绍

    原文地址:https://www.xingkongbj.com/blog/esdoc/creat-esdoc.html 官网 ESDoc:https://esdoc.org/ JSDoc:http:/ ...

  10. Easyui下的点击回车键跳转到下个控件

    在Easyui框架下,JavaScript 中的 onKeyDown事件居然失效了.所以使用了另外的函数去实现点击回车键跳转到下个控件. /** * 点击回车键跳转到下个控件; * @param ol ...