scrapy 是一个高级的网页内容抓取工具,主要用来自动化访问网 页并程序化提取其中对用户有用的内容。scrapy 构建于流行的 python 异步框架 twisted 之上,利用该框架的特点达到抓取的高效率,但 其面向用户的接口则是完全经过封装并与普通 python 代码写法并无二致的,因此不熟悉 twisted 的用户也不用担心。
安装
由于 scrapy 是一个 python package,所以先安装 virtualenv 及 pip:
install virtualenv and pip接着安装 scrapy:
install scrapy使用
scrapy 提供的各种工具能大量简化实际抓取时的代码量,同时其对抓取过程的抽象化也很 到位,方便用户对其控制的同时也提供了相当的自动化特性。
这里就用罗森的官方网站(http://www.lawson.com.cn/shops)为例,说明一下如何使用 scrapy。示例的结果是得到一份罗森在上海的所有便利店的清单。
新建 Project
首先用 scrapy 新建一个 project:
scrapy create project熟悉一下目录结构:
scrapy project tree-
items.py
定义抓取结果中单个项所需要包含的所有内容,比如便利店的地址、 分店名称等。 -
pipelines.py
定义如何对抓取到的内容进行再处理,例如输出文件、写入数据库等。 -
settings.py
是 scrapy 的设置文件,可对其行为进行调整。 -
spiders
目录下存放写好的 spider,也即是实际抓取逻辑。 -
scrapy.cfg
是整个项目的设置,主要用于部署 scrapyd 服务,本文不会涉及。
第一个 spider
scrapy 中最为重要的部分就是 spider。它包含了 分析网页与抓取网页数据的具体逻辑,也就是说对网页上任何内容的任何处理都在 spider 中实现。因此,这是 scrapy 整个框架的核心。
首先定义 Item:
items.py 这里定义了一个便利店(ConvStore
)所应包含的内容( Field ),会在 spider 中用到,用来承载其抓取下来的实际数据。
现在来看 spider:
spiders/store.py- 首先可以看到代码很短,整个
LawsonSpider
类只有二十多行,但已经能够为我们抓 取所有必需的信息。 - scrapy 提供了一些基本类(Base class)让我们去继承,
CrawlSpider
就是其中之一。代码中所定义的类变量(Class variable)都是再 scrapy 中有各自作 用的。-
name
是该 spider 的名字,scrapy 命令行工具调用 spider 时就用这个名字去找 到对应的 spider。 -
start_urls
是 spider 的入口,即是告诉它该从哪个网页开始抓取。 -
allowed_domains
限定 spider 的抓取活动只能在指定的 domain 中进行。
-
-
rules
定义了一系列规则用来匹配网页中出现的内容,并根据规则分发至不同的处理 方法中。这里定义了一个规则是向网页中所有匹配正则表达式r'list\?area_id=\d+'
的链接(如果你在浏览器打开初始页面的话会发现这些就是页面下方以上海各个区命名 的那几个链接)发出请求并将其结果交给parse_store_list
方法(Method)来处理。 -
SgmlLinkExtractor
是 scrapy 提供的连接提取器,它的用途就是,呃,提取链接。-
allow
参数是正则表达式,网页中匹配的链接会被抓取。 -
tags
指定从哪些标签抓取链接,默认['a', 'area']
(通过分析网页 这里不能包含area
,故手动指定。
-
-
parse_store_list
方法定义了如何抓取特定网页中的数据-
HtmlXPathSelector
是一个选择器,使用它能方便地定位到网页中的某个位置并抓取其中内容。
-
CrawlSpider
这个类是整个抓取逻辑的基础,他的工作流程如下:
- 若有
start_urls
,则从这些 URL 开始抓取,若没有,则执行start_requests
方 法(用户须定义),并请求该方法返回的Request
对象,并从这些请求结果中开始 抓取。 - 所有网页请求返回的
Response
默认交给parse
方法处理。-
parse
方法在CrawlSpider
的默认实现是用已定义的rules
对获得的网页内 容进行匹配并进行由Rule
所指定的进一步处理(即交给callback
参数所指定 的callable
去处理)。 - 若不指定
callback
,Rule
的默认处理是对匹配的网址发起请求,并再次交给parse
。
-
- 任何方法中返回的 Item 实例(如示例中的
ConvStore
)都会被作为有效数据保存 (输出文件等),再处理( Pipeline)。
HtmlXPathSelector
这是一个通过 XPath 对 HTML 页面进 行结构化定位和内容读取的工具。scrapy 使用它定位到网页中用户所需要的数据并进行抓 取。
HtmlXPathSelector-
HtmlXPathSelector
需要一个 Response 对象来实例化。 -
//div[@class="ShopList"]/table/tr
选择了所有包含罗森门店信息的tr
标签。 -
s.select('th/p/text()').extract()
在之前的选择基础上继续对其子标签做选择, 这里就确实得选择到了分店名。extract()
则将该标签的文本数据读取出来。 -
HtmlXPathSelector
还有正则表达式接口,后文会提到。
Field, Item 及 Item Loader
Field 仅仅是一个 dict
的 wrapper 类,因此使用方法与 dict
完全一样,在 scrapy 中它负责声明单个 Item 的字段及该字段的各种行为(如序列化方法 serializer
)。
Item 用 Field 定义了单个有效数据的具体字段,而实际中则是主要有两种方法写入 数据:
- 使用其类似
dict
的接口进行数据的写入和读取,key
为字段名。 - 使用 Item Loader。
dict
接口的用法如上所示很简单,这里说一下 Item Loader。
Item Loader 的主要作用是对抓取数据的各个字段进行特殊处理,在这里我们定义了一个 StoreLoader
类继承(Inherit)自 ItemLoader
:
-
default_output_processor
定义默认的输出处理器,这里我们对抓取的数据值进行 strip 操作。 -
branch_in
方法是对branch
字段的特殊处理,他发生在输入的时候,也就是刚抓 取到数据之后。这里的处理是为没有店
这个字的分店名补上这个字。 -
<field>_in
和<field>_out
会各对指定字段做一次处理,前者是在刚抓取到数据 时,后者是在最终输出之前,用户根据需要定义相应方法。- scrapy 有一些 built-in processor 可以直接使用,进行一些通用处理。
-
add_value
将值赋予相应字段,很好理解。 -
load_item
返回该条填充过数据的 Item。
使用 Item Loader 的好处显而易见,我们有一个统一的地方对所有数据字段进行处理,不 用将其混入抓取逻辑,使整个流程分工明确。
另一个常用的 Item Loader 是 XPathItemLoader
,显然这个版本利用了 XPath:
它将字段与 XPath 表达式关联起来,直接完成定位、读取和写入数据的操作,很方便。
加上经纬度
经纬度对于定位一个地点是很有用的,通过电子地图能够精确地定位至相关地点。我发现 罗森网站提供了这个信息,但它并未明文显示,而是需要通过其所链接到的百度地图的页 面中去抓取下来,听起来很麻烦,但实际却很简单。
Parse latitude and longitude 这里为 LawsonSpider
新增了一个方法 parse_geo
,同时改写了 parse_store_list
。
- 在
parse_store_list
的循环中我抓取每个店的tr
标签中的td/a/@rel
属性 (Attribute)(这里@rel
表示a
标签的rel
属性),若有这一属性则对这个 地图的链接发起请求,即yield req
。- 在 scrapy 中,spider 类中的方法若返回 Request 实例则 scrapy 会自动对该 Request 包含的 URL 发出请求,并将其返回的结果封装为 Response 后交给
callback
参数中指定的方法处理,若未指定callback
,则交给parse
方法处 理。
- 在 scrapy 中,spider 类中的方法若返回 Request 实例则 scrapy 会自动对该 Request 包含的 URL 发出请求,并将其返回的结果封装为 Response 后交给
-
req.meta['store'] = store
,每个 Request 有一个预定义的meta
属性(dict
),保存在其中的值在其对应的 Response 中可以再次取出:store = response.meta['store']
。 -
hxs.re(r'(\d+\.\d+),(\d+\.\d+)')
使用了HtmlXPathSelector
的正则表达式接 口直接从网页中通过正则表达式匹配抓取数据。
完整 spider 代码
items.py
没有改动,与上文中的一致。
scrapy 命令行工具
scrapy 提供了一些命令行工具 (Command line tool),之 前创建 Project 的时候用到的 startproject
就是其中之一。而除了这个之外,其他工 具也各自提供了相当有用的功能。
这里仅挑出部分来讲。
shell
Run scrapy shell
运行后会进入 Python Interpreter,在这里我们能进行各种试验,配合 Firebug 之类的工具,为程序 构建一个原型:
- 抓取各区分店列表链接,同时演示
SgmlLInkExtractor
用法:
- 抓取分店列表,
fetch
用来载入新的 URL:
- 抓取分店名称,演示
HtmlXPathSelector
用法:
这是一个相当完善的命令行界面,提供了所有必需的网页分析及抓取工具,十分适合在实 际写抓取程序前做实验。
而 shell
不仅能从命令行直接调用,还能从程序中调用直接进入以便分析程序做调试:
这样在执行到 parse_geo
时就会掉入 shell
界面,可以做进一步调试。
crawl
真正的抓取就是通过这个命令执行的:
scrapy crawl `scrapy crawl store` result 从最后的报告中可以看到这个 spider 在一分钟内抓取了该网站全部320条数据 (item_scraped_count
)。
若要输出抓取结果到一个文件,则加上参数:
若要输出抓取结果到一个文件,则加上参数:
scrapy output file这样,这篇 scrapy 使用教程的第一部分就结束了。