Scrapy向导

阅读该向导前,我们假定你已经在系统上安装了Scrapy。如果还没有的话,请先阅读安装向导。

该向导会带你过一遍以下任务:

1.创建一个新的Scrapy项目

2.编写一个用来爬取网站和提取数据的爬虫程序

3.使用命令行导出所爬取的数据

4.使爬虫递归跟踪链接

5.使用爬虫参数

Scrapy是用Python编写的。如果你对这门语言还不熟,可以先去了解下,这样才能更好地使用Scrapy。

如果你已经熟悉某些编程语言,又想快点学习Python,我们建议你阅读《深入Python 3》或《Python向导》(译注:蓝色表示有超链接,可在原文中直接点击阅读,下同)。

如果你不懂编程,但想学习Python的话,这本《硬方法学Python》在线书籍很有用。你也可以看下这些给非编程人员的Python资源。

创建项目

在开始爬取前,你需要先创建一个新的Scrapy项目。进入你想存放代码的一个目录,然后运行以下命令:

scrapy startproject tutorial

之后会出现一个tutorial文件夹,里面有这些文件:

我们的第一个爬虫

爬虫是你定义的,且被Scrapy用来从一个(或多个)网站爬取信息的类。该类必须继承scrapy.Spider,并且指明首次需要提交的请求,如何跟踪页面上的链接(可选),以及如何解析下载来的页面以提取数据。

下面是我们第一个爬虫的代码。将它保存在一个文件中并命名为quotes_spider.py,然后将其放在tutorial/spiders目录下:

如你所见,我们的爬虫继承了scrapy.Spider并定义了一些属性和方法:

·name:标记爬虫。在一个项目中,该属性必须唯一,也就是说每个爬虫的名称必须不同。

·start_requests():必须返回一个可迭代的请求对象(你可以返回一个请求列表或者生成器函数),而爬虫会根据这些请求来开始爬取。随后的请求会从第一批请求中陆续生成。

·parse():该方法用于处理被下载过来的响应(响应与每个被递交的请求相对应)。响应参数为TextResponse实例(包含页面内容),之后有许多有用的方法可用于处理该实例。

parse()方法通常用于解析响应,将爬取到的数据提取到字典中,寻找新的网址并创建相应的新请求。

如何运行我们的爬虫

要运行我们的爬虫,需要进入项目的顶层目录中,并运行以下命令:

scrapy crawl quotes

现在看一下当前目录,你会发现生成了两个新的文件:quotes-1.html和quotes-2.html,其中包含相应网址的内容,就跟我们的parse方法所规定的那样。

注意:

如果你在想为什么我们还没去解析HTML,别急,马上就会讲到。

内部发生了什么?

爬虫的start_requests方法返回scrapy.Request对象,并由Scrapy安排调度。在接收到每个请求所对应的响应时,Scrapy会实例化响应(Response)对象,并调用与请求相关的回调方法(此时为parse方法),同时将响应作为参数传递过去。

走条捷径

不需要使用start_requests()方法来从网址中生成scrapy.Request对象,你可以就定义一个start_urls类属性,将网址以列表形式赋予给它。该列表会默认被start_requests()使用来给你的爬虫创建初始请求:

然后parse()方法会被调用,用来处理每个网址所对应生成的请求。虽然我们没有指明让Scrapy这么做,但之所以会这样,是因为parse()是Scrapy用于处理请求的默认回调方法,不被显性调用。

提取数据

学习如何用Scrapy提取数据的最好方法是使用Scrapy shell工具。运行以下命令:

scrapy shell ‘http://quotes.toscrape.com/page/1/’

注意:

在命令行下运行Scrapy shell时,要记住给网址带上单引号。不然那些带有某些参数(如&符号)的网址不会起作用。

在Windows平台上要用双引号:

scrapy shell “http://quotes.toscrape.com/page/1/”

接着你会看到如下类似输出:

在使用shell工具时,你可以尝试对响应对象使用CSS来选择元素:

运行response.css('title')后得到一个类似列表的SelectorList对象,它表示包裹在XML/HTML元素周围的Selector对象的一个列表,其允许你进一步提交查询来细化选择或直接提取数据。

若要从上面的title中提取文本,你可以输入:

这里需要注意两件事:第一个是我们在CSS查询中加入了::text,这表示我们只想选择元素中的文本元素。如果不指出::text,我们将得到全部的title元素,包括它的标签:

另一件事是调用.extract()后会得到一个列表,这是因为我们现在处理的是SelectorList的一个实例。如果你只想要列表中的第一个元素,可以这么写:

本来呢,你可以这么写:

不过,用.extract_first()可以避免出现IndexError,如果没有找到对应的元素,则会返回一个None。

这里有一个技巧:由于页面中没有数据,所以会导致出现一些错误,而你应该让大部分的爬虫代码巧妙地应对这些错误,所以就算一些部分无法被爬取到,你至少也可以得到一些数据。

除了extract()extract_first()方法,你还可以配合正则表达式用re()方法来提取数据。

为了使用合适的CSS选择器,以下方法对你会有所帮助:用view(response)在你的浏览器中打开shell中下载过来的响应页面。你可以使用浏览器开发者工具或者类似Firebug(参见《利用Firebug来爬取》和《利用Firefox来爬取》这些章节)这种扩展程序。

被大部分浏览器所支持的Selector Gadget也是一个不错的工具,它可以快速确定CSS选择器以直观地选择元素。

XPath简介:

除了CSS,Scrapy选择器同样支持XPath表达式。

XPath表达式非常强大,它同样也是Scrapy选择器的基础。实际上,如果你仔细阅读shell中选择器对象的文本表述,你就会发现CSS选择器会在内部被转换成XPath。

XPath表达式可能没有CSS选择器那样流行,但它提供了更加强大的功能。除了能够导航结构之外,它也可以查找内容,比方说通过XPath你能够选择包含文本“Next Page”的链接,所以XPath非常适用于爬取任务。如果你已经知道怎么用CSS选择器的话,我们也鼓励你去学习下XPath,它会让爬取变得更加简单。

我们不会在这里覆盖很多XPath方面的知识,但是你可以通过这个链接来了解如何通过Scrapy选择器来使用XPath。如果想要进一步学习XPath,我们建议阅读这两个向导。第一个向导会通过实例带你学习XPath,而第二个则会带你学习如何用XPath思考。

提取名人名言

现在你已经对选择和提取稍有了解,接下来就让我们通过代码来从网页上提取出名言数据,以完成该爬虫。

让我们打开Scrapy shell,并尝试去提取到我们想要的数据:

通过以下方式我们可以得到与quote这类HTML元素相对应的选择器列表:

由以上查询返回的每一个选择器都可以让我们对子元素进行更加深入的查询。让我们给第一个选择器赋一个变量,这样就可以将我们的CSS选择器直接作用在一个属于quote类别的特定div元素上。

现在,让我们使用刚才创建的quote对象把title,author还有tags从quote中提取出来:

因为tags是一个字符串列表,所以我们可以采用.extract()方法来得到。

知道怎么在一个quote元素下进行提取后,我们现在可以迭代所有quotes元素,然后将它们一起放在一个Python字典中。

在爬虫中提取数据

回到我们写的爬虫。到目前为止,爬虫还没有爬取任何具体的数据,它只是把一整个HTML页面下载到了一个本地文件中。现在让我们将上述的提取逻辑整合到我们的爬虫中。

一个Scrapy爬虫通常会生成许多包含从页面中提取过来的数据的字典。我们也可以在回调方法中使用Python关键字yield做到,如下所示:

如果运行这个爬虫,则会输出日志和提取到的数据。

存储爬取到的数据

要存储爬取到的数据,最简单的方法是使用Feed exports,运行以下命令:

scrapy crawl quotes -o quotes.json

接着会生成一个包含所有爬取字段并且以JSON格式序列化的quotes.json文件。

你也可以使用其他格式,比如JSON Lines:

scrapy crawl quotes -o quotes.jl

JSON Lines这种格式很有用,因为它类似流,你可以很方便地加入新的记录。当你运行两次的时候,它也不会出现和JSON一样的问题。而且,因为每条记录都是单独成行,所以你不必等到先把整个大型文件存入内存中再进行处理,有一些像JQ这样的工具来可以在命令行中帮忙做到这点。

在小型项目中(比如该向导中的项目),用Feed exports输出应该就足够了。但是如果你想对所爬取字段进行更复杂的操作的话,可以编写一个字段管道(Item Pipeline)。当创建项目时,文件夹里就会出现一个用于编写字段管道的占位文件tutorial/pipelines.py。不过如果你只想存储所爬取字段的话,就不需要在设置中执行任何字段管道。

跟踪链接

既然你已经知道怎么从页面上提取数据,让我们看看怎么从这些页面中跟踪一些链接。

首先提取我们想要跟踪的链接。审查过页面后,我们可以发现带有以下标记的可进入下一页的链接:

让我们尝试在shell中提取:

这样会得到锚元素,但我们要的是href属性。为应对这种情况,Scrapy支持一种扩展的CSS来让你可以选择属性内容,比如:

现在看下我们改过后的爬虫,它可以递归跟踪下一页链接,并提取相应页面的数据:

在提取过数据后,parse()方法会查找下一页链接,并通过urljoin()方法来构造一个完整的绝对路径(因为提取到的可能为相对路径),接着提交一个进入下一页的新请求,并将其自身作为回调方法来提取下一页的数据,如此循环爬取,直至最后。

这就是Scrapy的跟踪链接机制:当你在一个回调方法中提交请求,Scrapy会安排发送该请求,并在请求结束后注册一个待执行的回调方法。

通过这种方式,你可以构建一个复杂的爬虫程序来根据你所定义的规则去跟踪链接,并从访问的页面上提取出不同的数据。

以上我们所编写的爬虫创建了一种循环,来不断跟踪下一页的链接,直到最后一页——适合爬取博客,论坛以及其他有页码的网站。

创建请求的一条捷径

你可以使用response.follow来更快地创建请求对象:

与scrapy.Request不同,response.follow直接支持相对路径——不需要调用urljoin。注意response.follow只是返回一个请求实例;你仍然需要提交该请求。

除了传递一个字符串,你还可以传递一个选择器给response.follow

;该选择器需能够提取到必要的属性:

对于元素,response.follow可以自动提取到它的href属性,所以代码可以被进一步缩短为:

注意:

不能使用response.follow(response.css('li.next a')),因为response.css返回一个类似列表的对象,其中包含对应所有结果的选择器。可以像上述例子那样用for循环,或者使用response.follow(response.css('li.next a')[0])。

更多例子和模式

下面是另一个用来爬取名人信息的爬虫,它举例说明了如何回调和跟踪链接。

该爬虫会从主页开始爬取,跟踪所有去往名人页面的链接,并调用parse_author回调方法来爬取每个名人页面,以及跟踪下一页的链接并再次回调parse方法(前文分见过)。

这里我们将response.follow作为位置参数(positional arguments),这样代码可以更简洁;当然也可以使用scrapy.Request。

parse_author回调方法定义了一个辅助方法来提取和清理用CSS查询到的数据,并返回带有名人数据的Python字典。

该爬虫所展现的另一个有趣的点是:虽然有很多名言都来自同一个作者,但是我们不必担心爬虫会去重复访问同一个名人页面。Scrapy会默认过滤掉已经访问过的网址,以避免由于编程失误来过度地访问服务器。以上功能可以通过设置DUPEFILTER_CLASS来实现。

希望现在你对Scrapy的跟踪链接和回调机制有了一个不错的理解。

上述爬虫例子充分利用了跟踪链接机制,你也可以去看下用于一般爬虫的CrawlSpider类,它会启动一个小型的规则引擎,而你可以基于该引擎来编写你自己的爬虫程序。

还有一种通用的模式就是从多个页面中构造一个数据字段,可使用一个技巧来给回调方法传递额外的数据。

使用爬虫参数

通过使用-a选项,你可以在运行爬虫时给它提供命令行参数:

scrapy crawl quotes -o quotes-humor.json -a tag=humor

这些参数会传递给爬虫的__init__方法,并会成为爬虫的默认属性。

在下面这个例子中,提供给tag参数的值会通过self.tag启用。你可以用这种方法来构建基于tag参数的网址,并让你的爬虫只爬取特定标签(tag)的名言:

如果你将tag=humor参数传递给该爬虫,你会发现它只会访问带有humor标签的网址,例如http://quotes.toscrape.com/tag/humor。

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20171209G07E7I00?refer=cp_1026

相关快讯

扫码关注云+社区