目录
推荐书籍:·崔庆才·著的Python3网络爬虫开发实战第2版
《正版 Python3网络爬虫开发实战第2版 崔庆才网络数据采集抓取处理分析书籍教程网络爬虫项目开发》【摘要 书评 试读】- 京东图书
本人根据此书进行python爬虫的学习并记录下学习过程中的笔记
Scrapy框架的使用
Scrapy入门
1.本节目标
-
创建一个Scrapy项目
-
编写一个Spider来抓取站点和处理数据
-
初步了解Item Pipeline的功能,将抓取的内容保存
-
运行Scrapy爬虫项目
要抓取的目标站点: Quotes to Scrape
此站点内容包括:名人名言,作者和标签
2.准备工作
需要安装好Scrapy框架
pip install Scrapy
3.创建项目
首先我们需要创建一个Scrapy项目,可以直接用命令生成,可以直接在Windows下的CMD窗口下在指定文件夹下创建,也可以在pycharm的终端下创建,项目名称可以叫作scrapytutorial,创建命令如下:
scrapy startproject scrapytutorial
执行完上述命令之后,会在你所在的当前目录生成一个名为scrapytutorial的文件夹,具体文件夹结构如下图所示
scrapy.cfg # Scrapy部署时的配置文件 scrapytutorial # 项目的模块,引入的时候需要从这里引入 __init__.py items.py # Items的定义,定义爬取的数据结构 middlewares.py # Middlewares.py的定义,定义爬取时的中间件 pipelines.py # Pipelines的定义,定义数据管道 settings.py # 配置文件 spiders # 放置Spiders的文件夹 __init__.py
4.创建Spider
出现如下图所示,则创建Scrapy项目完成,接下来开始创建Spider
使用命令行创建一个Spider,比如要生成Quotes这个Spider,可以执行如下命令
cd scrapytutorial
scrapy genspider quotes quotes.toscrape.com
进入刚才创建的scrapytutorial文件夹,然后执行genspider命令。第一个参数是Spider的名称,第二个参数是网站域名。执行完毕之后,spiders文件夹中多了一个quotes.py,它就是刚刚创建的Spider,内容如下所示:
如上图,QuotesSpider就是刚才命令行自动创建的Spider,它继承了scrapy的Spider类,QuotesSpider有3个属性,分别是name,allowed_domains和start_urls,还有一个方法parse。
-
name:name是每个项目唯一的名字,用来区分不同的Spider。
-
allowed_domains:allowed_domains是允许爬取的域名,如果初始或后续的请求链接不是这个域名下的,则请求链接会被过滤掉。
-
start_urls:start_urls包含了Spider在启动时爬取的URL列表,初始请求是由它来定义的。
-
parse:parse是Spider的一个方法。
5.创建Item
Item是保存爬取数据的容器,定义了爬取结果的数据结构。它的使用方法和字典类似。相比于字典,Item多了额外的保护机制,可以避免拼写错误或者是定义字段错误。
创建Item需要继承scrapy的Item类,并且定义类型为Field的字段,这个字段就是我们要爬取的字段。
观察我们要爬的目标网站,我们可以获取到的内容有下面几项。
-
text:文本,即每条名言的内容,是一个字符串。
-
authoe:作者,即每条名言的作者,是一个字符串。
-
tags:标签,即每条名言的标签,是字符串组成的列表。
这样的话,每条爬取数据就包含这3个字段,那么我们就可以定义对应的Item,此时将items.py修改如下:
import scrapy
class QuteItem(scrapy.Item):
text = scrapy.Field()
author = scrapy.Field()
tags = scrapy.Field()
这里,我们声明了QuteItem,继承了Item类,然后使用Field定义了3个字段,接下来爬取时我们会使用这个Item
6.解析Response
前面我们看到,parse方法的参数response是start_urls里面的链接爬取后的结果,即页面请求后得到的Response,Scrapy将其转化为了一个数据对象,里面包含了页面请求后得到的Response Status,Body等内容。所以在parse方法中,我们可以直接对response变量包含的内容进行解析,比如浏览请求结果的网页源代码,进一步分析源代码内容,或者找出结果中的链接而得到下一个请求。
我们可以看到网页中既有我们想要的结果,又有下一页的链接,这两部分内容我们都要进行处理。
首先看看网页结构。如下图所示。每一页都有多个class为quote的区块,每个区块内都包含text,author,tags。那么我们先找出所有的quote,然后提取每个quote中的内容。网页结构如下:
每一个class为quote的div标签都包含有名言,作者和标签三块部分,如下图,分别为名言,作者和标签
因此,我们可以使用CSS选择器或XPath选择器进行提取,这个过程我们可以直接借助response的css或xpath方法实现,这都是Scrapy给我们封装好的方法,直接调用即可。
在这里我们使用CSS选择器进行选择,可以将parse方法的内容进行如下改写:
def parse(self, response):
quotes = response.css('.quote')
for quote in quotes:
text = quote.css('.text::text').extract_first()
author = quote.css('.author::text').extract_first()
tags = quote.css('.tags .tag::text').extract()
这里首先利用CSS选择器选取所有的quote并将其赋值为quotes变量,然后利用for循环遍历每个quote,解析每个quote的内容
对text来说,观察到它的class为text,所用可以用.text选择器来选取,这个结果实际上是整个带有标签的节点,要获取它的正文内容,可以加::text。这时的结果是长度为1的列表,所以还需要用extract_first方法来获取第一个元素,而对于tags来说,由于我们要获取所有的标签,所以用extract方法获取整个列表即可。
为了更好地理解以上内容的提取过程,我们以第一个quote的结果为例,看一下各个提取写法会得到怎样的提取结果。源码如下:
<div class="quote" itemscope itemtype="http://schema.org/CreativeWork">
<span class="text" itemprop="text">“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”</span>
<span>by <small class="author" itemprop="author">Albert Einstein</small>
<a href="/author/Albert-Einstein">(about)</a>
</span>
<div class="tags">
Tags:
<meta class="keywords" itemprop="keywords" content="change,deep-thoughts,thinking,world" / >
<a class="tag" href="/tag/change/page/1/">change</a>
<a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a>
<a class="tag" href="/tag/thinking/page/1/">thinking</a>
<a class="tag" href="/tag/world/page/1/">world</a>
</div>
</div>
不同选择器的返回结果如下:
quote.css('.text') [<Selector xpath="descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), 'text')]"data='<span class="text"itemprop="text">"The "'>]quote.css('.text::text') [<Selector xpath="descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), 'text')]/text()"data='"The world as we have created it is a pr'>]quote.css('.text').extract() ['span class="text"itemprop="text">"The world as we have created it is a process of our thinking.It cannot be changed without changing our thinking."</span>']quote.css('.text::text').extract() ['"The world as we have created it is a process of our thinking.It cannot be changed without changing our thinking."']quote.css('.text::text').extract_first() "The world as we have created it is a process of our thinking.It cannot be changed without changing our thinking."
这里我们演示了不同提取过程的写法,其提取结果也是各不相同,比如单独调用css方法我们得到的是Selector对象组成的列表;调用extract方法会进一步从Selector对象里提取其内容,在加上::text则会从HTML代码中提取出正文文本
因此对于text,我们只需要获取结果的第一个元素即可,所以使用extract_first方法,得到的就是一个字符串。而对于tags,我们要获取所有结果组成的列表,所以使用extract方法,得到的就是所有标签字符串组成的列表。
def parse(self, response):
quotes = response.css('.quote')
for quote in quotes:
text = quote.css('.text::text').extract_first()
author = quote.css('.author::text').extract_first()
tags = quote.css('.tags .tag::text').extract()
7.使用Item
上文我们已经定义了QuoteItem,接下来就要使用它了。
我们可以把Item理解为一个字典,和字典还不太相同,其本质是一个类,所以在使用的时候需要实例化。实例化之后,我们一次用刚才解析的结果赋值Item的每一个字段,最后将Item返回。
QuotesSpider的改写如下:
import scrapy
from scrapytutorial.items import QuoteItem
class QuotesSpider(scrapy.Spider):
name = 'quotes'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['http://quotes.toscrape.com/']
def parse(self, response):
quotes = response.css('.quote')
for quote in quotes:
item = QuoteItem()
item['text'] = quote.css('.text::text').extract_first()
item['author'] = quote.css('.author::text').extract_first()
item['tags'] = quote.css('.tags .tag::text').extract()
yield item
如此一来,首页的所以内容就被解析出来并被复制成一个个QuoteItem了,每个QuoteItem就代表一条名言,包含名言内容,作者和标签。
8.后续Request
上面的操作实现了从首页抓取内容,如果运行它,我们其实已经可以从首页提取到所有quote信息并转化为一个个QuoterItem对象了。