入门教程

学习目标

完成下列任务:

  • 创建一个Scrapy项目
  • 定义提取的结构化数据(Item)
  • 编写爬取网站的 spider 并提取出结构化数据(Item)
  • 编写 Item Pipeline 来存储提取到的Item(即结构化数据)

创建项目

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

  1. scrapy startproject tutorial

运行过程:

入门教程 - 图1

该命令将会创建包含下列内容的 tutorial 目录:

这些文件分别是:

  1. scrapy.cfg: 项目的配置文件;(用于发布到服务器)
  2. tutorial/: 该项目文件夹。之后将在此编写Python代码。
  3. tutorial/items.py: 项目中的item文件;(定义结构化数据字段field).
  4. tutorial/pipelines.py: 项目中的pipelines文件;(用于存放执行后期数据处理的功能,定义如何存储结构化数据)
  5. tutorial/settings.py: 项目的设置文件;(如何修改User-Agent,设置爬取时间间隔,设置代理,配置中间件等等)
  6. tutorial/spiders/: 放置spider代码的目录;(编写爬取网站规则)

定义Item

Item 定义结构化数据字段,用来保存爬取到的数据;其使用方法和python字典类似

可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field的类属性来定义一个Item。

首先根据需要从腾讯招聘获取到的数据对item进行建模。 我们需要从腾讯招聘中获取 职位名称、职位详情页url、职位类别、人数、工作地点以及发布时间。 对此,在item中定义相应的字段。编辑 tutorial 目录中的 items.py 文件:

  1. import scrapy
  2. class RecruitItem(scrapy.Item):
  3. name = scrapy.Field()
  4. detailLink = scrapy.Field()
  5. catalog = scrapy.Field()
  6. recruitNumber = scrapy.Field()
  7. workLocation = scrapy.Field()
  8. publishTime = scrapy.Field()

编写第一个爬虫(Spider)

Spider是开发者编写用于从单个网站(或者一些网站)爬取数据的类。

创建一个Spider,必须继承 'scrapy.Spider' 类, 需要定义以下三个属性:

  • name:

    spider名字;必须是唯一的

  • start_urls:

    初始的URL列表

  • parse(self, response):

    每个初始URL完成下载后被调用

    这个函数要完成的功能:

  1. 1.负责解析返回的网页数据(response.body),提取结构化数据(生成item)
  2. 2.生成需要下一页的请求URL

以下为我们的第一个Spider代码,保存在 tutorial/spiders 目录下的 tencent_spider.py 文件中:

  1. import scrapy
  2. class RecruitSpider(scrapy.spiders.Spider):
  3. name = "tencent"
  4. allowed_domains = ["hr.tencent.com"]
  5. start_urls = [
  6. "http://hr.tencent.com/position.php?&start=0#a"
  7. ]
  8. def parse(self, response):
  9. f = open('tengxun.txt', 'wb')
  10. f.write(response.body)
  11. f.close()

爬取

进入项目的根目录,执行下列命令启动spider:

  1. scrapy crawl tencent

crawl tencent 启动用于爬取 tencent 的spider,您将得到类似的输出:

入门教程 - 图2

现在,查看当前目录,会注意到有文件被创建了: tengxun.txt,正如我们的 parse 方法里做的一样。

注意,在刚启动的时候会有一段error信息,不用理会

在第六天作业里面有说明原因

  1. 2016-08-11 13:07:35 [boto] ERROR: Caught exception reading instance data
  2. Traceback (most recent call last):
  3. File "/usr/lib/python2.7/dist-packages/boto/utils.py", line 210, in retry_url
  4. r = opener.open(req, timeout=timeout)
  5. File "/usr/lib/python2.7/urllib2.py", line 429, in open
  6. response = self._open(req, data)
  7. File "/usr/lib/python2.7/urllib2.py", line 447, in _open
  8. '_open', req)
  9. File "/usr/lib/python2.7/urllib2.py", line 407, in _call_chain
  10. result = func(*args)
  11. File "/usr/lib/python2.7/urllib2.py", line 1228, in http_open
  12. return self.do_open(httplib.HTTPConnection, req)
  13. File "/usr/lib/python2.7/urllib2.py", line 1198, in do_open
  14. raise URLError(err)
  15. URLError: <urlopen error timed out>

刚才发生了什么?

Scrapy为Spider的 start_urls 属性中的每个URL创建了 scrapy.Request 对象,并将 parse 方法作为回调函数(callback)赋值给了Request。

Request对象经过调度,执行生成 scrapy.http.Response 对象并送回给parse() 方法。

提取Item

Selectors选择器简介

Scrapy Selectors 内置XPathCSS Selector 表达式机制

XPath表达式的例子及对应的含义:

  1. /html/head/title: 选择<HTML>文档中 <head> 标签内的 <title> 元素
  2. /html/head/title/text(): 选择上面提到的 <title> 元素的文字
  3. //td: 选择所有的 <td> 元素
  4. //div[@class="mine"]: 选择所有具有 class="mine" 属性的 div 元素

Selector有四个基本的方法:

  1. xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
  2. css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表.
  3. extract(): 序列化该节点为unicode字符串并返回list
  4. re(): 根据传入的正则表达式对数据进行提取,返回unicode字符串list列表。

尝试Selector选择器

为了介绍Selector的使用方法,接下来我们将要使用内置的 scrapy shell 。Scrapy Shell需要您预装好IPython(一个扩展的Python终端)。

您需要进入项目的根目录,执行下列命令来启动shell:

  1. scrapy shell "http://hr.tencent.com/position.php?&start=0#a"

注解:当您在终端运行Scrapy时,请一定记得给url地址加上引号,否则包含参数的url(例如 & 字符)会导致Scrapy运行失败。

shell的输出类似:

入门教程 - 图3

当shell载入后,将得到一个包含response数据的本地 response 变量。输入 response.body将输出response的包体, 输出 response.headers 可以看到response的包头。

  • 当输入 response.selector 时, 将获取到一个response 初始化的类 Selector 的对象
  • 此时,可以通过使用 response.selector.xpath() 或 response.selector.css() 来对 response 进行查询。
  • 或者,scrapy也对 response.selector.xpath() 及 response.selector.css() 提供了一些快捷方式, 例如 response.xpath() 或 response.css()

让我们来试试:

  1. response.xpath('//title')
  2. [<Selector xpath='//title' data=u'<title>\u804c\u4f4d\u641c\u7d22 | \u793e\u4f1a\u62db\u8058 | Tencent \u817e\u8baf\u62db\u8058</title'>]
  3. response.xpath('//title').extract()
  4. [u'<title>\u804c\u4f4d\u641c\u7d22 | \u793e\u4f1a\u62db\u8058 | Tencent \u817e\u8baf\u62db\u8058</title>']
  5. print response.xpath('//title').extract()[0]
  6. <title>职位搜索 | 社会招聘 | Tencent 腾讯招聘</title>
  7. response.xpath('//title/text()')
  8. <Selector xpath='//title/text()' data=u'\u804c\u4f4d\u641c\u7d22 | \u793e\u4f1a\u62db\u8058 | Tencent \u817e\u8baf\u62db\u8058'>
  9. response.xpath('//title/text()')[0].extract()
  10. u'\u804c\u4f4d\u641c\u7d22 | \u793e\u4f1a\u62db\u8058 | Tencent \u817e\u8baf\u62db\u8058'
  11. print response.xpath('//title/text()')[0].extract()
  12. 职位搜索 | 社会招聘 | Tencent 腾讯招聘
  13. response.xpath('//title/text()').re('(\w+):')
  14. [u'\u804c\u4f4d\u641c\u7d22',
  15. u'\u793e\u4f1a\u62db\u8058',
  16. u'Tencent',
  17. u'\u817e\u8baf\u62db\u8058']

提取数据

现在,我们来尝试从这些页面中提取些有用的数据。

我们可以通过XPath选择该页面中网站列表里所有 lass=even 元素:

  1. site = response.xpath('//*[@class="even"]')

职位名称:

  1. print site[0].xpath('./td[1]/a/text()').extract()[0]
  2. TEG15-运营开发工程师(深圳)

职位名称详情页:

  1. print site[0].xpath('./td[1]/a/@href').extract()[0]
  2. position_detail.php?id=20744&keywords=&tid=0&lid=0

职位类别:

  1. print site[0].xpath('./td[2]/text()').extract()[0]
  2. 技术类

对于 .xpath() 调用返回selector组成的list,因此可以拼接更多的 .xpath() 来进一步获取某个节点。

  1. for sel in response.xpath('//*[@class="even"]'):
  2. name = sel.xpath('./td[1]/a/text()').extract()[0]
  3. detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
  4. catalog = sel.xpath('./td[2]/text()').extract()[0]
  5. recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
  6. workLocation = sel.xpath('./td[4]/text()').extract()[0]
  7. publishTime = sel.xpath('./td[5]/text()').extract()[0]
  8. print name, detailLink, catalog,recruitNumber,workLocation,publishTime

在我们的tencent_spider.py文件修改成如下代码:

  1. import scrapy
  2. class RecruitSpider(scrapy.spiders.Spider):
  3. name = "tencent"
  4. allowed_domains = ["hr.tencent.com"]
  5. start_urls = [
  6. "http://hr.tencent.com/position.php?&start=0#a"
  7. ]
  8. def parse(self, response):
  9. for sel in response.xpath('//*[@class="even"]'):
  10. name = sel.xpath('./td[1]/a/text()').extract()[0]
  11. detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
  12. catalog = sel.xpath('./td[2]/text()').extract()[0]
  13. recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
  14. workLocation = sel.xpath('./td[4]/text()').extract()[0]
  15. publishTime = sel.xpath('./td[5]/text()').extract()[0]
  16. print name, detailLink, catalog,recruitNumber,workLocation,publishTime

如图所示:

入门教程 - 图4

现在尝试再次爬取hr.tencent.com,您将看到爬取到的网站信息被成功输出:

  1. scrapy crawl tencent

运行过程:

入门教程 - 图5

使用item

Item 对象是自定义的python字典。可以使用标准的字典语法来获取到其每个字段的值。

输入 `scrapy shell'

  1. import scrapy
  2. class RecruitItem(scrapy.Item):
  3. name = scrapy.Field()
  4. detailLink = scrapy.Field()
  5. catalog = scrapy.Field()
  6. recruitNumber = scrapy.Field()
  7. workLocation = scrapy.Field()
  8. publishTime = scrapy.Field()
  9. item = RecruitItem()
  10. item['name'] = 'sanlang'
  11. item['name']
  12. 'sanlang'

一般来说,Spider将会将爬取到的数据以Item对象返回。所以为了将爬取的数据返回,最终tencent_spider.py代码将是:

  1. import scrapy
  2. from tutorial.items import RecruitItem
  3. class RecruitSpider(scrapy.spiders.Spider):
  4. name = "tencent"
  5. allowed_domains = ["hr.tencent.com"]
  6. start_urls = [
  7. "http://hr.tencent.com/position.php?&start=0#a"
  8. ]
  9. def parse(self, response):
  10. for sel in response.xpath('//*[@class="even"]'):
  11. name = sel.xpath('./td[1]/a/text()').extract()[0]
  12. detailLink = sel.xpath('./td[1]/a/@href').extract()[0]
  13. catalog = sel.xpath('./td[2]/text()').extract()[0]
  14. recruitNumber = sel.xpath('./td[3]/text()').extract()[0]
  15. workLocation = sel.xpath('./td[4]/text()').extract()[0]
  16. publishTime = sel.xpath('./td[5]/text()').extract()[0]
  17. print name, detailLink, catalog,recruitNumber,workLocation,publishTime
  18. item = RecruitItem()
  19. item['name']=name.encode('utf-8')
  20. item['detailLink']=detailLink.encode('utf-8')
  21. item['catalog']=catalog.encode('utf-8')
  22. item['recruitNumber']=recruitNumber.encode('utf-8')
  23. item['workLocation']=workLocation.encode('utf-8')
  24. item['publishTime']=publishTime.encode('utf-8')
  25. yield item

现在对hr.tencent.com进行爬取将会产生 RecruitItem 对象:

运行过程:

入门教程 - 图6

保存爬取到的数据

最简单存储爬取的数据的方式是使用 Feed exports:

  1. scrapy crawl tencent -o items.json

该命令将采用 JSON 格式对爬取的数据进行序列化,生成 items.json 文件。

如果需要对爬取到的item做更多更为复杂的操作,您可以编写 Item Pipeline 。 类似于我们在创建项目时对Item做的,用于您编写自己的 tutorial/pipelines.py 也被创建。 不过如果您仅仅想要保存item,您不需要实现任何的pipeline。