咱们以前介绍了利用Beautiful Soup、正则表达式来提取网页数据,这确实很是方便。而Scrapy还提供了本身的数据提取方法,即Selector(选择器)。Selector 是基于lxml来构建的,支持XPath选择器、CSS选择器以及正则表达式,功能全面,解析速度和准确度很是高。css
Selector是一个能够独立使用的模块。咱们能够直接利用Selector这个类来构建一个选择器对象,而后调用它的相关方法如xpath()、css()等来提取数据。html
案例:web
from scrapy import Selector body= '<html><head><title>Hello World</title></head><body></body></ html>' selector = Selector(text=body) title = selector.xpath('//title/text()').extract_first() print(title)
结果:正则表达式
咱们没有在Scrapy框架中运行,而是把Scrapy中的Selector单独拿出来使用了,构建的时候传入text参数,就生成了Selector选择器对象,而后就能够像前面咱们所用的Scrapy中的解析方式同样,调用xpath()、css()等方法来提取。架构
在这里咱们查找的是源代码中的title中的文本,在Path选择器最后加 text()方法就能够实现文本的提取了。框架
以上内容就是Selector的直接使用方式Beautiful Soup等库相似,Selector其实也是强大的网页解析库。若是方便的话,咱们也能够在其余项目中直接使用Selector来提取数据。dom
Selector选择器的使用能够分为三步:scrapy
导入选择器from scrapy.selector import Selectoride
建立选择器实例selector = Selector(response=response)函数
使用选择器selector.xpath()或者selector.css()
不过Scrapy项目里咱们能够直接response.css()或response.xpath(),怎么方便怎么用。
在Scrapy中,要抓取网站的连接配置、抓取逻辑、解析逻辑里其实都是在Spider中配置的在上一章的实例中,咱们发现抓取逻辑也是在Spider中完成的。
在实现Scrapy爬虫项目时,最核心的类即是Spider类了,它定义了如何爬取某个网站的流程和解析方式。简单来讲,Spider要作的事就是两件:定义爬取网站的动做和分析爬取下来的网页。
Spider循环爬取过程:
以初始的URL初始化Request,并设置回调函数。当该Request成功请求并返回时,Response生成并做为参数传给该回调函数。
在回调函数内分析返回的网页内容。返回结果有两种形式。一种是解析到的有效结果返回字典或Item对象,它们能够通过处理后(或直接)保存。另外一种是解析获得下一个(以下页)连接,能够利用此连接构造Reque并设置新的回调函数,返回Request等待后续调度。
若是返回的是字典或Item对象,咱们可经过Feed Exports等组件将返回结果存入到文件。若是设置了Pipeline的话,咱们可使用Pipeline处理(如过滤、修正等)并保存。
若是返回的是Reqeust,那么Request执行成功获得Response以后,Response会被传递给Request中定义的回调函数,在回调函数中咱们能够再次使用选择器来分析新获得的网页内 容,并根据分析的数据生成Item。
经过以上几步循环往复进行,咱们完成了站点的爬取。
在上一章的例子中,咱们定义的Spider是继承自scrapy.spiders.Spider。scrapy.spiders.Spider这个类是最简单最基本的Spider类,其余Spider必须继承这个类。还有后面一些特殊Spider类也都继承自它。
scrapy.spiders.Spider这个类提供了start_requests()方法的默认实现,读取并请求start_urls属性,并根据返回的结果调用 parse()方法解析结果。
基础属性:
name:爬虫名称,是定义Spider名字的字符串。Spider的名字定义了Scrapy如何定位并初始化Spider,它必须是惟一的。不过咱们能够生成多个相同的Spider实例,数量没有限制。name是Spider最重要的属性。若是Spider爬取单个网站,一个常见的作法是以该网站的域名名称来命名Spider。例如,Spider爬取mywebsite.com,该Spider一般会被命名为mywebsite。
allowed_domains:容许爬取的域名,是可选配置,不在此范围的连接不会被跟进爬取。
start_urls:它是起始URL列表,当咱们没有实现start_requests()方法时,默认会从这个列表开始抓取。
custom_settings:它是一个字典,是专属于本Spider的配置,此设置会覆盖项目全局的设置。此设置必须在初始化前被更新,必须定义成类变量。
crawler:它是由from_crawler()方法设置的,表明的是本Spider类对应的Crawler对象。Crawler对象包含了不少项目组件,利用它咱们能够获取项目的一些配置信息,如最多见的获取项目的设置信息,即Settings。
settings:它是一个Settings对象,利用它咱们能够直接获取项目的全局设置变量。
除了基础属性,Spider还有一些经常使用的方法:
start_requests():此方法用于生成初始请求,它必须返回一个可迭代对象。此方法会默认使用start_urls里面的URL来构造Request,并且Request是GET请求方式。若是咱们想在启动时以POST方式访问某个站点,能够直接重写这个方法,发送 POST请求时使用FormRequest便可。
parse():当Response没有指定回调函数时,该方法会默认被调用。它负责处理Response处理返回结果,并从巾提取处想要的数据和下一步的请求,而后返回。该方法须要返回一个包含Request或ltem的可迭代对象。
closed():当Spider关闭时,该方法会被调用,在这里通常会定义释放资源的一些操做或其余收尾操做。
Downloader Middleware即下载中间件,它是处于Scrapy的Request和Response之间的处理模块。
咱们上一章已经看过Scrapy框架的架构了。
Scheduler从队列中拿出一个Request发送给Downloader执行下载,这个过程会通过Downloader Middleware的处理。另外,当Downloader将Request下载完成获得Response返回给Spider时会再次通过Downloader Middleware处理。
也就是说,Downloader Middleware在整个架构中起做用的位置有两个,分别是:
在Scheduler调度出队列的Request发送给Doanloader下载以前,也就是咱们能够在Request执行下载以前对其进行修改。
在下载后生成的Response发送给Spider以前,也就是咱们能够在生成Resposne被Spider解析以前对其进行修改。
Downloader Middleware的功能很是强大,修改User-Agent处理重定向、设置代理、失败重试、设置 Cookies等功能都须要借助它来实现。
Scrapy其实已经提供了许多Downloader Middleware,好比负责失败重试、自动重定向等功能的Middleware,它们被DOWNLOADER_MIDDLEWARES_BASE变量所定义。
DOWNLOADER_MIDDLEWARES_BASE变量的内容以下所示:
这是一个字典格式,字典的键名是Scrapy内置的Downloader Middleware的名称,键值表明了调用的优先级,优先级是一个数字,数字越小表明越靠近Scrapy引擎,数字越大表明越靠近Downloader,数字小的Downloader Middleware会被优先调用。
若是向己定义的Downloader Middleware要添加到项目里,DOWNLOADER_MIDDLEWARES_BASE变量不能直接修改。Scrapy提供了另一个设置变量DOWNLOADER_MIDDLEWARES,咱们直接修改这个变量就能够添加本身定义的DownloaderMiddleware,以及禁用DOWNLOADER_MIDDLEWARES_BASE里面定义的Downloader Middleware。
Scrapy内置的Downloader Middleware为Scrapy提供了基础的功能,但在项目实战中咱们每每须要单独定义Downloader Middleware。不用担忧,这个过程很是简单,咱们只须要实现某几个方法便可。
每一个Downloader Middleware都定义了一个或多个方法的类,核心的方法有以下三个:
process_request(request,spider)
process_response(request,response,spider)
pro cess_exception(request,exception,spider)
咱们只须要实现至少一个方法,就能够定义一个Downloader Middleware下面咱们来看看这三个方法的详细用法。
Request被Scrapy引擎调度给Downloader以前,process_request()方法就会被调用,也就是在Request从队列里调度出来到Downloader下载执行以前,咱们均可以用process_request()方法对 Request进行处理。方法的返回值必须为None、Response对象、Request对象之一,或者抛出IgnoreRequest异常。
process_request()方法的参数有以下两个:
request,是Request对象,即被处理的Request。
spider,是Spdier对象,即此Request对应的Spider。
返回类型不一样,产生的效果也不一样。下面概括一下不一样的返回状况。
Downloader执行Request下载以后,会获得对应的Response。Scrapy引擎便会将Response发送给 Spider进行解析。在发送以前,咱们均可以用process_response()方法来对Response进行处理。方法的返回值必须为Request对象、Response对象之一,或者抛出IgnoreRequest异常。
process_response()方法的参数有以下三个:
request,是Request对象,即此Response对应的Request。
response,是Response对象,即此被处理的Response。
spider,是Spider对象,即此Response对应的Spider。
下面概括下不一样的返回状况。
当Downloader或process_request()方法抛出异常时,例如抛出IgnoreRequest异常,process_exception()方法就会被调用。方法的返回值必须为None、Response对象、Request对象之一。
process_exception()方法的参数有以下:
request,是Request对象,即产生异常的Request。
exception,是Exception对象,即抛出的异常。
spdier,是Spider对象,即Request对应的Spider。
下面概括一下不一样的返回状况。
以上内容即是这三个方法的详细使用逻辑。在使用它们以前,请先对这三个方法的返回值的处理状况有一个清晰的认识。在自定义Downloader Middleware的时候,也必定要注意每一个方法的返回类型。
新建一个项目。
scrapy startproject scrapydownloadertest
新建了一个Scrapy项目,名为scrapydownloadertest。进入项目,新建一个Spider。
scrapy genspider httpbin httpbin.org
新建了一个Spider,名为httpbin。
# -*- coding: utf-8 -*- import scrapy class HttpbinSpider(scrapy.Spider): name = 'httpbin' allowed_domains = ['httpbin.org'] start_urls = ['http://httpbin.org/'] def parse(self, response): pass
接下来咱们修改start_urls,将parse()方法添加一行日志输出,将response变量的text属性输出出来,这样咱们即可以看到Scrapy发送的Request信息了。
修改Spider内容以下:
# -*- coding: utf-8 -*- import scrapy class HttpbinSpider(scrapy.Spider): name = 'httpbin' allowed_domains = ['httpbin.org'] start_urls = ['http://httpbin.org/get'] def parse(self, response): self.logger.debug(response.text)
接下来运行此Spider。
scrapy crawl httpbin
Scrapy运行结果包含Scrapy发送的Request信息,内容以下:
咱们观察一下Headers,Scrapy发送的Request使用的User-Agent是Scrapy/1.8.0(+http: //scrapy.org),这实际上是由Scrapy内置的UserAgentMiddleware设置的,UserAgentMiddleware的源码以下:
在from_crawler()方法中,首先尝试获取settings里面USER_AGENT,而后把USER_AGENT传递给__init__()方法进行初始化,其参数就是user_agent。若是没有传递USER_AGENT参数就是默认设置为Scrapy字符串。咱们新建的项目没有设置USER_AGENT,因此这里的user_agent变量就是Scrapy。接下来,在process_request()方法中,将user-agent变量设置为headers变量的一个属性,这样就成功设置了User-Agent。所以,User-Agent就是经过此Downloader Middleware的process_request()方法设置的。
修改请求时的User-Agent能够有两种方式:一是修改settings里面的USER_AGENT变量;二是经过Downloader Middleware的process_request()方法来修改。
第一种方法很是简单,咱们只须要在setting.py里面加一行USER_AGENT的定义便可:
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
通常推荐使用这种方法来设置。可是若是想设置得更加灵活,好比设置随机的User-Agent的设置。
第二种方法,在middlewares.py里添加一个RandomUserAgentMiddleware的类。
import random class RandomUserAgentMiddlerware(): def __init__(self): self.user_agents = [ #放几个User-Agent在里面 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36' ] def process_request(self,request,spider): request.headers['User-Agent'] = random.choice(self.user_agents)
咱们首先在类的__init__()方法中定义几个不一样的User-Agent,并用一个列表来表示。接下来实现了process_request()方法,它有一个参数request,咱们直接修改request的属性便可。在这里咱们直接设置了request变量的headers属性的User-Agent,设置内容是随机选择的User-Agent,这样一个Downloader Middleware就写好了。
不过,要使之生效咱们还须要再去调用这个Downloader Middleware。在settings.py中,将DOWNLOADER_MIDDLEWARES取消注释,并设置成以下内容:
DOWNLOADER_MIDDLEWARES = { 'scrapydownloadertest.middlewares.RandomUserAgentMiddleware':543 }
而后从新运行Spider,就能够看到User-Agent被成功修改成列表中所定义的随机的一个User-Agent了。
咱们就经过实现Downloader Middleware并利用process_request()方法成功设置了随机的User-Agent。
另外,Downloader Middleware还有process_response()方法。Downloader对Request执行下载以后会获得Response,随后Scrapy引擎会将Response发送回Spider进行处理。可是在Response被发送给Spider以前,咱们一样可使用process_response()方法对Response进行处理。好比这里修改一下Response的状态码,在RandomUserAgentMiddleware添加以下代码:
def process_response(self,request,response,spider): response.status = 201 return response
咱们将response变量的status属性修改成201,随后将response返回,这个被修改后的Response就会被发送到Spider。
咱们再在Spider里面输出修改后的状态码,在parse()方法中添加以下的输出语句:
self.logger.debug('Status Code :' + str(response.status))
从新运行以后,控制台输出了以下内容:
[httpbin] DEBUG: Status Code: 201
能够发现,Response的状态码成功修改了。
所以要想对Response进行后处理,就能够借助于process_response()方法。
另外还有一个process_exception()方法,它是用来处理异常的方法。若是须要异常处理的话,咱们能够调用此方法。不过这个方法的使用频率相对低一些,这里就不谈了。