scrapy是python最有名的爬虫框架之一,能够很方便的进行web抓取,而且提供了很强的定制型,这里记录简单学习的过程和在实际应用中会遇到的一些常见问题css
在安装scrapy以前有一些依赖须要安装,不然可能会安装失败,scrapy的选择器依赖于lxml
,还有Twisted
网络引擎,下面是ubuntu下安装的过程html
# 1. 安装xml依赖库 $ sudo apt-get install libxml2 libxml2-dev $ sudo apt-get install libxslt1-dev $ sudo apt-get install python-libxml2 # 2. 安装lxml $ sudo pip install lxml # 3. 安装Twisted(版本能够换成最新的),用pip也能够,若是失败的话下载源码安装,以下 $ wget https://pypi.python.org/packages/6b/23/8dbe86fc83215015e221fbd861a545c6ec5c9e9cd7514af114d1f64084ab/Twisted-16.4.1.tar.bz2#md5=c6d09bdd681f538369659111f079c29d $ tar xjf Twisted-16.4.1.tar.bz2 $ cd Twisted-16.4.1 $ sudo python setup.py install # 3. 安装scrapy $ sudo pip install scrapy
# 安装xml依赖库 $ xcode-select —install # 其实相关依赖pip会自动帮咱们装上 $ pip install scrapy
mac下安装有时候会失败,建议使用virtualenv
安装在独立的环境下,能够减小一些问题,由于mac系统自带python,例如一些依赖库依赖的一些新的版本,而升级新版本会把旧版本卸载掉,卸载可能会有权限的问题python
咱们可使用命令行初始化一个项目linux
$ scrapy startproject tutorial
这里能够查看scrapy更多其余的命令git
初始化完成后,咱们获得下面目录结构github
scrapy.cfg: 项目的配置文件 tutorial/: 该项目的python模块, 在这里添加代码 items.py: 项目中的item文件 pipelines.py: 项目中的pipelines文件. settings.py: 项目全局设置文件. spiders/ 爬虫模块目录
咱们先看一下scrapy的处理流程web
scrapy由下面几个部分组成正则表达式
spiders
:爬虫模块,负责配置须要爬取的数据和爬取规则,以及解析结构化数据数据库
items
:定义咱们须要的结构化数据,使用至关于dict
pipelines
:管道模块,处理spider模块分析好的结构化数据,如保存入库等
middlewares
:中间件,至关于钩子,能够对爬取先后作预处理,如修改请求header,url过滤等
咱们先来看一个例子,在spiders
目录下新建一个模块DmozSpider.py
import scrapy class DmozSpider(scrapy.Spider): # 必须定义 name = "dmoz" # 初始urls start_urls = [ "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/", "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/" ] # 默认response处理函数 def parse(self, response): # 把结果写到文件中 filename = response.url.split("/")[-2] with open(filename, 'wb') as f: f.write(response.body)
打开终端进入根目录,执行下面命令
$ scrapy crawl dmoz
爬虫开始爬取start_urls定义的url,并输出到文件中,最后输出爬去报告,会输出爬取得统计结果
2016-09-13 10:36:43 [scrapy] INFO: Spider opened 2016-09-13 10:36:43 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) 2016-09-13 10:36:43 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023 2016-09-13 10:36:44 [scrapy] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: None) 2016-09-13 10:36:45 [scrapy] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: None) 2016-09-13 10:36:45 [scrapy] INFO: Closing spider (finished) 2016-09-13 10:36:45 [scrapy] INFO: Dumping Scrapy stats: {'downloader/request_bytes': 548, 'downloader/request_count': 2, 'downloader/request_method_count/GET': 2, 'downloader/response_bytes': 16179, 'downloader/response_count': 2, 'downloader/response_status_count/200': 2, 'finish_reason': 'finished', 'finish_time': datetime.datetime(2016, 9, 13, 2, 36, 45, 585113), 'log_count/DEBUG': 3, 'log_count/INFO': 7, 'response_received_count': 2, 'scheduler/dequeued': 2, 'scheduler/dequeued/memory': 2, 'scheduler/enqueued': 2, 'scheduler/enqueued/memory': 2, 'start_time': datetime.datetime(2016, 9, 13, 2, 36, 43, 935790)} 2016-09-13 10:36:45 [scrapy] INFO: Spider closed (finished)
这里咱们完成了简单的爬取和保存的操做,会在根目录生成两个文件Resources
和Books
每次进入控制台运行爬虫仍是比较麻烦的,并且很差调试,咱们能够经过CrawlerProcess
经过代码运行爬虫,新建一个模块run.py
from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from spiders.DmozSpider import DmozSpider # 获取settings.py模块的设置 settings = get_project_settings() process = CrawlerProcess(settings=settings) # 能够添加多个spider # process.crawl(Spider1) # process.crawl(Spider2) process.crawl(DmozSpider) # 启动爬虫,会阻塞,直到爬取完成 process.start()
参考:http://doc.scrapy.org/en/latest/topics/practices.html#run-scrapy-from-a-script
如上面的DmozSpider
类,爬虫类继承自scrapy.Spider
,用于构造Request
对象给Scheduler
属性
name
:爬虫的名字,必须惟一(若是在控制台使用的话,必须配置)
start_urls
:爬虫初始爬取的连接列表
parse
:response结果处理函数
custom_settings
:自定义配置,覆盖settings.py
中的默认配置
方法
start_requests
:启动爬虫的时候调用,默认是调用make_requests_from_url
方法爬取start_urls
的连接,能够在这个方法里面定制,若是重写了该方法,start_urls默认将不会被使用,能够在这个方法里面定制一些自定义的url,如登陆,从数据库读取url等,本方法返回Request对象
make_requests_from_url
:默认由start_requests
调用,能够配置Request对象,返回Request对象
parse
:response到达spider的时候默认调用,若是在Request对象配置了callback函数,则不会调用,parse方法能够迭代返回Item
或Request
对象,若是返回Request对象,则会进行增量爬取
每一个请求都是一个Request对象,Request对象定义了请求的相关信息(url
, method
, headers
, body
, cookie
, priority
)和回调的相关信息(meta
, callback
, dont_filter
, errback
),一般由spider迭代返回
其中meta
至关于附加变量,能够在请求完成后经过response.meta
访问
请求完成后,会经过Response
对象发送给spider处理,经常使用属性有(url
, status
, headers
, body
, request
, meta
, )
详细介绍参考官网
https://doc.scrapy.org/en/latest/topics/request-response.html#request-objects
https://doc.scrapy.org/en/latest/topics/request-response.html#response-objects
看下面这个例子
from scrapy import Spider from scrapy import Request class TestSpider(Spider): name = 'test' start_urls = [ "http://www.qq.com/", ] def login_parse(self, response): ''' 若是登陆成功,手动构造请求Request迭代返回 ''' print response for i in range(0, 10): yield Request('http://www.example.com/list/1?page={0}'.format(i)) def start_requests(self): ''' 覆盖默认的方法(忽略start_urls),返回登陆请求页,制定处理函数为login_parse ''' return Request('http://www.example.com/login', method="POST" body='username=bomo&pwd=123456', callback=self.login_parse) def parse(self, response): ''' 默认请求处理函数 ''' print response
上面咱们只是爬取了网页的html文本,对于爬虫,咱们须要明确咱们须要爬取的结构化数据,须要对原文本进行解析,解析的方法一般有下面这些
普通文本操做
正则表达式:re
Dom树操做:BeautifulSoup
XPath选择器:lxml
scrapy默认支持选择器的功能,自带的选择器构建与lxml之上,并对其进行了改进,使用起来更为简洁明了
XPpath是标准的XML文档查询语言,能够用于查询XML文档中的节点和内容,关于XPath语法,能够参见这里
先看一个例子,经过html或xml构造Selector对象,而后经过xpath查询节点,并解析出节点的内容
from scrapy import Selector html = '<html><body><span>good</span><span>buy</span></body></html>' sel = Selector(text=html) nodes = sel.xpath('//span') for node in nodes: print node.extract()
Selector至关于节点,经过xpath去到子节点集合(SelectorList),能够继续搜索,经过extract
方法能够取出节点的值,extract
方法也能够做用于SelectorList,对于SelectorList能够经过extract_first
取出第一个节点的值
经过text()
取出节点的内容
经过@href
去除节点属性值(这里是取出href
属性的值)
直接对节点取值,则是输出节点的字符串
除了XPath选择器,scrapy还支持css选择器
html = """ <html> <body> <span>good</span> <span>buy</span> <ul> <li class="video_part_lists">aa<li> <li class="video_part_lists">bb<li> <li class="audio_part_lists">cc<li> <li class="video_part_lists"> <a href="/">主页</a> <li> </ul> </body> </html> """ sel = Selector(text=html) # 选择class为video_part_lists的li节点 lis = sel.css('li.video_part_lists') for li in lis: # 选择a节点的属性 print li.css('a::attr(href)').extract()
关于css选择器更多的规则,能够见w3c官网
上面咱们只是爬取了网页的html文本,对于爬虫,咱们须要明确咱们须要爬取的结构化数据,咱们定义一个item存储分类信息,scrapy的item继承自scrapy.Item
from scrapy import Item, Field class DmozItem(Item): title = Field() link = Field() desc = Field()
scrapy.Item
的用法与python中的字典用法基本同样,只是作了一些安全限制,属性定义使用Field,这里只是进行了声明,而不是真正的属性,使用的时候经过键值对操做,不支持属性访问
what, 好坑爹,这意味着全部的属性赋值都得用字符串了,这里有解释(仍是没太明白)
修改DmozSpider的parse方法
class DmozSpider(scrapy.Spider): ... def parse(self, response): for sel in response.xpath('//ul/li'): dmoz_item = DmozItem() dmoz_item['title'] = sel.xpath('a/text()').extract() dmoz_item['link'] = sel.xpath('a/@href').extract() dmoz_item['desc'] = sel.xpath('text()').extract() print dmoz_item
spider负责爬虫的配置,item负责声明结构化数据,而对于数据的处理,在scrapy中使用管道的方式进行处理,只要注册过的管道均可以处理item数据(处理,过滤,保存)
下面看看管道的声明方式,这里定义一个预处理管道PretreatmentPipeline.py
,若是item的title为None,则设置为空字符串
class PretreatmentPipeline(object): def process_item(self, item, spider): if item['title']: # 不让title为空 item['title'] = '' return item
再定义一个过滤重复数据的管道DuplicatesPipeline.py
,当link重复,则丢弃
from scrapy.exceptions import DropItem class DuplicatesPipeline(object): def __init__(self): self.links = set() def process_item(self, item, spider): if item['link'] in self.links: # 跑出DropItem表示丢掉数据 raise DropItem("Duplicate item found: %s" % item) else: self.links.add(item['link']) return item
最后能够定义一个保存数据的管道,能够把数据保存到数据库中
from scrapy.exceptions import DropItem from Database import Database class DatabasePipeline(object): def __init__(self): self.db = Database def process_item(self, item, spider): if self.db.item_exists(item['id']): self.db.update_item(item) else: self.db.insert_item(item)
定义好管道以后咱们须要配置到爬虫上,咱们在settings.py
模块中配置,后面的数字表示管道的顺序
ITEM_PIPELINES = { 'pipelines.DuplicatesPipeline.DuplicatesPipeline': 1, 'pipelines.PretreatmentPipeline.PretreatmentPipeline': 2, }
咱们也能够为spider配置单独的pipeline
class TestSpider(Spider): # 自定义配置 custom_settings = { # item处理管道 'ITEM_PIPELINES': { 'tutorial.pipelines.FangDetailPipeline.FangDetailPipeline': 1, }, } ...
除了process_item
方法外,pipeline还有open_spider
和spider_closed
两个方法,在爬虫启动和关闭的时候调用
爬虫的一般须要在一个网页里面爬去其余的连接,而后一层一层往下爬,scrapy提供了LinkExtractor类用于对网页连接的提取,使用LinkExtractor须要使用CrawlSpider
爬虫类中,CrawlSpider
与Spider
相比主要是多了rules
,能够添加一些规则,先看下面这个例子,爬取链家网的连接
from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class LianjiaSpider(CrawlSpider): name = "lianjia" allowed_domains = ["lianjia.com"] start_urls = [ "http://bj.lianjia.com/ershoufang/" ] rules = [ # 匹配正则表达式,处理下一页 Rule(LinkExtractor(allow=(r'http://bj.lianjia.com/ershoufang/pg\s+$',)), callback='parse_item'), # 匹配正则表达式,结果加到url列表中,设置请求预处理函数 # Rule(FangLinkExtractor(allow=('http://www.lianjia.com/client/', )), follow=True, process_request='add_cookie') ] def parse_item(self, response): # 这里与以前的parse方法同样,处理 pass
Role对象有下面参数
link_extractor
:连接提取规则
callback
:link_extractor提取的连接的请求结果的回调
cb_kwargs
:附加参数,能够在回调函数中获取到
follow
:表示提取的连接请求完成后是否还要应用当前规则(boolean),若是为False
则不会对提取出来的网页进行进一步提取,默认为False
process_links
:处理全部的连接的回调,用于处理从response提取的links,一般用于过滤(参数为link列表)
process_request
:连接请求预处理(添加header或cookie等)
LinkExtractor经常使用的参数有:
allow
:提取知足正则表达式的连接
deny
:排除正则表达式匹配的连接(优先级高于allow
)
allow_domains
:容许的域名(能够是str
或list
)
deny_domains
:排除的域名(能够是str
或list
)
restrict_xpaths
:提取知足XPath选择条件的连接(能够是str
或list
)
restrict_css
:提取知足css选择条件的连接(能够是str
或list
)
tags
:提取指定标签下的连接,默认从a
和area
中提取(能够是str
或list
)
attrs
:提取知足拥有属性的连接,默认为href
(类型为list
)
unique
:连接是否去重(类型为boolean
)
process_value
:值处理函数(优先级大于allow
)
关于LinkExtractor的详细参数介绍见官网
注意:若是使用rules规则,请不要覆盖或重写
CrawlSpider
的parse
方法,不然规则会失效,可使用parse_start_urls
方法
从最开始的流程图能够看到,爬去一个资源连接的流程,首先咱们配置spider相关的爬取信息,在启动爬取实例后,scrapy_engine
从Spider取出Request
(通过SpiderMiddleware
),而后丢给Scheduler(通过SchedulerMiddleware
),Scheduler接着把请求丢给Downloader(通过DownloadMiddlware
),Downloader把请求结果丢还给Spider,而后Spider把分析好的结构化数据丢给Pipeline,Pipeline进行分析保存或丢弃,这里面有4个角色
scrapy有下面三种middlewares
SpiderMiddleware
:一般用于配置爬虫相关的属性,引用连接设置,Url长度限制,成功状态码设置,爬取深度设置,爬去优先级设置等
DownloadMiddlware
:一般用于处理下载以前的预处理,如请求Header(Cookie,User-Agent),登陆验证处理,重定向处理,代理服务器处理,超时处理,重试处理等
SchedulerMiddleware
(已经废弃):为了简化框架,调度器中间件已经被废弃,使用另外两个中间件已经够用了
爬虫中间件有下面几个方法
process_spider_input
:当response经过spider的时候被调用,返回None(继续给其余中间件处理)或抛出异常(不会给其余中间件处理,当成异常处理)
process_spider_output
:当spider有item或Request输出的时候调动
process_spider_exception
:处理出现异常时调用
process_start_requests
:spider当开始请求Request的时候调用
下面是scrapy自带的一些中间件(在scrapy.spidermiddlewares
命名空间下)
UrlLengthMiddleware
RefererMiddleware
OffsiteMiddleware
HttpErrorMiddleware
DepthMiddleware
咱们本身实现一个SpiderMiddleware
TODO
参考连接:http://doc.scrapy.org/en/latest/topics/spider-middleware.html
下载中间件有下面几个方法
process_request
:请求经过下载器的时候调用
process_response
:请求完成后调用
process_exception
:请求发生异常时调用
from_crawler
:从crawler构造的时候调用
from_settings
:从settings构造的时候调用
``
更多详细的参数解释见这里
在爬取网页的时候,使用不一样的User-Agent
能够提升请求的随机性,定义一个随机设置User-Agent的中间件RandomUserAgentMiddleware
import random class RandomUserAgentMiddleware(object): """Randomly rotate user agents based on a list of predefined ones""" def __init__(self, agents): self.agents = agents # 从crawler构造,USER_AGENTS定义在crawler的配置的设置中 @classmethod def from_crawler(cls, crawler): return cls(crawler.settings.getlist('USER_AGENTS')) # 从settings构造,USER_AGENTS定义在settings.py中 @classmethod def from_settings(cls, settings): return cls(settings.getlist('USER_AGENTS')) def process_request(self, request, spider): # 设置随机的User-Agent request.headers.setdefault('User-Agent', random.choice(self.agents))
在settings.py
设置USER_AGENTS参数
USER_AGENTS = [ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)", "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)", "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0", "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20", "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52", ]
配置爬虫中间件的方式与pipeline相似,第二个参数表示优先级
# 配置爬虫中间件 SPIDER_MIDDLEWARES = { 'myproject.middlewares.CustomSpiderMiddleware': 543, # 若是想禁用默认的中间件的话,能够设置其优先级为None 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None, } # 配置下载中间件 DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.RandomUserAgentMiddleware': 543, 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None, }
爬虫最怕的就是封ip,这时候就须要代理服务器来爬取,scrapy设置代理服务器很是简单,只须要在请求前设置Request
对象的meta
属性,添加proxy
值便可,一般咱们能够经过中间件来作
class ProxyMiddleware(object): def process_request(self, request, spider): proxy = 'https://178.33.6.236:3128' # 代理服务器 request.meta['proxy'] = proxy
scrapy默认已经自带了缓存的功能,一般咱们只须要配置便可,打开settings.py
# 打开缓存 HTTPCACHE_ENABLED = True # 设置缓存过时时间(单位:秒) #HTTPCACHE_EXPIRATION_SECS = 0 # 缓存路径(默认为:.scrapy/httpcache) HTTPCACHE_DIR = 'httpcache' # 忽略的状态码 HTTPCACHE_IGNORE_HTTP_CODES = [] # 缓存模式(文件缓存) HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
更多参数参见这里
scrapy网络请求是基于Twisted,而Twisted默认支持多线程,并且scrapy默认也是经过多线程请求的,而且支持多核CPU的并发,一般只须要配置一些参数便可
# 默认Item并发数:100 CONCURRENT_ITEMS = 100 # 默认Request并发数:16 CONCURRENT_REQUESTS = 16 # 默认每一个域名的并发数:8 CONCURRENT_REQUESTS_PER_DOMAIN = 8 # 每一个IP的最大并发数:0表示忽略 CONCURRENT_REQUESTS_PER_IP = 0
更多参数参见这里
在使用的时候遇到过一个问题,在初始化scrapy startproject tutorial
的时候,若是使用了一些特殊的名字,如:test
, fang
等单词的话,经过get_project_settings
方法获取配置的时候会出错,改为tutorial
或一些复杂的名字的时候不会
ImportError: No module named tutorial.settings
这是一个bug,在github上有提到:https://github.com/scrapy/scrapy/issues/428,但貌似没有彻底修复,修改一下名字就行了(固然scrapy.cfg
和settings.py
里面也须要修改)
上面咱们是在settings.py里面配置pipeline,这里的配置的pipeline会做用于全部的spider,咱们能够为每个spider配置不一样的pipeline,设置Spider
的custom_settings
对象
class LianjiaSpider(CrawlSpider): ... # 自定义配置 custom_settings = { 'ITEM_PIPELINES': { 'tutorial.pipelines.TestPipeline.TestPipeline': 1, } }
经过LinkExtractor提取的scrapy.Link
默认不带节点信息,有时候咱们须要节点的其余attribute属性,scrapy.Link
有个text
属性保存从节点提取的text
值,咱们能够经过修改lxmlhtml._collect_string_content
变量为etree.tostring
,这样能够在提取节点值就变味渲染节点scrapy.Link.text
,而后根据scrapy.Link.text
属性拿到节点的html,最后提取出咱们须要的值
from lxml import etree import scrapy.linkextractors.lxmlhtml scrapy.linkextractors.lxmlhtml._collect_string_content = etree.tostring
有时候咱们已经把urls下载到数据库了,而不是在start_urls里配置,这时候能够重载spider的start_requests
方法
def start_requests(self): for u in self.db.session.query(User.link): yield Request(u.link)
咱们还能够在Request添加元数据,而后在response中访问
def start_requests(self): for u in self.db.session.query(User): yield Request(u.link, meta={'name': u.name}) def parse(self, response): print response.url, response.meta['name']
有时候咱们须要爬取的一些常常更新的页面,例如:间隔时间为2s,爬去一个列表前10页的数据,从第一页开始爬,爬完成后从新回到第一页
目前的思路是,经过parse方法迭代返回Request进行增量爬取,因为scrapy默认由缓存机制,须要修改
scrapy默认有本身的去重机制,默认使用scrapy.dupefilters.RFPDupeFilter
类进行去重,主要逻辑以下
if include_headers: include_headers = tuple(to_bytes(h.lower()) for h in sorted(include_headers)) cache = _fingerprint_cache.setdefault(request, {}) if include_headers not in cache: fp = hashlib.sha1() fp.update(to_bytes(request.method)) fp.update(to_bytes(canonicalize_url(request.url))) fp.update(request.body or b'') if include_headers: for hdr in include_headers: if hdr in request.headers: fp.update(hdr) for v in request.headers.getlist(hdr): fp.update(v) cache[include_headers] = fp.hexdigest() return cache[include_headers]
默认的去重指纹是sha1(method + url + body + header),这种方式并不能过滤不少,例若有一些请求会加上时间戳的,基本每次都会不一样,这时候咱们须要自定义过滤规则
from scrapy.dupefilter import RFPDupeFilter class CustomURLFilter(RFPDupeFilter): """ 只根据url去重""" def __init__(self, path=None): self.urls_seen = set() RFPDupeFilter.__init__(self, path) def request_seen(self, request): if request.url in self.urls_seen: return True else: self.urls_seen.add(request.url)
配置setting
DUPEFILTER_CLASS = 'tutorial.custom_filters.CustomURLFilter'
scrapy全部的迭代出来的的Item都会通过全部的Pipeline,若是须要处理不一样的Item,只能经过isinstance()
方法进行类型判断,而后分别进行处理,暂时没有更好的方案
咱们能够经过Request的priority控制url的请求的执行顺序,但因为网络请求的不肯定性,不能保证返回也是按照顺序进行的,若是须要进行逐个url请求的话,吧url列表放在meta对象里面,在response的时候迭代返回下一个Request对象到调度器,达到顺序执行的目的,暂时没有更好的方案
scrapy虽然是最有名的python爬虫框架,可是仍是有不少不足,例如,item不能单独配置给制定的pipeline,每个爬取的全部item都会走遍全部的管道,须要在管道里面去判断不一样类型的item,若是在pipelines和items比较多的项目,将会让项目变得很是臃肿
若有问题欢迎到个人博客留言
最后安利一下本身的博客:http://zhengbomo.github.com