爬虫之scrapy框架
一、scrapy框架介绍
1、介绍
Scrapy,Python开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。
Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。它也提供了多种类型爬虫的基类,如BaseSpider、sitemap爬虫等,最新版本又提供了web2.0爬虫的支持。
Scrap,是碎片的意思,这个Python的爬虫框架叫Scrapy。
Scrapy 是基于twisted框架开发而来,twisted是一个流行的事件驱动的python网络框架。因此Scrapy使用了一种非阻塞(又名异步)的代码来实现并发。
2、scrapy架构图
1. 这是官方给出的架构图
2. 各个组件的作用
引擎(Engine)
引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。
调度器(Scheduler)
用来接收引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 相当于一个URL的优先级队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
下载器(Downloader)
用于下载网页内容, 并将网页内容返回给EGINE,下载器是建立在twisted这个高效的异步模型上的
下载器中间件(Downloader middlewares)
下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response(也包括引擎传递给下载器的Request)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。
爬虫(Spiders)
Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站
爬虫中间件(Spider middlewares)
Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。
项目管道(Item Pipeline)
Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)。
3、scrapy框架的基本流程
0. 程序开始运行 1. Spiders用yeild将url发送给Engine(引擎) 2. Engine把url发送给调度器(Scheduler) 3. 调度器(Scheduler)会将url生成request返回给Engine(引擎) 4. Engine(引擎)拿到request,通过下载器中间件(Downloader middlewares)进行层层过滤发送给下载器(Downloader) 5. 下载器(Downloader)在网上获取到response数据之后,又经过下载器中间件(Downloader middlewares)进行层层过滤发送给Engine(引擎) 6. Engine(引擎)获取到response数据之后,返回给Spiders,Spiders的parse()方法对获取到的response数据进行处理,解析出items或者requests 7. 将解析出来的items或者requests发送给Engine(引擎) 8. Engine(引擎)获取到items或者requests,将items发送给Item Pipeline进行数据的存储 9. 注意,只有当调度器中不存在任何request了,整个程序才会停止,也就是说,对于下载失败的URL,Scrapy也会重新下载
4、scrapy框架中间件
# 下载器中间件Downloader Middleware 1. process_request(request, spider) 当每个request通过下载中间件时,该方法被调用,这里可以修改UA,代理,Refferer 2. process_response(request, response, spider) 下载结果经过中间件时被此方法处理 3. process_exception(request, exception, spider) 下载过程中出现异常时被调用,这里可以处理超时 # 爬虫中间件Spider Middleware SpiderMiddleware主要处理解析Item的相关逻辑修正,比如数据不完整要添加默认,增加其他额外信息等 0. process_start_requests(start_requests, spider): 当spider发出请求时,被调用。 # Scheduler是scrapy官方结构图中的组件 运行流程:Spiders --> process_start_requests --> Scheduler 1. process_spider_input(response, spider) """参数 response(Response对象) - 被处理的response spider(Spider对象) - 该response对应的spider """ 当response通过spider中间件时,该方法被调用,处理该response。 # Downloader和Spiders是scrapy官方结构图中的组件,即上面那张图 运行流程:Downloader --> process_spider_input--> Spiders 2. process_spider_output(response, result, spider) """参数 response(Response对象) - 生成该输出的response result(包含Reques或Item对象的可迭代对象(iterable)) - spider返回的result spider(Spider对象) - 其结果被处理的spider """ 当Spider处理response返回result时,该方法被调用。 运行流程:Spiders --> process_spider_input--> Item Pipelines 3. process_spider_exception(response, exception, spider) 当spider或(其他spider中间件的) process_spider_input() """参数 response(Response对象) - 异常被抛出时被处理的response exception(Exception对象) - 被抛出的异常 spider(Spider对象) - 抛出异常的spider """ 抛出异常时, 该方法被调用。
二、scrapy的基础
1、安装
1. Linux系统 pip install scrapy # 从官网下载 pip install -i https://pypi.douban.com/simple/ scrapy # 从豆瓣源下载 2. Windows系统 1. pip install -i https://pypi.douban.com/simple/ wheel 2. pip install twisted 3. pip install -i https://pypi.douban.com/simple/ pywin32 4. pip install -i https://pypi.douban.com/simple/ scrapy
2、cmd下的命令行工具
1. 查看帮助 scrapy -h scrapy <command> -h 2. 有两种命令:其中Project-only必须切到项目文件夹下才能执行,而Global的命令则不需要 Global commands: startproject # 创建项目 genspider # 创建爬虫程序 settings # 如果是在项目目录下,则得到的是该项目的配置 runspider # 运行一个独立的python文件,不必创建项目 shell # scrapy shell url地址 在交互式调试,如选择器规则正确与否 fetch # 独立于程单纯地爬取一个页面,可以拿到请求头 view # 下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求 version # scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依赖库的版本 Project-only commands: crawl # 运行爬虫,必须创建项目才行,确保配置文件中ROBOTSTXT_OBEY = False check # 检测项目中有无语法错误 list # 列出项目中所包含的爬虫名 edit # 编辑器,一般不用 parse # scrapy parse url地址 --callback 回调函数 #以此可以验证我们的回调函数是否正确 bench # scrapy bentch压力测试 3. 官网链接 https://docs.scrapy.org/en/latest/topics/commands.html
3、目录结构
project_name/ scrapy.cfg project_name/ __init__.py items.py middlewares.py pipelines.py settings.py spiders/ __init__.py 爬虫1.py 爬虫2.py 爬虫3.py 文件说明: scrapy.cfg 项目的主配置信息,用来部署scrapy时使用,爬虫相关的配置信息在settings.py文件中。 items.py 设置数据存储模板,用于结构化数据,如:Django的Model middlewares.py 中间件是处于引擎(crawler.engine)和下载器(crawler.engine.download())之间的一层组件 pipelines 数据处理行为,如:一般结构化的数据持久化 settings.py 配置文件,如:递归的层数、并发数,延迟下载等。强调:配置文件的选项必须大写否则视为无效 spiders 爬虫目录,如:创建文件,编写爬虫规则 注意: 1、一般创建爬虫文件时,以网站域名命名 2、默认只能在终端执行命令,为了更便捷操作: # 在项目根目录下新建:entrypoint.py from scrapy.cmdline import execute execute(['scrapy', 'crawl', 'qsbk', '--nolog'])
如何在pycharm中运行scrapy程序
1、一般创建爬虫文件时,以网站域名命名 2、默认只能在终端执行命令,为了更便捷操作: # 在项目根目录下新建:entrypoint.py from scrapy.cmdline import execute execute(['scrapy', 'crawl', 'qsbk', '--nolog'])
4、Spider主爬虫程序类
Spiders是定义如何抓取某个站点(或一组站点)的类,包括如何执行爬行(即跟随链接)以及如何从其页面中提取结构化数据(即抓取项目)。换句话说,Spiders是为特定站点(或者在某些情况下,一组站点)爬网和解析页面定义自定义行为的地方。 1. 生成初始的Requests来爬取第一个URLS,并且标识一个回调函数 第一个请求定义在start_requests()方法内默认从start_urls列表中获得url地址来生成Request请求, 默认的回调函数是parse方法。回调函数在下载完成返回response时自动触发 2. 在回调函数中,解析response并且返回值 返回值可以4种: 包含解析数据的字典 Item对象 新的Request对象(新的Requests也需要指定一个回调函数) 或者是可迭代对象(包含Items或Request) 3. 在回调函数中解析页面内容 通常使用Scrapy自带的Selectors,但很明显你也可以使用Beutifulsoup,lxml或其他你爱用啥用啥。 4. 最后,针对返回的Items对象将会被持久化到数据库 通过Item Pipeline组件存到数据库:https://docs.scrapy.org/en/latest/topics/item-pipeline.html#topics-item-pipeline) 或者导出到不同的文件(通过Feed exports:https://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports)
5、Scrapy自带的选择器Selectors(xpath)
使用Scrapy自带的选择器,获取到的数据都是Selectors对象, 需要使用extract方法把Selectors对象的内容提取出来。 1,div.xpath('.//div[@class="author clearfix"]/a/h2/text()') 结果是Selector对象组成的列表:[<Selector xpath='xxx' data='xxx'>,] 2,div.xpath('.//div[@class="author clearfix"]/a/h2/text()').extract() 把Selector对象的内容提取出来,还是列表:['data的内容',] 3,div.xpath('.//div[@class="author clearfix"]/a/h2/text()').extract()[0] 把提取出来的列表按索引取出某一个值,若只提取一个,也可以使用extract_first() div.xpath('.//div[@class="author clearfix"]/a/h2/text()').extract_first() 4,div.xpath('.//div[@class="author clearfix"]/a/h2/text()')[0].extract() 先在列表中取出某个Selector对象,在把Selector对象的内容提取出来
6、DupeFilter(去重)
1. 默认使用方式 DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter' Request(...,dont_filter=False) # 如果dont_filter=True则告诉Scrapy这个URL不参与去重。 2. 自定义去重规则 from scrapy.dupefilter import RFPDupeFilter,看源码,仿照BaseDupeFilter # 步骤一:在项目目录下自定义去重文件dup.py class UrlFilter(object): def __init__(self): self.visited = set() # 或者放到数据库 @classmethod def from_settings(cls, settings): return cls() def request_seen(self, request): if request.url in self.visited: return True self.visited.add(request.url) def open(self): # can return deferred pass def close(self, reason): # can return a deferred pass def log(self, request, spider): # log that a request has been filtered pass
三、scrapy的小Demo
1、创建并执行爬虫应用程序
1. 在cmd下输入命令 cd 工作目录 scrapy startproject 项目名 # 创建项目 cd 项目名 scrapy genspider 应用名称 爬取的起始url # 创建爬虫程序 scrapy crawl 应用名称 # 该种执行形式会显示执行的日志信息 scrapy crawl 应用名称 --nolog # 该种执行形式不会显示执行的日志信息 scrapy crawl 应用名称 -o xxx.xml # 存储数据文本格式 2. 示例 # 爬取糗事百科 cd E:\SpiderProject\爬虫\scrapy框架 # 进入工作目录 scrapy startproject QSBK # 创建一个名为QSBK的项目 cd QSBK # 进入项目目录 scrapy genspider qsbk www.qiushibaike.com # 创建爬虫程序(会在项目下的spiders目录下创建qsbk.py程序) scrapy crawl qsbk # 执行应用程序 scrapy crawl qsbk -o qsbk.xml # 将爬取到的数据解析后存储成xml格式的文件 3.spiders目录下创建的qsbk.py程序 # -*- coding: utf-8 -*- import scrapy class QsbkSpider(scrapy.Spider): name = 'qsbk' # 应用名称 # 允许爬取的域名(如果遇到非该域名的url则爬取不到数据) allowed_domains = ['www.qiushibaike.com'] # 起始爬取的url start_urls = ['http://www.qiushibaike.com/'] # 访问起始URL并获取结果后的回调函数,该函数的response参数就是向起始的url发送请求后,获取的响应对象. # 该函数返回值必须为可迭代对象或者NUll def parse(self, response): print(response.text) # 获取字符串类型的响应内容 print(response.body) # 获取字节类型的响应内容 4. 项目的配置文件(settings.py)相关配置 修改内容及其结果如下: 19行:伪装请求载体身份(伪装成浏览器) USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36' 22行:可以忽略或者不遵守robots协议 ROBOTSTXT_OBEY = False 注意:settings.py的配置项的名称都要大写 5. 爬取糗百首页中文字段子的内容和作者 对qsbk.py进行修改 """ 注意:记得修改settings.py的配置哦!!! 类QsbkSpider是继承了scrapy.Spider这个类的, scrapy.Spider中的start_requests方法是我们爬虫主逻辑、策略, 因为start_requests方法默认只是做了一些简单的爬取措施, 如果要爬取的网页有了很强的反爬措施,此时就需要我们重写start_requests方法,破解反爬措施。 start_requests方法必须yield Request对象,因此需要导入 from scrapy import Request """ import scrapy from scrapy import Request # from scrapy.http import Request # 跟上面导入的Request是一样的 class QsbkSpider(scrapy.Spider): name = 'qsbk' # 应用名称 # 允许爬取的域名(如果遇到非该域名的url则爬取不到数据) allowed_domains = ['www.qiushibaike.com'] # 起始爬取的url start_urls = ['https://www.qiushibaike.com/text/'] def start_requests(self): # 爬虫主逻辑 策略 yield Request( url="https://www.qiushibaike.com/text/", callback=self.parse, # 定义回调函数,不写默认就是 parse ) def parse(self, response): # 用xpath或者BS或者 css selector进行数据解析 # 这里的response自带xpath方法,可以将xpath表达式直接作用于该函数中 odiv = response.xpath('//div[@id="content-left"]/div') content_list = [] # 用于存储解析到的数据 # xpath函数返回的为列表,列表中存放的数据为Selector类型的数据 # 我们解析到的内容被封装在了Selector对象中,需要调用extract()函数将解析的内容从Selecor中取出 for div in odiv: # 1.获取用户名author author = div.xpath('.//div[@class="author clearfix"]/a/h2/text()') if author: author = author[0].extract() else: author = "匿名用户" # 2.获取这个用户的段子的内容contents contents = div.xpath('.//div[@class="content"]/span/text()') # 遇到换行br就会生成一个Selector对象 content_list.append({ "author": author, "content": "".join([selector.extract().strip() for selector in contents]) }) print("content_list", content_list) return content_list
2、使用BeautifulSoup解析
""" 除了解析代码,其余代码跟上面的例子一样 """
from bs4 import BeautifulSoup
def parse(self, response): print(response.text) soup = BeautifulSoup(response.text, 'lxml') odiv = soup.find_all('div', class_='article') content_list = [] # 用于存储解析到的数据 for div in odiv: author = div.find('div', class_='author').text print(author) content = div.find('div', class_='content').text print(content) dic = { '作者': author, '内容': content } print(dic) # 将数据存储到content_list这个列表中 content_list.append(dic) return content_list
四、爬取亚马逊的iphoneXs商品
1.主爬虫程序 """ 登录亚马逊官方,搜索:iphone xs max 需要解析出来的数据:商品简介、价格、配送方 爬取前10页 注意在settings中设置UA反爬和不遵守robots """ import scrapy from scrapy import Request class AmazonSpider(scrapy.Spider): name = 'amazon' allowed_domains = ['www.amazon.cn'] start_urls = ['https://www.amazon.cn/'] def start_requests(self): yield Request( url="https://www.amazon.cn/s/ref=nb_sb_noss_1?__mk_zh_CN=%E4%BA%9A%E9%A9%AC%E9%80%8A%E7%BD%91%E7%AB%99&url=search-alias%3Daps&field-keywords=iphone+xs+max", callback=self.parse_index, dont_filter=True ) def parse_index(self, response): ''' 解析商品首页获取几十个商品详情的url,发请求 解析函数的返回值: (1) 字典或者迭代数据(比如列表套字典) (2) 请求Request对象 (3) item对象(通过pipeline做持久化的) ''' # 商品列表页的所有商品的URL detail_urls = response.xpath('//*[contains(@id,"result_")]/div/div[3]/div[1]/a/@href').extract() for detail_url in detail_urls: print("detail_url", detail_url) yield Request( url=detail_url, callback=self.parse_detail, # parse_detail解析详情页的函数 dont_filter=True ) # 解析一个下一页的URL,经过测试发现下面xpath解析出来的url是相对路径,因此需要我们拼接出完整的url # response.urljoin方法能帮我们把要爬取的网页域名和需要拼接的url进行拼接 next_url = response.urljoin(response.xpath('//*[@id="pagnNextLink"]/@href').extract_first()) yield Request( url=next_url, callback=self.parse_index, dont_filter=True ) def parse_detail(self, response): ''' response: 某一个商品详情页的响应体 ''' title = response.xpath('//*[@id="productTitle"]/text()')[0].extract().strip() price = response.xpath('//*[@id="priceblock_ourprice"]/text()')[0].extract().strip() delivery = response.xpath('//*[@id="ddmMerchantMessage"]/*[1]/text()').extract() print(title) print(price) print(delivery) 2.免费代理网站 http://www.goubanjia.com/ 3.设置代理池 当你的ip访问亚马逊太过频繁的时候,亚马逊会对你进行反爬(暂封你的ip), 若你发现无法爬取亚马逊网页的时候,可设置代理池(下面会详细讲,现在先把网页爬下来) 步骤: 1. 在免费代理网站上找一个能用的代理ip 2. 在middlewares.py中找到AmazonDownloaderMiddleware类下的process_request方法 3. 在process_request中写入如下几行 proxy = 'http://77.70.115.104:8080' request.meta['download_timeout'] = 20 request.meta['proxy'] = proxy
五、Item
Item对象是用于收集抓取数据的简单容器。它们提供类似字典的 API,并具有用于声明其可用字段的方便语法。 Scrapy Items类似于Django Models,但是Scrapy Items更简单,因为没有不同字段类型的概念。 1.声明Item 在项目中找到items.py,使用简单的类定义语法和Field对象声明项目 import scrapy class AmazonItem(scrapy.Item): # define the fields for your item here like: title = scrapy.Field() price = scrapy.Field() delivery = scrapy.Field() 2.使用Item # 在需要使用的地方(爬虫主程序)导入这个类 from ..items import AmazonItem # 实例化对象并添加数据 item = AmazonItem() item["title"] = title item["price"] = price item["delivery"] = delivery 3. Demo # 上面亚马逊的parse_detail可以这样写(先实例化再设置数据) def parse_detail(self, response): ''' response: 某一个商品详情页的响应体 ''' title = response.xpath('//*[@id="productTitle"]/text()')[0].extract().strip() price = response.xpath('//*[@id="priceblock_ourprice"]/text()')[0].extract().strip() delivery = response.xpath('//*[@id="ddmMerchantMessage"]/*[1]/text()').extract() print(title) print(price) print(delivery) item = AmazonItem() # 先实例化 item["title"] = title # 再设置数据 item["price"] = price item["delivery"] = delivery return item # 还可以这样写(实例化的时候初始化数据) def parse_detail(self, response): ''' response: 某一个商品详情页的响应体 ''' title = response.xpath('//*[@id="productTitle"]/text()')[0].extract().strip() price = response.xpath('//*[@id="priceblock_ourprice"]/text()')[0].extract().strip() delivery = response.xpath('//*[@id="ddmMerchantMessage"]/*[1]/text()').extract() print(title) print(price) print(delivery) item = AmazonItem(title=title, price=price, delivery=delivery) # 实例化的时候初始化数据 # 获取字段值 print(item['title']) print(item.get('price')) # 访问所有键 print(item.keys()) # 访问所有的键值对 print(item.items()) return item
六、Item PipeLine
在一个项目被蜘蛛抓取之后,它被发送到项目管道,该项目管道通过顺序执行的几个组件处理它。
每个项目管道组件(有时简称为“项目管道”)是一个实现简单方法的Python类。他们收到一个项目并对其执行操作,同时决定该项目是否应该继续通过管道或被丢弃并且不再处理。
项目管道的典型用途是:
cleansing HTML data(清洗数据)
validating scraped data (checking that the items contain certain fields)(校验数据)
checking for duplicates (and dropping them)(去重)
storing the scraped item in a database(排序存储)
PipeLine只接收Item对象,PipeLine在项目中的pipelines.py里面定义。
定义好的PipeLine类需要在settings里面进行声明,只要在主程序中return item就会自动去settings中找到声明的PipeLine类,
然后对item进行处理,存储。
# 大概在settings的67行设置pipeline类, ITEM_PIPELINES = { 'Amazon.pipelines.AmazonPipeline': 300, }
1、编写自己的项目管道
每个项管道组件都是一个以下方法的Python类: # item进入pipeline前会调用这个方法 open_spider(self,蜘蛛) 打开蜘蛛时会调用此方法。 # 必须实现的方法,用来处理item数据的方法(核心方法) process_item(self,项目,蜘蛛) 为每个项目管道组件调用此方法。process_item() 返回带数据的dict,返回一个Item (或任何后代类)对象,返回Twisted Deferred或引发 DropItem异常。丢弃的项目不再由其他管道组件处理。 # item处理完毕后调用这个方法 close_spider(self,蜘蛛) 当蜘蛛关闭时调用此方法。 # 当pipeline类实例化的时候,如果有from_crawler方法会先执行这个方法,再执行init方法 # 参数crawler代表这个爬虫程序,可以在此拿到爬虫的名字、域名、配置参数等,都可以从这里拿到 from_crawler(cls,crawler) 如果存在,则调用此类方法以从a创建管道实例Crawler。它必须返回管道的新实例。Crawler对象提供对所有Scrapy核心组件的访问, 如设置和信号; 它是管道访问它们并将其功能挂钩到Scrapy的一种方式。
2、使用MongoDB存储item
1. settings中的配置 # 与Mongodb数据库相关配置 HOST = "127.0.0.1" PORT = 27017 USER = "root" PWD = "" DB = "amazon" TABLE = "goods" # 声明pipeline类 ITEM_PIPELINES = { 'Amazon.pipelines.MongodbPipeline': 300, } 2. 定义pipeline类 from pymongo import MongoClient class MongodbPipeline(object): def __init__(self, host, port, user, pwd, db, table): self.host = host self.port = port self.user = user self.pwd = pwd self.db = db self.table = table @classmethod def from_crawler(cls, crawler): """ Scrapy会先通过getattr判断我们是否自定义了from_crawler,有则调它来完成实例化 从爬虫程序crawler中的settings配置拿到我们需要的数据,返回给这个类,然后调用init实例化 """ HOST = crawler.settings.get('HOST') PORT = crawler.settings.get('PORT') USER = crawler.settings.get('USER') PWD = crawler.settings.get('PWD') DB = crawler.settings.get('DB') TABLE = crawler.settings.get('TABLE') # 必须返回这个类的对象 return cls(HOST, PORT, USER, PWD, DB, TABLE) def open_spider(self, spider): """ 爬虫刚启动时执行一次 """ # self.client = MongoClient('mongodb://%s:%s@%s:%s' %(self.user,self.pwd,self.host,self.port)) self.client = MongoClient(host=self.host, port=self.port) def close_spider(self, spider): """ 爬虫关闭时执行一次 """ self.client.close() def process_item(self, item, spider): # 操作并进行持久化 d = dict(item) # 把item转换成字典类型 if all(d.values()): # 当字典不为空的时候插入数据 self.client[self.db][self.table].save(d) # save方法也等于插入数据并保存 return item
3、item流向多个pipeline
1. 基于上面的settings再进行配置 # 声明pipeline类 ITEM_PIPELINES = { 'Amazon.pipelines.MongodbPipeline': 300, 'Amazon.pipelines.FilePipeline': 400, } 2. 再定义一个pipeline类 class FilePipeline(object): def open_spider(self, spider): """ 爬虫刚启动时执行一次 """ print("文件写入一个item") # self.client = MongoClient('mongodb://%s:%s@%s:%s' %(self.user,self.pwd,self.host,self.port)) self.file = open("file_pipeline.txt", "w") def close_spider(self, spider): """ 爬虫关闭时执行一次 """ self.file.close() def process_item(self, item, spider): # 操作并进行持久化 d = dict(item) import json self.file.write(json.dumps(d) + "\n") 3. 多个管道的流程 ITEM_PIPELINES = { 'Amazon.pipelines.MongodbPipeline': 300, 'Amazon.pipelines.FilePipeline': 400, } 300和400是权重,设置多少都行,看自己喜好,权重小的管道先执行, 因此MongodbPipeline先执行,执行完毕后,process_item方法必须return item, return item后会把item传给下一个管道FilePipeline进行处理。 如果你想处理完后不再给别人处理了,那么可以 from scrapy.exceptions import DropItem def process_item(self, item, spider): if 某种情况: raise DropItem("Duplicate item found: %s" % item)
七、下载中间件
scrapy的下载中间件个Django的中间件类似, process_request、process_response等方法也是类似的 不同的是: Django中process_request返回一个对象后,process_response是从process_request对应的process_response开始返回, 而scrapy的下载中间件process_request返回一个对象后,process_response是从第一个process_response开始返回, 1. settings中的配置 # 大概在settings的55行 DOWNLOADER_MIDDLEWARES = { 'Amazon.middlewares.MyDownMiddleware': 543, } 2. 权重 process_request权重小的先执行 process_response权重大的先执行 process_request权重小的先执行 3. 各个方法的作用 class MyDownMiddleware(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 因此我们更换代理的时候,应该放在process_exception中 def process_exception(self, request, exception, spider): # 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 proxy = 'http://77.70.115.104:8080' request.meta['download_timeout'] = 20 request.meta['proxy'] = proxy return request # 返回request,即把请求返回给调度器,调度器再重新去发请求
八、代理池
1、原理
我们跟换代理的时候,应该是在某个代理网站上,把可用的代理全部爬下来,
当出现异常的时候就去代理池中把真正能用的ip跟换到我的程序中,
因此还需要写一个爬代理ip网站的程序,这个不用担心,去github中搜就可以,
把搜到的应用结合到我们的爬虫程序中
2、步骤
1. 把下载到的程序放到我们爬虫程序中 2. 读README.md文件,根据步骤进行一些初始化配置 3. 把爬ip的程序启动后,把爬取到的代理ip存到MongoDB中 [DB] ;Configure the database information ;type: SSDB/MONGODB if use redis, only modify the host port,the type should be SSDB type = MONGODB host = 127.0.0.1 port = 27017 name = proxy 4. 如果要在爬虫代码中使用的话, 可以将此api封装成函数直接使用,例如: import requests def get_proxy(): return requests.get("http://127.0.0.1:5010/get/").content def delete_proxy(proxy): requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy)) 开发的api接口在这里设置 [API] # API config http://127.0.0.1:5010 # The ip specified when starting the web API ip = 0.0.0.0 # he port on which to run the web API port = 5010
3、代码实现
1. 首先确保已经把代理ip的那个网站爬下来了 2. 确保DB和API设置好了 3. 在我们的爬虫程序中新建一个proxy.py文件 代码如下: import requests def get_proxy(): return requests.get("http://127.0.0.1:5010/get/").content def delete_proxy(proxy): requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy)) 4. 我们爬虫程序的下载中间件代码 from .proxy import get_proxy,delete_proxy class AmazonDownloaderMiddleware(object): def process_exception(self, request, exception, spider): # 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 proxy = "http://"+get_proxy() request.meta['download_timeout'] = 20 request.meta["proxy"] = proxy return request