Scrapy中间件的使用
前言
当我们爬虫遇到懒加载的数据该怎么办呢,首先我们就会想到用selenium模拟人为滑动不断加载数据,最后再获取数据,但是selenium速度又太慢,那么使用selenium+scrapy就刚好可以解决这个问题,下面是一个使用scrapy抓取懒加载数据的一个小案例,代码和方法的不足,还请各位大牛指点一二!!!
Scrapy中间件
1 scrapy中间件的分类和作用
1.1 scrapy中间件的分类
Scrapy中间件可以分为以下几类:
- 请求中间件(Request Middleware):
请求中间件作用于发送给服务器的每个请求,可以用于对请求进行修改、添加请求头、设置代理等。主要的方法是process_request(request, spider)。如果一个中间件返回了一个Response对象,Scrapy将不再继续处理该请求,而是直接将该Response返回给爬虫。 - 下载中间件(Downloader Middleware):
下载中间件作用于接收到的每个响应,可以用于对响应进行修改、处理异常、设置代理等。主要的方法是process_response(request, response, spider)。类似于请求中间件,如果一个中间件返回了一个Response对象,Scrapy将不再继续处理该响应,而是直接将该Response返回给爬虫。 - 异常中间件(Spider Middleware):
异常中间件用于处理请求和响应过程中产生的异常,可以对异常进行自定义处理。主要的方法是process_exception(request, exception, spider)。
2 scrapy中间件的作用
- crapy中间件的主要作用是在请求和响应的处理过程中进行拦截和处理,对爬虫进行全局性的控制和干预。中间件可以用于实现诸如请求和响应处理、代理设置、用户代理(User-Agent)随机切换、Cookie处理、错误处理、自定义重试逻辑等功能。
- 但在scrapy默认的情况下 两种中间件都在middlewares.py一个文件中
- 爬虫中间件使用方法和下载中间件相同,常用下载中间件
2 下载中间件的使用方法:
process_request(request, spider):
- 作用:
- 在发送请求之前对请求进行预处理,允许你修改请求或添加自定义的请求头信息等
- 参数:
- request:即将发送的请求对象。
- spider:爬虫实例,可以在中间件中访问爬虫的属性和方法。
- 返回值:
- 返回一个None,表示继续处理该请求,将其发送给服务器。
- 返回一个Response对象,表示中间件处理了请求,并将这个自定义的响应返回给爬虫,不会再继续发送该请求。
- 返回一个Request对象,表示中间件处理了请求,并返回一个新的请求对象给爬虫,Scrapy会继续处理这个新的请求。
process_response(request, response, spider):
- 作用:
- 在接收到响应后对响应进行预处理,允许你修改响应内容、处理异常情况等。
- 参数:
- request:对应的请求对象。
- response:接收到的响应对象。
- spider:爬虫实例,可以在中间件中访问爬虫的属性和方法。
- 返回值:
- 返回一个Response对象,表示中间件处理了响应,并将这个自定义的响应返回给爬虫,不会再继续处理该响应。
- 返回一个Request对象,表示中间件处理了响应,并返回一个新的请求对象给爬虫,Scrapy会继续处理这个新的请求。
- 返回一个None,表示继续处理该响应,将其返回给爬虫。
process_exception(request, exception, spider):
-
作用:
- 在请求或响应处理过程中产生异常时进行处理,允许你对异常进行自定义处理或进行错误重试等操作。
-
参数:
- request:产生异常的请求对象。
- exception:产生的异常对象。
- spider:爬虫实例,可以在中间件中访问爬虫的属性和方法。
-
返回值:
- 返回一个Request对象,表示中间件处理了异常,并返回一个新的请求对象给爬虫,Scrapy会继续处理这个新的请求。
- 返回一个None,表示继续处理异常,Scrapy将按照默认方式处理异常。
-
在这些方法中,你可以根据需求对请求和响应进行修改,处理异常情况,实现自定义的重试逻辑等。同时,你还可以利用spider参数访问爬虫的属性和方法,以便更好地与爬虫进行交互和控制。如:spider.name
-
需要注意的是,这些方法在中间件中的执行顺序是由它们在DOWNLOADER_MIDDLEWARES设置项中的优先级决定的。优先级数值越小,优先级越高,即优先执行。因此,在实现自定义的下载中间件时,你可以根据功能的需要和依赖关系,合理设置中间件的优先级。
3 抓取某易新闻
3.1 抓取前分析
抓取国内、国际两大板块的数据
-
分析
国内、国际两大板块的数据是动态加载的,并不是跟着当前的请求一起返回的 -
解决方法
1、通过selenium配合爬虫抓取页面提取数据
2、找到加载动态数据的url地址 通过爬虫进行抓取
3.2 代码配置
- 配置settings.py文件
# Scrapy settings for wangyi project
BOT_NAME = 'wangyi'
SPIDER_MODULES = ['wangyi.spiders']
NEWSPIDER_MODULE = 'wangyi.spiders'
# 默认请求头
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36'
# 用于更换随机请求头
USER_AGENTS_LIST = [
"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" ]
LOG_LEVEL = 'ERROR'
ROBOTSTXT_OBEY = False
DOWNLOADER_MIDDLEWARES = {
'wangyi.middlewares.WangyiDownloaderMiddleware': 543,
}
ITEM_PIPELINES = {
'wangyi.pipelines.WangyiPipeline': 300,
}
- 爬虫代码wy.py
import scrapy
from selenium import webdriver
from selenium.webdriver import ChromeOptions
from selenium.webdriver.chrome.options import Options
from wangyi.items import WangyiItem
class WySpider(scrapy.Spider):
name = "wy"
# allowed_domains = ["抓取的网站域名"]
start_urls = ["抓取网站的起始url"]
li_index = [1, 2] # 索引为1 和2 的俩 为国内和国际板块的url
page_url = [] # 存放所需要板块的url
# 隐藏浏览器界面
chrome_option = Options()
chrome_option.add_argument('--headless')
chrome_option.add_argument('--disable-gpu')
# 防止检测
chrome_option.add_experimental_option('excludeSwitches', ['enable-automation'])
chrome_option.add_experimental_option('useAutomationExtension', False)
# 导入配置
driver = webdriver.Chrome(options=chrome_option)
def parse(self, response, **kwargs):
# 抓取国内和国际板块的数据
# 返回了所有板块的url
url_list = response.xpath('/html/body/div/div[3]/div[2]/div[2]/div/ul/li/a/@href').extract()
# print(url_list)
for i in range(len(url_list)):
if i in self.li_index:
url = url_list[i]
# 把板块对应的url存放起来
self.page_url.append(url)
yield scrapy.Request(url, callback=self.parse_detail)
# 对于板块请求后的处理
def parse_detail(self, response):
url_list = response.xpath('/html/body/div/div[3]/div[3]/div[1]/div[1]/div/ul/li/div/div/div/div[1]/h3/a/@href').extract()
for url in url_list:
yield scrapy.Request(url, callback=self.parse_detail_con)
def parse_detail_con(self, response):
items = WangyiItem()
title = response.xpath('//*[@id="container"]/div[1]/h1/text()').extract_first()
con = response.xpath('//*[@id="content"]/div[2]/p/text()').extract()[1:]
items['title'] = title
items['con'] = con
print(items)
yield items
- Middlewares.py
import time
from scrapy.http import HtmlResponse
from wangyi.settings import USER_AGENTS_LIST
import random
class WangyiSpiderMiddleware:
···
class WangyiDownloaderMiddleware:
# 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):
ua = random.choice(USER_AGENTS_LIST) # 每次随机一个请求头
request.headers['User-Agent'] = ua # 设置请求头
return None
def process_response(self, request, response, spider):
driver = spider.driver # 获取爬虫中的driver属性
# 如果是板块的url我们就进行抓取
if request.url in spider.page_url:
driver.get(request.url) # 使用selenium抓取刚才的请求
# 滚动条滚动到最下方
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
time.sleep(1)
# 拖动两次
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
time.sleep(1)
text = driver.page_source
# 构造HTML响应 篡改响应
return HtmlResponse(url=request.url, body=text, request=request, encoding='utf-8')
return response
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
print('process_exception')
def spider_opened(self, spider):
spider.logger.info("Spider opened: %s" % spider.name)
3.3 打印结果
总结
本文介绍了Scrapy中间件的使用方法,主要涵盖了下载中间件的详细说明。下载中间件是Scrapy中的一类中间件,用于在请求和响应的处理过程中进行拦截和处理。主要的三个方法是process_request、process_response和process_exception。
-
process_request(request,
spider)方法用于在发送请求之前对请求进行预处理,允许你修改请求或添加自定义的请求头信息等。可以返回一个None,表示继续处理该请求,将其发送给服务器;或返回一个Response对象,表示中间件处理了请求,并将自定义的响应返回给爬虫,不再继续发送该请求;或返回一个Request对象,表示中间件处理了请求,并返回一个新的请求对象给爬虫,Scrapy会继续处理这个新的请求。 -
process_response(request, response,
spider)方法用于在接收到响应后对响应进行预处理,允许你修改响应内容、处理异常情况等。可以返回一个Response对象,表示中间件处理了响应,并将自定义的响应返回给爬虫,不再继续处理该响应;或返回一个Request对象,表示中间件处理了响应,并返回一个新的请求对象给爬虫,Scrapy会继续处理这个新的请求;或返回一个None,表示继续处理该响应,将其返回给爬虫。 -
process_exception(request, exception,
spider)方法用于处理请求或响应处理过程中产生的异常,允许你对异常进行自定义处理或进行错误重试等操作。可以返回一个Request对象,表示中间件处理了异常,并返回一个新的请求对象给爬虫,Scrapy会继续处理这个新的请求;或返回一个None,表示继续处理异常,Scrapy将按照默认方式处理异常。
本文还提供了一个使用Scrapy抓取懒加载数据的小案例,通过配合Selenium和Scrapy实现了动态加载数据的抓取。案例中的爬虫通过下载中间件对板块URL进行抓取,使用Selenium模拟浏览器滚动实现动态加载,然后将抓取到的数据返回给爬虫进行处理。感兴趣的小伙伴可以自行配置管道进行保存。
总的来说,下载中间件是Scrapy强大灵活的功能之一,通过定制中间件,可以实现对请求和响应的全局性处理,提高爬虫的灵活性和可靠性。同时,也可以配合其他工具如Selenium等,实现对动态加载数据的抓取。