当前位置:   article > 正文

python打开一个软件并进行操作_Python自动化开发学习-Scrapy

python打开一个软件并添加不同的项目

项目准备

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。使用之前有一个类似django的创建项目以及目录结构的过程。

Scrapy 安装

使用pip安装(windows会有问题):

pip3 install scrapy

装不上主要是因为依赖的模块Twisted安装不上,所以得先安装Twisted,并且不能用pip直接下载安装。先去下载Twisted的whl安装文件:https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

然后使用pip本地安装:

pip install E:\Downloads\Twisted-18.9.0-cp36-cp36m-win_amd64.whl

pip install -i https://mirrors.aliyun.com/pypi/simple/ scrapy

pip install -i https://mirrors.aliyun.com/pypi/simple/ pywin32

Scrapy 组件

Scrapy主要包括了以下组件:

引擎(Scrapy):

用来处理整个系统的数据流处理, 触发事务(框架核心)

调度器(Scheduler):

用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址

下载器(Downloader):

用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)

爬虫(Spiders):

爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面

项目管道(Pipeline):

负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

下载器中间件(Downloader Middlewares):

位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。

爬虫中间件(Spider Middlewares):

介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。

调度中间件(Scheduler Middewares):

介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

工作流程:

绿线是数据流向,引擎是整个程序的入口。首先从初始 URL 开始(这步大概是引擎把初始URL加到调度器),Scheduler 会将其交给 Downloader 进行下载,下载之后会交给 Spider 进行分析,Spider 分析出来的结果有两种:一种是需要进一步抓取的链接,例如“下一页”的链接,这些东西会被传回 Scheduler ;另一种是需要保存的数据,它们则被送到 Item Pipeline 那里,那是对数据进行后期处理(详细分析、过滤、存储等)的地方。

另外,引擎和其他3个组件直接有通道。在数据流动的通道里还可以安装各种中间件,进行必要的处理。

Scrapy 项目结构

启动项目

打开终端进入想要存储 Scrapy 项目的目录,然后运行 scrapy startproject (project name)。创建一个项目:

> scrapy startproject PeppaScrapy

执行完成后,会生成如下的文件结构:

ProjectName/

├── ProjectName

│ ├── __init__.py

│ ├── items.py

│ ├── middlewares.py

│ ├── pipelines.py

│ ├── settings.py

│ └── spiders

│ └── __init__.py

└── scrapy.cfg

文件说明

scrapy.cfg : 项目的主配置信息。(真正爬虫相关的配置信息在settings.py文件中)

items.py : 设置数据存储模板,用于结构化数据,如:Django的Model

pipelines : 数据处理行为,如:一般结构化的数据持久化

settings.py : 配置文件,如:递归的层数、并发数,延迟下载等

spiders : 爬虫目录,如:创建文件,编写爬虫规则

创建爬虫应用

先切换到项目目录,在执行grnspider命令 scrapy genspider [-t template] (name) (domain) 。比如:

> cd PeppaScrapy

> scrapy genspider spider_lab lab.scrapyd.cn

效果就是在spiders目录下,创建了一个spider_lab.py的文件。这里没有用-t参数指定模板,就是用默认模板创建的。其实不用命令也行了,自己建空文件,然后自己写也是一样的。

可以使用-l参数,查看有哪些模板:

> scrapy genspider -l

Available templates:

basic

crawl

csvfeed

xmlfeed

然后再用-d参数,加上上面查到的模板名,查看模板的内容:

> scrapy genspider -d basic

# -*- coding: utf-8 -*-

import scrapy

class $classname(scrapy.Spider):

name = '$name'

allowed_domains = ['$domain']

start_urls = ['http://$domain/']

def parse(self, response):

pass

爬取页面

把之前的创建的应用的文件修改一下,简单完善一下parse方法:

import scrapy

class SpiderLabSpider(scrapy.Spider):

name = 'spider_lab'

allowed_domains = ['lab.scrapyd.cn']

start_urls = ['http://lab.scrapyd.cn/']

def parse(self, response):

print(response.url)

print(response.body.decode())

查看应用列表:

> scrapy list

spider_lab

运行单独爬虫应用,这里加上了--nolog参数,避免打印日志的干扰:

> scrapy crawl spider_lab --nolog

在python里启动爬虫

每次都去命令行打一遍命令也很麻烦,也是可以直接写python代码,执行python来启动的。把下面的代码加到引用文件的最后:

if __name__ == '__main__':

from scrapy import cmdline

log_level = '--nolog'

name = SpiderLabSpider.name

cmdline.execute(('scrapy crawl %s %s' % (name, log_level)).split())

其实就是提供了在python里调用命令行执行命令的方法。之后,还可以写一个main.py放到项目根目录下,写上启动整个项目的命令。

Windows 编码问题

有可能会遇到编码问题,不过我的windows没问题,如果遇到了,试一下下面的方法:

import io

import sys

sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')

踩坑(爬虫Robots协议)

Robots协议就是每个网站对于来到的爬虫所提出的要求。并非强制要求遵守的协议,只是一种建议。

默认scrapy遵守robot协议。我在爬 http://dig.chouti.com/ 的时候遇到了这个问题。把 --nolog 参数去掉,查看错误日志,有如下的信息:

[scrapy.core.engine] DEBUG: Crawled (200) (referer: None)

[scrapy.downloadermiddlewares.robotstxt] DEBUG: Forbidden by robots.txt:

先去下载robots.txt文件,然后根据文件的建议,就禁止继续爬取了。可以直接浏览器输入连接查看文件内容:

User-agent: *

Allow: /link/

Disallow: /?

Disallow: /*?

Disallow: /user

Disallow: /link/*/comments

Disallow: /admin/login

# Sitemap files

Sitemap: https://dig.chouti.com/sitemap.xml

你要守规矩的的话,就只能爬 https://dig.chouti.com/link/xxxxxxxx 这样的url,一个帖子一个帖子爬下来。

如果可以选择不遵守协议,那么就在爬的时候把这个设置设为False。全局的设置在settings.py文件里:

# Obey robots.txt rules

ROBOTSTXT_OBEY = True

也可以只对一个应用修改设置:

import scrapy

class SpiderLabSpider(scrapy.Spider):

name = 'chouti'

allowed_domains = ['chouti.com']

start_urls = ['http://dig.chouti.com/']

custom_settings = {'ROBOTSTXT_OBEY': False}

def parse(self, response):

print(response.url)

print(response.encoding)

print(response.text)

if __name__ == '__main__':

from scrapy import cmdline

log_level = '--nolog'

name = SpiderLabSpider.name

cmdline.execute(('scrapy crawl %s %s' % (name, log_level)).split())

自定义请求头

上面踩坑的过程中,一度以为是请求头有问题,已定义请求头的方法也是设置settings.py文件,里面有一个剩下的默认配置:

# Override the default request headers:

#DEFAULT_REQUEST_HEADERS = {

# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',

# 'Accept-Language': 'en',

#}

默认都注释掉了,你可以在这里为全局加上自定义的请求头,当然也可以只为单独的应用配置:

import scrapy

class SpiderLabSpider(scrapy.Spider):

name = 'test'

allowed_domains = ['chouti.cn']

start_urls = ['http://dig.chouti.com/']

# 这个网站会屏蔽User-Agent里包含python的请求

custom_settings = {'ROBOTSTXT_OBEY': False,

'DEFAULT_REQUEST_HEADERS': {'User-Agent': 'python'},

}

def parse(self, response):

print(response.request.headers) # 这个是请求头

print(response.headers) # 这个是响应头

if __name__ == '__main__':

from scrapy import cmdline

log_level = ''

name = SpiderLabSpider.name

cmdline.execute(('scrapy crawl %s %s' % (name, log_level)).split())

xpath 选择器

使用xpaht选择器可以提取数据,另外还有CSS选择器也可以用。

XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。对 XPath 的理解是很多高级 XML 应用的基础。

scrapy 里的 xpath

解析页面内容会用到Selector这个类,下面只贴出parse回调函数里的代码:

from scrapy.selector import Selector

def parse(self, response):

title1 = response.xpath('//title')

print('title1', title1)

title2 = Selector(response).xpath('//title')

print('title2', title2)

上面的两种用法是一样的,通过response对象也可以直接调用xpath方法。这里说明了xpath方法是Selector这个类提供的。另外用方法二还有一个好处,就是因为之后需要调用Selector类里的方法,这样显示的声明Selector类之后,编辑器可以找到类似的方法,给出各种提示。直接用response调用,就没有这种便利了。

另外还有一个XmlXPathSelector类,作用和Selector类差不多,可能是就版本使用的类。

表达式

常用的表达式:

node_name : 选取从节点的所有子节点。就是标签名,比如上面的title

// : 匹配当前节点下的所有节点,不考虑位置。就是选择下面的子子孙孙

/ : 匹配当前节点下的子节点,只往下找一层,就是找儿子。类似文件路径

. : 选择当前节点。类似文件路径

.. : 选择当前节点的父节点。类似文件路径

@ : 选取属性

提取属性

提取属性的话,也是先定位到标签的范围,然后最后@属性名称,拿到所有对应的属性。另外@*可以拿到所有的属性。要当某个标签下的属性,就在标签名之后/@就好了:

Selector(response).xpath('//@href') # 提取所有的href属性

Selector(response).xpath('//ol[@class="page-navigator"]//@href') # ol.page-navigator下的所有的href属性

Selector(response).xpath('//head/meta/@*').extract() # head>meta 标签了所有的属性

Selector(response).xpath('//*[@id="body"]/div/@class') # id为body的标签的下一级标签里的class属性

查找标签,限定属性

使用这样的表达式:标签[@属性名='属性值'] ,另外还能用not(),注意要用小括号把取反的内容包起来:

Selector(response).xpath('//div[@id="body"]//span[@class="text"]') # 只要 span.text 的span标签

Selector(response).xpath('//div[@id="body"]//span[not(@class="text")]') # 没有text这个class的span标签

Selector(response).xpath('//meta[@name]') # 有name属性的meta

Selector(response).xpath('//meta[not(@name)]') # 没有name属性meta

提取值

xpath方法返回的是个对象,这个对象还可以无限次的再调用xpath方法。拿到最终的对象之后,我们需要获取值,这里有 extract() 和 extract_first() 这两个方法。因为查找的结果可能是多个值,extract方法返回列表,而extract_first方法直接返回值,但是是列表是第一个元素的值。

提取文字

表达式:/text() 可以把文字提取出来:

def parse(self, response):

tags = Selector(response).xpath('//ul[@class="tags-list"]//a/text()').extract()

print(tags) # 这样打印效果不是很好

for tag in tags:

print(tag.strip())

还有个方法,可以提取整段文字拼到一起。表达式:string() :

Selector(response).xpath('string(//ul[@class="tags-list"]//a)').extract() # 这样没拿全

Selector(response).xpath('string(//ul[@class="tags-list"])').extract() # 这样才拿全了

上面第一次没拿全,某个a标签下的文字就是一段。string()表达式看来值接收一个值,如果传的是个列表,可能就只操作第一个元素。

在我们商品详情、小说内容的时候可能会比较好用。

匹配class的问题

xpath中没有提供对class的原生查找方法。因为class里是可以包含多个值的。比如下面的这个标签:

Test

下面的表达式是无法匹配到的:

response.xpath('//div[@class="test"]')

要匹配到,你得写死:

response.xpath('//div[@class="test main"]')

但是这样显然是不能接受的,如果还有其他test但是没出main的标签就匹配不上了。

contains 函数 (XPath),检查第一个参数字符串是否包含第二个参数字符串。用这个函数就能做好了

response.xpath('//div[contains(@class, "test")]')

这样又有新问题了,如果有别的class名字比如:test1、mytest,这种也都会被上面的方法匹配上。

concat 函数 (XPath),返回参数的串联。就是字符串拼接,contains的两个参数的两边都加上空格,就能解决上面的问题。之所以要引入concat函数时因为,后面的字符串可以手动在两边加上空格,但是@class是变量,这个也不能用加号,就要用这个函数做拼接:

response.xpath('//div[contains(concat(" ", @class, " "), " test ")]')

normalize-space 函数 (XPath),返回去掉了前导、尾随和重复的空白的参数字符串。上面已经没问题了。不过还不够完美。在拼接@class之前,先把两边可能会出现的其他空白字符给去掉,可能会有某些操作需要改变一下class,但是又不要对这个class有任何实际的影响。总之这个是最终的解决方案:

response.xpath('//div[contains(concat(" ", normalize-space(@class), " "), " test ")]')

这里已经引出了好几个函数了,还有更多别的函数,需要的时候再查吧。

正则匹配

xpath也是可以用正则匹配的,用法很简单 re:test(x, y) 。第一个参数用@属性比较多,否则就是正则匹配标签了,就和纯的正则匹配似乎没什么差别了。

Selector(response=response).xpath('//a[re:test(@id, "i\d+")]')

xpath 与 css定位方式的比较

https://www.cnblogs.com/tina-cherish/p/7127812.html

xpath很强大,但是不支持原生的class,不过上面已经给了比较严谨的解决方案了。

css有部分功能无法实现。比如不能向上找,只能匹配当前层级,要通过判断子元素来确定当前元素是否匹配就不行。这种情况使用xpath的话,中括号里可以在嵌套中括号的。

不过css感觉更直观,也已经没什么学习成本了。

实战

登录抽屉并点赞。边一步一步实现,边补充用到的知识点。

获取首页的内容

import scrapy

from scrapy.selector import Selector

class SpiderLabSpider(scrapy.Spider):

name = 'chouti'

allowed_domains = ['chouti.com']

start_urls = ['http://dig.chouti.com/']

custom_settings = {'ROBOTSTXT_OBEY': False}

def parse(self, response):

items = Selector(response).xpath('//*[@id="content-list"]/div[@class="item"]')

for item in items:

news = item.xpath(

'./div[@class="news-content"]'

'//a[contains(concat(" ", normalize-space(@class), " "), " show-content ")]'

'/text()'

).extract()[-1]

print(news.strip())

if __name__ == '__main__':

from scrapy import cmdline

log_level = '--nolog'

name = SpiderLabSpider.name

cmdline.execute(('scrapy crawl %s %s' % (name, log_level)).split())

这里爬取的只是首页的内容

爬取所有页的内容

现在要获取所有分页的url,然后继续爬取。下面就是在parse回调函数后面增加了一点代码是做好了。不过现在的代码还不完善,会无休止的爬取下去,先不要运行,之后还要再改:

import urllib.parse

def parse(self, response):

items = Selector(response).xpath('//*[@id="content-list"]/div[@class="item"]')

for item in items:

news = item.xpath(

'./div[@class="news-content"]'

'//a[contains(concat(" ", normalize-space(@class), " "), " show-content ")]'

'/text()'

).extract()[-1]

print(news.strip())

# 不找下一页,而是找全部的页,这样会有去重的问题,就是要这个效果

pages = Selector(response).xpath('//div[@id="dig_lcpage"]//a/@href').extract()

print(pages)

url_parse = urllib.parse.urlparse(response.url)

for page in pages:

url = "%s://%s%s" % (url_parse.scheme, url_parse.hostname, page)

yield scrapy.Request(url=url)

这里做的事情就是当从前也分析了分页的信息,把分页信息生成新的url,然后再给调度器继续爬取。

这里用的 scrapy.Request() ,实际上是应该要通过 from scrapy.http import Request 导入再用的。不过这里并不需要导入,并且只能能在scrapy下调用。因为在 scrapy/__init__.py 里有导入这个模块了。并且这里已经不是系统第一次调用这个类了,程序启动的时候,其实就是跑了下面的代码把 start_urls 的地址开始爬取网页了:

for url in self.start_urls:

yield Request(url, dont_filter=True)

这段代码就是在当前类的父类 scrapy.Spider 里的 start_requests 方法里面。

爬取深度

爬取深度,允许抓取任何网站的最大深度。如果为零,则不施加限制。

这个是可以在配置文件里设置的。默认的配置里没有写这条,并且默认值是0,就是爬取深度没有限制。所以就会永不停止的爬取下去。实际上不会无休止,似乎默认就有去重的功能,爬过的页面不会重复爬取。所以不设置爬取深度,就能把所有的页面都爬下来了

这里要讲的是爬取深度的设置,所以和其他设置一样,可以全局的在settings.py里设置。也可以现在类的公用属性 custom_settings 这个字典里:

custom_settings = {

'ROBOTSTXT_OBEY': False,

'DEPTH_LIMIT': 1,

}

这个深度可以在返回的response参数里找到,在meta这个字典里:response.meta['depth']

去重规则

默认有下面2条配置:

DUPEFILTER_CLASS = 'scrapy.dupefilters.RFPDupeFilter'

DUPEFILTER_DEBUG = False

去重的功能默认就是在 'scrapy.dupefilters.RFPDupeFilter' 这个类里做的。这个类有个父类 BaseDupeFilter 帮我们定义好了接口,我们可以写一个自己的类自定义去重规则,继承 BaseDupeFilter 实现里面的方法:

from scrapy.dupefilters import BaseDupeFilter

class MyFilter(BaseDupeFilter):

def __init__(self):

# 去重可以用上集合

# 在request_seen方法里判断这个set,操作这个set

self.visited_url = set()

@classmethod

def from_settings(cls, settings):

"""初始化时调用的方法

返回一个实例,作用就是可以调用配置的信息生成实例

实例化时使用:obj = MyFilter.from_settings()

所以不要这样实例化:obj = MyFilter()

什么都不写,上面两重方法生成的实例是一样的

"""

return cls()

def request_seen(self, request):

"""过滤规则

检测当前请求是否需要过滤(去重)

返回True表示需要过滤,返回False表示不用过滤

"""

return False

def open(self): # can return deferred

"""开始爬虫时,调用一次

比如要记录到文件的,在这里检查和重建记录文件

"""

pass

def close(self, reason): # can return a deferred

"""结束爬虫时,调用一次

这里可以把之前的记录文件close掉

"""

pass

def log(self, request, spider): # log that a request has been filtered

"""日志消息的记录或打印可以写在这里"""

pass

现在知道了,默认就是有去重规则的。所以上面爬取所有页面的代码并并不会无休止的执行下去,而是可以把所有页面都爬完的。

启动和回调函数

程序启动后,首先会调用父类 scrapy.Spider 里的 start_requests 方法。我们也可以不设置 start_urls 属性,然后自己重构 start_requests 方法。启动的效果是一样的:

# start_urls = ['http://lab.scrapyd.cn/']

def start_requests(self):

urls = ['http://lab.scrapyd.cn/']

for url in urls:

yield scrapy.Request(url=url, dont_filter=True)

另外就是这个 scrapy.Request 类,回调函数 parse 方法最后也是调用这个方法类。这里还有一个重要的参数 callback 。默认不设置时 callback=parse ,所以可以手动设置callback参数,使用别的回调函数。或者准备多个回调函数,每次调度的时候设置不同额callback。比如第一次用默认的,之后在 parse 方法里再调用的时候,设置 callback=func 使用另外的回调函数。

Cookie

默认就是开启Cookie的,所以其实我们并不需要操作什么。

配置的 COOKIES_ENABLED 选项一旦关闭,则不会有Cookie了,别处再怎么设置也没用。

可以用meta参数,为请求单独设置cookie:

yield scrapy.Request(url, self.login, meta={'cookiejar': True})

不过如果要为请求单独设置的话,就得为每个请求都显示的声明。否则不写,就是认为是不要cookie。meta可以有如下设置:

meta={'cookiejar': True} # 使用Cookie

meta={'cookiejar': False} # 不使用Cookie,也就写在第一个请求里。之后的请求不设置就是不使用Cookie

meta={'cookiejar': response.meta['cookiejar']} # 使用上一次的cookie,上一次必须是True或者这个,否则会有问题

手动设置cookie值

Request 实例化的时候有 cookies 参数,直接传字典进去就可以了。

获取cookie的值

并没有cookie这个专门的属性。本质上cookie就是headers里的一个键值对,用下面的方法去headers里获取:

response.request.headers.getlist('Cookie') # 请求的Cookie

response.headers.getlist('Set-Cookie') # 响应的Cookie

登录抽屉并点赞

最后就是综合应用了。登录需要Cookies的操作。不过其实什么都不做就可以了,默认方法就能把Cookies操作好。

然后就是从打开页面、完成登录、到最后点赞,需要发多次的请求,然后每次请求返回后所需要做的操作也是不一样的,这里就需要准备多个回调函数,并且再发起请求的时候指定回调函数。代码如下:

import scrapy

from scrapy.selector import Selector

from utils.base64p import b64decode_str # 自己写的从文件读密码的方法,不是重点

class SpiderLabSpider(scrapy.Spider):

name = 'chouti_favor'

custom_settings = {

'ROBOTSTXT_OBEY': False,

}

def start_requests(self):

url = 'http://dig.chouti.com/'

yield scrapy.Request(url, self.login)

def login(self, response):

# 避免把密码公开出来,去文件里拿,并且做了转码,这不是这里的重点

with open('../../utils/password') as f:

auth = f.read()

auth = auth.split('\n')

post_dict = {

'phone': '86%s' % auth[0], # 从请求正文里发现,会在手机号前加上86

'password': b64decode_str(auth[1]), # 直接填明文的用户名和密码也行的

}

yield scrapy.FormRequest(

url='http://dig.chouti.com/login',

formdata=post_dict,

callback=self.check_login,

)

def check_login(self, response):

print(response.request.headers.getlist('Cookie'))

print(response.headers.getlist('Set-Cookie'))

print(response.text)

yield scrapy.Request(

url='http://dig.chouti.com/',

dont_filter=True, # 这页之前爬过了,如果不关掉过滤,就不会再爬了

)

def parse(self, response):

items = Selector(response).xpath('//*[@id="content-list"]/div[@class="item"]')

do_favor = True

for item in items:

news = item.xpath(

'./div[@class="news-content"]'

'//a[contains(concat(" ", normalize-space(@class), " "), " show-content ")]'

'/text()'

).extract()[-1]

print(news.strip())

# 点赞,做个判断,只赞第一条

if do_favor:

do_favor = False

linkid = item.xpath('./div[@class="news-content"]/div[@share-linkid]/@share-linkid').extract_first()

yield scrapy.Request(

url='https://dig.chouti.com/link/vote?linksId=%s' % linkid,

method='POST',

callback=self.favor,

)

def favor(self, response):

print("点赞", response.text)

if __name__ == '__main__':

from scrapy import cmdline

log_level = '--nolog'

name = SpiderLabSpider.name

cmdline.execute(('scrapy crawl %s %s' % (name, log_level)).split())

注意:首页的地址 http://dig.chouti.com 一共访问了两次。第二次如果不把 dont_filter 设为True,关闭过滤,就不会再去爬了。当然也可以第一次爬完之后,就保存在变量里,等登录后再从这个返回开始之后的处理。

上面的POST请求,用到了 FormRequest 这个类。这个类继承的是 Request 。里面主要就是把字典拼接成请求体,设置一下请求头的 Content-Type ,默认再帮我们把 method 设为 POST 。也是可以继续用 Request 的,就是把上面的3个步骤自己做了。主要是请求体,大概是按下面这样拼接一下传给body参数:

body='phone=86151xxxxxxxx&password=123456&oneMonth=1',

格式化处理

之前只是简单的处理,所以在parse方法中直接处理。对于想要获取更多的数据处理,则可以利用Scrapy的items将数据格式化,然后统一交由pipelines来处理。

回顾一下 Scrapy 组件和工作流程,项目管道(Pipeline) 组件负责这个工作。

items

先要编辑一下 items.py 里的类,默认会帮我们生成一个类,并有简单的注释。必须要处理2个数据 title 和 href ,则改写 items.py 如下:

import scrapy

class PeppascrapyItem(scrapy.Item):

# define the fields for your item here like:

# name = scrapy.Field()

title = scrapy.Field()

href = scrapy.Field()

然后去修改之前的 parse 方法,导入上面的类,把要处理的数据传递进去生成实例,然后 yield :

from PeppaScrapy.items import PeppascrapyItem

class SpiderLabSpider(scrapy.Spider):

name = 'spider_lab'

allowed_domains = ['lab.scrapyd.cn']

start_urls = ['http://lab.scrapyd.cn/']

def parse(self, response):

print(response.url)

items = Selector(response).xpath(

'//div[@id="body"]//div[@id="main"]/div[@class="quote post"]')

for item in items:

title = item.xpath('./span[@class="text"]/text()').extract_first()

href = item.xpath('./span/a/@href').extract_first()

yield PeppascrapyItem(title=title, href=href)

上面这段代码,只需要注意最后3行。把要保存的数据用items.py里的类实例化后,yield返回。

回顾下流程,之前yield返回给 scrapy.Request ,就是把数据返回给调度器继续继续爬取

这里yield返回给 scrapy.Item ,就是 Item Pipeline 里的 Item 进入数据的处理。

在 Item 里只是把数据传递出来,数据的处理则在 Pipeline 里。

如果有多处数据要返回,则可以自定义多个 scrapy.Item 类,来做数据的格式化处理。

Pipline

还有一个 pipelines.py 文件,默认里面只有一个 return ,但是传入2个参数 item 和 spider,先打印看看:

class PeppascrapyPipeline(object):

def process_item(self, item, spider):

print(item)

print(spider)

return item

只是编写处理方法还不够,这个方法需要注册。在settings.py文件里,默认写好了注册的方法,只需要把注释去掉。ITEM_PIPELINES 的 key 就是要注册的方法,而 value 则是优先级。理论上字典没有顺序,优先级小的方法先执行:

ITEM_PIPELINES = {

'PeppaScrapy.pipelines.PeppascrapyPipeline': 300,

}

最后返回的item是个字典,我们报错的变量名是key,值就是value。而spider则是这个爬虫 scrapy.Spider 对象。

执行多个操作

这里一个类就是执行一个操作,如果对返回的数据要有多次操作,也可以多定义几个类,然后注册上即可。

每次操作的item,就是上一次操作最后 return item 传递下来的。第一次操作的item则是从 scrapy.Item 传过来的。所以也可以对item进行处理,然后之后的操作就是在上一次操作对item的修改之上进行的。所以也可以想return什么就return什么,就是给下一个操作处理的数据。

绑定特定的爬虫

Pipline并没有和特定的爬虫进行绑定,也就是所有的爬虫都会依次执行所有的Pipline。对于特定爬虫要做得特定的操作,可以在process_item方法里通过参数spider的spider.name进行判断。

DropItem

接着讲上面的执行多个操作。如果在某个地方要终止之后所有的操作,则可以用 DropItem 。用法如下:

from scrapy.exceptions import DropItem

class PeppascrapyPipeline(object):

def process_item(self, item, spider):

print(item)

raise DropItem()

这样对这组数据的操作就终止了。一般应该把这句放在某个条件的分支里。

初始化操作

Pipeline 这个类里,还可以定义更多方法。除了上面的处理方法,还有另外3个方法,其中一个是类方法。所有的方法名都不能修改,具体如下:

class PeppascrapyPipeline(object):

def __init__(self, value):

self.value = value

def process_item(self, item, spider):

"""操作并进行持久化"""

print(item)

# 表示将item丢弃,不会被后续pipeline处理

raise DropItem()

# print(spider)

# return item 给后续的pipeline继续处理

# return item

@classmethod

def from_crawler(cls, crawler):

"""初始化时候,用于创建pipeline对象"""

val = crawler.settings.get('BOT_NAME')

# getint 方法可以直接获取 int 参数

# val = crawler.settings.getint('DEPTH_LIMIT')

return cls(val)

def open_spider(self,spider):

"""爬虫开始执行时,调用"""

print('START')

def close_spider(self,spider):

"""爬虫关闭时,被调用"""

print('OVER')

类方法 from_crawler 是用于创建pipeline对象的。主要是接收了crawler参数,可以获取到settings里的参数然后传给构造方法。比如这里获取了settings.py里的值传给了对象。

另外2个方法 open_spider 和 close_spider ,是在爬虫开始和关闭时执行的。即使爬虫有多次返回,处理方法要调用多次,但是这2个方法都只会调用一次。这2个方法是在爬虫 scrapy.Spider 开始和关闭的时候各执行一次的。而不是第一次返回数据处理和最后一次数据处理完毕。

打开文件的操作

以写入文件为例,写入一段数据需要3步:打开文件,写入,关闭文件。如果把这3不都写在 process_item 方法里,则会有多次的打开和关闭操作。正确的做法是,打开文件在 open_spider 方法里执行,写入还是在 process_item 方法里每次返回都可以写入,最后在 close_spider 方法里关闭文件。

中间件

默认有一个 middlewares.py 文件,里面默认创建了2个类,分别是爬虫中间件和下载中间件

爬虫中间件

class PeppascrapySpiderMiddleware(object):

# Not all methods need to be defined. If a method is not defined,

# scrapy acts as if the spider middleware does not modify the

# passed objects.

@classmethod

def from_crawler(cls, crawler):

# This method is used by Scrapy to create your spiders.

s = cls()

crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)

return s

def process_spider_input(self, response, spider):

"""下载完成,执行,然后交给parse处理"""

# Called for each response that goes through the spider

# middleware and into the spider.

# Should return None or raise an exception.

return None

def process_spider_output(self, response, result, spider):

"""spider处理完成,返回时调用

返回Request或者Item(字典也行,Item本身也是个字典)

Request就是给调度器继续处理

Item就是给项目管道保存

"""

# Called with the results returned from the Spider, after

# it has processed the response.

# Must return an iterable of Request, dict or Item objects.

for i in result:

yield i

def process_spider_exception(self, response, exception, spider):

"""异常调用"""

# Called when a spider or process_spider_input() method

# (from other spider middleware) raises an exception.

# Should return either None or an iterable of Response, dict

# or Item objects.

pass

def process_start_requests(self, start_requests, spider):

"""爬虫启动时调用"""

# Called with the start requests of the spider, and works

# similarly to the process_spider_output() method, except

# that it doesn’t have a response associated.

# Must return only requests (not items).

for r in start_requests:

yield r

def spider_opened(self, spider):

spider.logger.info('Spider opened: %s' % spider.name)

爬虫中间件这里要注意下 process_spider_output() 返回的内容之后是要交给调度器继续爬取的,或者是交给项目管道做保存操作。所以返回的可以是 Request 或者是 Item 。

下载器中间件

class PeppascrapyDownloaderMiddleware(object):

# Not all methods need to be defined. If a method is not defined,

# scrapy acts as if the downloader middleware does not modify the

# passed objects.

@classmethod

def from_crawler(cls, crawler):

# This method is used by Scrapy to create your spiders.

s = cls()

crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)

return s

def process_request(self, request, spider):

"""请求需要被下载时,经过所有下载器中间件的process_request调用"""

# Called for each request that goes through the downloader

# middleware.

# Must either:

# - return None: continue processing this request

# - or return a Response object

# - or return a Request object

# - or raise IgnoreRequest: process_exception() methods of

# installed downloader middleware will be called

return None

def process_response(self, request, response, spider):

"""spider处理完成,返回时调用"""

# Called with the response returned from the downloader.

# Must either;

# - return a Response object

# - return a Request object

# - or raise IgnoreRequest

return response

def process_exception(self, request, exception, spider):

"""异常处理

当下载处理器(download handler)

或 process_request() (下载中间件)抛出异常时执行

"""

# Called when a download handler or a process_request()

# (from other downloader middleware) raises an exception.

# Must either:

# - return None: continue processing this exception

# - return a Response object: stops process_exception() chain

# - return a Request object: stops process_exception() chain

pass

def spider_opened(self, spider):

spider.logger.info('Spider opened: %s' % spider.name)

process_request方法

对不同的返回值,回有不同的效果:

一般返回None,继续后面的中间件或者下载。这里可以修改一下请求头信息。比如,在请求头里添加代理的设置,然后再让后续的操作来执行。

返回Response,下载器就是要去下载生成Response。这里直接返回Response就相当于已经下载完成了。所以之后不再是执行下载了,而是返回给中间件里的process_response方法,执行下载完成后的操作。比如,可以不用默认的下载器来下载。到这里自己用Request模块写段代码去下载,然后创建一个scrap.http.Eesponse对象,把内容填进去返回。

返回Request,调度器就是生成一个个的Request,然后调度执行。如果这里返回了Request,就会停止这次的执行,把Request放回调度器,等待下一次被调度执行。在process_response方法里返回Request也是一样的效果,只是这里是在下载前要重新调度,那个是在下载后。

自定义操作

自定制命令

自定制命令

一、在spiders同级创建任意目录,如:commands

二、在目录里创建 crawlall.py 文件,名字任意取,这个文件名将来就是执行这段代码的命令

下面是一个启动spiders里所有爬虫的代码:

from scrapy.commands import ScrapyCommand

from scrapy.utils.project import get_project_settings

class Command(ScrapyCommand):

requires_project = True

def syntax(self):

return '[options]'

def short_desc(self):

return 'Runs all of the spiders'

def run(self, args, opts):

spider_list = self.crawler_process.spiders.list()

for name in spider_list:

self.crawler_process.crawl(name, **opts.__dict__)

self.crawler_process.start()

三、在 settings.py 中添加配置 COMMANDS_MODULE = '项目名称.目录名称' ,比如:

COMMANDS_MODULE = "PeppaScrapy.commands"

四、执行命令: scrapy crawlall

自定义扩展

利用信号在指定位置注册制定操作。

自定义的型号要写在写一类,然后在settings里注册。默认的配置文件里是有EXTENSIONS的,注释掉了,这里就放开注释然后改一下:

# Enable or disable extensions

# See https://doc.scrapy.org/en/latest/topics/extensions.html

EXTENSIONS = {

# 'scrapy.extensions.telnet.TelnetConsole': None,

'PeppaScrapy.extensions.MyExtension': 100

}

根据上面的操作,就是创建 extensions.py 文件,然后写一个 MyExtension 的类:

# PeppaScrapy/extensions.py 文件

from scrapy import signals

class MyExtension(object):

def __init__(self, value):

self.value = value

@classmethod

def from_crawler(cls, crawler):

val = crawler.settings.get('BOT_NAME')

ext = cls(val)

# 注册你的方法和信息

crawler.signals.connect(ext.spider_start, signal=signals.spider_opened)

crawler.signals.connect(ext.spider_stop, signal=signals.spider_closed)

return ext

# 写你要执行的方法

def spider_start(self, spider):

print('open')

def spider_stop(self, spider):

print('close')

所有的信号

上面的例子里用到了 spider_opened 和 spider_closed 这2个信号。

在 scrapy/signals.py 里可以查到所有的信号:

engine_started = object()

engine_stopped = object()

spider_opened = object()

spider_idle = object()

spider_closed = object()

spider_error = object()

request_scheduled = object()

request_dropped = object()

response_received = object()

response_downloaded = object()

item_scraped = object()

item_dropped = object()

配置文件详细

# -*- coding: utf-8 -*-

# Scrapy settings for step8_king project

#

# For simplicity, this file contains only settings considered important or

# commonly used. You can find more settings consulting the documentation:

#

# http://doc.scrapy.org/en/latest/topics/settings.html

# http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html

# http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html

# 1. 爬虫名称

BOT_NAME = 'step8_king'

# 2. 爬虫应用路径

SPIDER_MODULES = ['step8_king.spiders']

NEWSPIDER_MODULE = 'step8_king.spiders'

# Crawl responsibly by identifying yourself (and your website) on the user-agent

# 3. 客户端 user-agent请求头

# USER_AGENT = 'step8_king (+http://www.yourdomain.com)'

# Obey robots.txt rules

# 4. 禁止爬虫配置

# ROBOTSTXT_OBEY = False

# Configure maximum concurrent requests performed by Scrapy (default: 16)

# 5. 并发请求数

# CONCURRENT_REQUESTS = 4

# Configure a delay for requests for the same website (default: 0)

# See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay

# See also autothrottle settings and docs

# 6. 延迟下载秒数

# DOWNLOAD_DELAY = 2

# The download delay setting will honor only one of:

# 7. 单域名访问并发数,并且延迟下次秒数也应用在每个域名

# CONCURRENT_REQUESTS_PER_DOMAIN = 2

# 单IP访问并发数,如果有值则忽略:CONCURRENT_REQUESTS_PER_DOMAIN,并且延迟下次秒数也应用在每个IP

# CONCURRENT_REQUESTS_PER_IP = 3

# Disable cookies (enabled by default)

# 8. 是否支持cookie,cookiejar进行操作cookie

# COOKIES_ENABLED = True

# COOKIES_DEBUG = True

# Disable Telnet Console (enabled by default)

# 9. Telnet用于查看当前爬虫的信息,操作爬虫等...

# 使用telnet ip port ,然后通过命令操作

# TELNETCONSOLE_ENABLED = True

# TELNETCONSOLE_HOST = '127.0.0.1'

# TELNETCONSOLE_PORT = [6023,]

# 10. 默认请求头

# Override the default request headers:

# DEFAULT_REQUEST_HEADERS = {

# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',

# 'Accept-Language': 'en',

# }

# Configure item pipelines

# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html

# 11. 定义pipeline处理请求

# ITEM_PIPELINES = {

# 'step8_king.pipelines.JsonPipeline': 700,

# 'step8_king.pipelines.FilePipeline': 500,

# }

# 12. 自定义扩展,基于信号进行调用

# Enable or disable extensions

# See http://scrapy.readthedocs.org/en/latest/topics/extensions.html

# EXTENSIONS = {

# # 'step8_king.extensions.MyExtension': 500,

# }

# 13. 爬虫允许的最大深度,可以通过meta查看当前深度;0表示无深度

# DEPTH_LIMIT = 3

# 14. 爬取时,0表示深度优先Lifo(默认);1表示广度优先FiFo

# 后进先出,深度优先

# DEPTH_PRIORITY = 0

# SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleLifoDiskQueue'

# SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.LifoMemoryQueue'

# 先进先出,广度优先

# DEPTH_PRIORITY = 1

# SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'

# SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'

# 15. 调度器队列

# SCHEDULER = 'scrapy.core.scheduler.Scheduler'

# from scrapy.core.scheduler import Scheduler

# 16. 访问URL去重

# DUPEFILTER_CLASS = 'step8_king.duplication.RepeatUrl'

# Enable and configure the AutoThrottle extension (disabled by default)

# See http://doc.scrapy.org/en/latest/topics/autothrottle.html

"""

17. 自动限速算法

from scrapy.contrib.throttle import AutoThrottle

自动限速设置

1. 获取最小延迟 DOWNLOAD_DELAY

2. 获取最大延迟 AUTOTHROTTLE_MAX_DELAY

3. 设置初始下载延迟 AUTOTHROTTLE_START_DELAY

4. 当请求下载完成后,获取其"连接"时间 latency,即:请求连接到接受到响应头之间的时间

5. 用于计算的... AUTOTHROTTLE_TARGET_CONCURRENCY

target_delay = latency / self.target_concurrency

new_delay = (slot.delay + target_delay) / 2.0 # 表示上一次的延迟时间

new_delay = max(target_delay, new_delay)

new_delay = min(max(self.mindelay, new_delay), self.maxdelay)

slot.delay = new_delay

"""

# 开始自动限速

# AUTOTHROTTLE_ENABLED = True

# The initial download delay

# 初始下载延迟

# AUTOTHROTTLE_START_DELAY = 5

# The maximum download delay to be set in case of high latencies

# 最大下载延迟

# AUTOTHROTTLE_MAX_DELAY = 10

# The average number of requests Scrapy should be sending in parallel to each remote server

# 平均每秒并发数

# AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0

# Enable showing throttling stats for every response received:

# 是否显示

# AUTOTHROTTLE_DEBUG = True

# Enable and configure HTTP caching (disabled by default)

# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings

"""

18. 启用缓存

目的用于将已经发送的请求或相应缓存下来,以便以后使用

from scrapy.downloadermiddlewares.httpcache import HttpCacheMiddleware

from scrapy.extensions.httpcache import DummyPolicy

from scrapy.extensions.httpcache import FilesystemCacheStorage

"""

# 是否启用缓存策略

# HTTPCACHE_ENABLED = True

# 缓存策略:所有请求均缓存,下次在请求直接访问原来的缓存即可

# HTTPCACHE_POLICY = "scrapy.extensions.httpcache.DummyPolicy"

# 缓存策略:根据Http响应头:Cache-Control、Last-Modified 等进行缓存的策略

# HTTPCACHE_POLICY = "scrapy.extensions.httpcache.RFC2616Policy"

# 缓存超时时间

# HTTPCACHE_EXPIRATION_SECS = 0

# 缓存保存路径

# HTTPCACHE_DIR = 'httpcache'

# 缓存忽略的Http状态码

# HTTPCACHE_IGNORE_HTTP_CODES = []

# 缓存存储的插件

# HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

"""

19. 代理,需要在环境变量中设置

from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware

方式一:使用默认

os.environ

{

http_proxy:http://root:woshiniba@192.168.11.11:9999/

https_proxy:http://192.168.11.11:9999/

}

方式二:使用自定义下载中间件

def to_bytes(text, encoding=None, errors='strict'):

if isinstance(text, bytes):

return text

if not isinstance(text, six.string_types):

raise TypeError('to_bytes must receive a unicode, str or bytes '

'object, got %s' % type(text).__name__)

if encoding is None:

encoding = 'utf-8'

return text.encode(encoding, errors)

class ProxyMiddleware(object):

def process_request(self, request, spider):

PROXIES = [

{'ip_port': '111.11.228.75:80', 'user_pass': ''},

{'ip_port': '120.198.243.22:80', 'user_pass': ''},

{'ip_port': '111.8.60.9:8123', 'user_pass': ''},

{'ip_port': '101.71.27.120:80', 'user_pass': ''},

{'ip_port': '122.96.59.104:80', 'user_pass': ''},

{'ip_port': '122.224.249.122:8088', 'user_pass': ''},

]

proxy = random.choice(PROXIES)

if proxy['user_pass'] is not None:

request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])

encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass']))

request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass)

print "**************ProxyMiddleware have pass************" + proxy['ip_port']

else:

print "**************ProxyMiddleware no pass************" + proxy['ip_port']

request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])

DOWNLOADER_MIDDLEWARES = {

'step8_king.middlewares.ProxyMiddleware': 500,

}

"""

"""

20. Https访问

Https访问时有两种情况:

1. 要爬取网站使用的可信任证书(默认支持)

DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"

DOWNLOADER_CLIENTCONTEXTFACTORY = "scrapy.core.downloader.contextfactory.ScrapyClientContextFactory"

2. 要爬取网站使用的自定义证书

DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"

DOWNLOADER_CLIENTCONTEXTFACTORY = "step8_king.https.MySSLFactory"

# https.py

from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory

from twisted.internet.ssl import (optionsForClientTLS, CertificateOptions, PrivateCertificate)

class MySSLFactory(ScrapyClientContextFactory):

def getCertificateOptions(self):

from OpenSSL import crypto

v1 = crypto.load_privatekey(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.key.unsecure', mode='r').read())

v2 = crypto.load_certificate(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.pem', mode='r').read())

return CertificateOptions(

privateKey=v1, # pKey对象

certificate=v2, # X509对象

verify=False,

method=getattr(self, 'method', getattr(self, '_ssl_method', None))

)

其他:

相关类

scrapy.core.downloader.handlers.http.HttpDownloadHandler

scrapy.core.downloader.webclient.ScrapyHTTPClientFactory

scrapy.core.downloader.contextfactory.ScrapyClientContextFactory

相关配置

DOWNLOADER_HTTPCLIENTFACTORY

DOWNLOADER_CLIENTCONTEXTFACTORY

"""

"""

21. 爬虫中间件

class SpiderMiddleware(object):

def process_spider_input(self,response, spider):

'''

下载完成,执行,然后交给parse处理

:param response:

:param spider:

:return:

'''

pass

def process_spider_output(self,response, result, spider):

'''

spider处理完成,返回时调用

:param response:

:param result:

:param spider:

:return: 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)

'''

return result

def process_spider_exception(self,response, exception, spider):

'''

异常调用

:param response:

:param exception:

:param spider:

:return: None,继续交给后续中间件处理异常;含 Response 或 Item 的可迭代对象(iterable),交给调度器或pipeline

'''

return None

def process_start_requests(self,start_requests, spider):

'''

爬虫启动时调用

:param start_requests:

:param spider:

:return: 包含 Request 对象的可迭代对象

'''

return start_requests

内置爬虫中间件:

'scrapy.contrib.spidermiddleware.httperror.HttpErrorMiddleware': 50,

'scrapy.contrib.spidermiddleware.offsite.OffsiteMiddleware': 500,

'scrapy.contrib.spidermiddleware.referer.RefererMiddleware': 700,

'scrapy.contrib.spidermiddleware.urllength.UrlLengthMiddleware': 800,

'scrapy.contrib.spidermiddleware.depth.DepthMiddleware': 900,

"""

# from scrapy.contrib.spidermiddleware.referer import RefererMiddleware

# Enable or disable spider middlewares

# See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html

SPIDER_MIDDLEWARES = {

# 'step8_king.middlewares.SpiderMiddleware': 543,

}

"""

22. 下载中间件

class DownMiddleware1(object):

def process_request(self, request, spider):

'''

请求需要被下载时,经过所有下载器中间件的process_request调用

:param request:

:param spider:

:return:

None,继续后续中间件去下载;

Response对象,停止process_request的执行,开始执行process_response

Request对象,停止中间件的执行,将Request重新调度器

raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception

'''

pass

def process_response(self, request, response, spider):

'''

spider处理完成,返回时调用

:param response:

:param result:

:param spider:

:return:

Response 对象:转交给其他中间件process_response

Request 对象:停止中间件,request会被重新调度下载

raise IgnoreRequest 异常:调用Request.errback

'''

print('response1')

return response

def process_exception(self, request, exception, spider):

'''

当下载处理器(download handler)或 process_request() (下载中间件)抛出异常

:param response:

:param exception:

:param spider:

:return:

None:继续交给后续中间件处理异常;

Response对象:停止后续process_exception方法

Request对象:停止中间件,request将会被重新调用下载

'''

return None

默认下载中间件

{

'scrapy.contrib.downloadermiddleware.robotstxt.RobotsTxtMiddleware': 100,

'scrapy.contrib.downloadermiddleware.httpauth.HttpAuthMiddleware': 300,

'scrapy.contrib.downloadermiddleware.downloadtimeout.DownloadTimeoutMiddleware': 350,

'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': 400,

'scrapy.contrib.downloadermiddleware.retry.RetryMiddleware': 500,

'scrapy.contrib.downloadermiddleware.defaultheaders.DefaultHeadersMiddleware': 550,

'scrapy.contrib.downloadermiddleware.redirect.MetaRefreshMiddleware': 580,

'scrapy.contrib.downloadermiddleware.httpcompression.HttpCompressionMiddleware': 590,

'scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware': 600,

'scrapy.contrib.downloadermiddleware.cookies.CookiesMiddleware': 700,

'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 750,

'scrapy.contrib.downloadermiddleware.chunked.ChunkedTransferMiddleware': 830,

'scrapy.contrib.downloadermiddleware.stats.DownloaderStats': 850,

'scrapy.contrib.downloadermiddleware.httpcache.HttpCacheMiddleware': 900,

}

"""

# from scrapy.contrib.downloadermiddleware.httpauth import HttpAuthMiddleware

# Enable or disable downloader middlewares

# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html

# DOWNLOADER_MIDDLEWARES = {

# 'step8_king.middlewares.DownMiddleware1': 100,

# 'step8_king.middlewares.DownMiddleware2': 500,

# }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/74682
推荐阅读
相关标签
  

闽ICP备14008679号