scrapy框架的中间件
中间件的使用
-
作用:拦截所有的请求和响应
-
拦截请求:process_request拦截正常的请求,process_exception拦截异常的请求
-
篡改请求的头信息
def process_request(self, request, spider): print('proces_request!!!') #UA伪装 request.headers['User-Agent'] = random.choice(self.user_agent_list) return None # user_agent_list 这个列表中是大量的浏览器信息为了给User-Agent切换信息
-
代理
#拦截发生异常的请求,目的就是为了将异常的请求进行修正,然后将修正之后的正常的请求进行重新发送 def process_exception(self, request, exception, spider): #代理操作 # request.meta['proxy'] = 'http://ip:port' print('i am exception!!!') return request
注意:
process_exception
,return request的作用是将修正后的请求重新发送
-
-
拦截响应 ------ 以爬取网易新闻为例
-
篡改响应数据
- 不满足需求的响应数据对应的一定是不满足需求的响应对象
- 直接更换响应对象
-
需求:爬取网易新闻中国内,国际,军事,航工,无人机这五个板块下所有的新闻标题和内容
- 如何通过中间件更换不满足需求的响应对象
selenum
如何作用到scrapy
中
-
分析:每一个板块中显示的新闻标题是动态加载的
-
selenium
在scrapy
中的编码流程:- 实例化浏览器对象:爬虫类中将实例化出来的浏览器作为爬虫类的一个属性属性可以交互给中间件类
- 编写自动化操作:写在中间件的
process_response
中 - 关闭浏览器:写在爬虫类的
closed(self)
方法中
-
代码实现:
爬虫文件得到代码:
# -*- coding: utf-8 -*- import scrapy from selenium import webdriver from wangyiPro.items import WangyiproItem class WangyiSpider(scrapy.Spider): name = 'wangyi' # allowed_domains = ['www.xxx.com'] start_urls = ['https://news.163.com/'] #整个项目中涉及的响应对象个数: # - 1+5+n #解析:解析五个新闻板块对应的url five_model_urls = [] bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Desktop\爬虫+数据+算法\chromedriver.exe') #方法只会被调用一次 def closed(self,spider): self.bro.quit() def parse(self, response): li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li') model_indexs = [3,4,6,7,8] for index in model_indexs: li_tag = li_list[index] #解析出了每一个板块对应的url model_url = li_tag.xpath('./a/@href').extract_first() self.five_model_urls.append(model_url) #对每一个板块的url进行手动的请求发送 yield scrapy.Request(model_url,callback=self.parse_model) #解析:每一个板块中的新闻标题和新闻详情页的url(两个值都是动态加载出来的) def parse_model(self,response): #遇到了不满足需求的响应对象就是当前方法中的response参数 div_list = response.xpath('/html/body/div/div[3]/div[4]/div[1]/div/div/ul/li/div/div') for div in div_list: title = div.xpath('./div/div[1]/h3/a/text()').extract_first() detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first() item = WangyiproItem() item['title'] = title if detail_url: yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item}) def parse_detail(self,response): item = response.meta['item'] content = response.xpath('//*[@id="endText"]//text()').extract() content = ''.join(content) item['content'] = content yield item
items.py文件的代码:
import scrapy class WangyiproItem(scrapy.Item): # define the fields for your item here like: title = scrapy.Field() content = scrapy.Field()
middlewares.py文件的代码:中间件
# -*- coding: utf-8 -*- # Define here the models for your spider middleware # # See documentation in: # https://docs.scrapy.org/en/latest/topics/spider-middleware.html from scrapy import signals from scrapy.http import HtmlResponse from time import sleep class WangyiproDownloaderMiddleware(object): def process_request(self, request, spider): return None #拦截所有的响应(1+5+n),只有5个响应不满足需求 def process_response(self, request, response, spider): #1.将拦截到所有的响应中的指定5个不满足需求的响应对象找出 # request.url:每一个响应对应的url #spider.five_model_urls:5个板块对应的url # print(spider.five_model_urls) if request.url in spider.five_model_urls: #满足if条件的response就是5个板块对应的response spider.bro.get(request.url)#对每一个板块对应的url进行get请求发送 sleep(3) spider.bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(2) spider.bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(2) page_text = spider.bro.page_source new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request) return new_response #2.将这5个响应对象删除,实例化5个新的响应对象 #3.保证5个新的响应对象中包含动态加载出来的新闻标题数据 #4.将满足需求的5个新的响应对象返回 else: return response def process_exception(self, request, exception, spider): pass
注:记得在
settings.py
文件中开启对应的操作 -