Scrapy爬虫框架-自定义中间件
Scrapy中内置了多个中间件,不过在多数情况下开发者都会选择创建一个属于自己的中间件,这样既可以满足自己的开发需求,还可以节省很多开发时间。在实现自定义中间件时需要重写部分方法,因为Scrapy引擎需要根据这些方法名来执行并处理,如果没有重写这些方法,Scrapy的引擎将会按照原有的方法执行,从而失去自定义中间件的意义。
1.1 设置固定请求头
示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/18/22 10:45 AM
# 文件 :设置请求头.py
# IDE :PyCharm
import scrapy
import requests
class HeaderSpider(scrapy.Spider):
# 定义爬虫名称
name = 'header'
def start_requests(self):
# 设置固定的请求头
self.headers = {
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"
}
return [scrapy.Request('http://httpbin.org/get', headers=self.headers, callback=self.parse)]
# 响应信息
def parse(self, response):
print(response.text)
# 导入CrawlProcess类
from scrapy.crawler import CrawlerProcess
# 导入获取项目设置信息
from scrapy.utils.project import get_project_settings
# 程序入口
if __name__ == "__main__":
# 创建CrawlProcess类对象并传入项目设置信息参数
process = CrawlerProcess(get_project_settings())
# 设置需要启动的爬虫名称
process.crawl('header')
# 启动爬虫
process.start()
程序运行结果如下:
2022-02-18 15:02:40 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2022-02-18 15:02:42 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://httpbin.org/robots.txt> (referer: None)
2022-02-18 15:02:42 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://httpbin.org/get> (referer: None)
{
"args": {
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en",
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-620f4492-522c108346226f8936ae0be6"
},
"origin": "122.143.185.159",
"url": "http://httpbin.org/get"
}
注 意
在没有使用指定的请求头时,发送网络请求将使用Scrapy默认的请求头信息,示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/18/22 10:45 AM
# 文件 :设置请求头.py
# IDE :PyCharm
import scrapy
import requests
class HeaderSpider(scrapy.Spider):
# 定义爬虫名称
name = 'header'
def start_requests(self):
return [scrapy.Request('http://httpbin.org/get', callback=self.parse)]
# 响应信息
def parse(self, response):
print(response.text)
# 导入CrawlProcess类
from scrapy.crawler import CrawlerProcess
# 导入获取项目设置信息
from scrapy.utils.project import get_project_settings
# 程序入口
if __name__ == "__main__":
# 创建CrawlProcess类对象并传入项目设置信息参数
process = CrawlerProcess(get_project_settings())
# 设置需要启动的爬虫名称
process.crawl('header')
# 启动爬虫
process.start()
程序运行结果如下:
{
"args": {
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en",
"Host": "httpbin.org",
"User-Agent": "Scrapy/2.5.1 (+https://scrapy.org)", # Scrapy默认的请求头
"X-Amzn-Trace-Id": "Root=1-620f4a86-17d6962a689a9de34bdeabcc"
},
"origin": "122.143.185.159",
"url": "http://httpbin.org/get"
}
1.2 设置随机请求头
设置请求头是爬虫程序中必不可少的一项设置,大多数网站都会根据请求头内容制订一些反爬策略,在Scrapy框架中如果只是简单地设置一个请求头的话,可以在当前的爬虫文件中以参数的形式添加在网络请求当中。对于实现多个网络请求时,最好每发送一次请求就更换一个请求头,这样可以避免请求头的反爬策略。对于这样的需求可以使用自定义中间件的方式实现一个设置随机请求头的中间件。步骤如下:
1.2.1 在Pycharm命令行窗口通过执行"scrapy startproject header" 命令创建一个名为“header“的项目。如下图
1.2.2 然后通过“cd header“命令进入到项目的文件夹,然后通过“scrapy genspider headerSpider quotes.toscrape.com“ 命令创建名为“headerSpider“的爬虫文件。如下图:
1.2.3 打开headerSpider.py文件,配置测试网络请求的爬虫代码。代码如下:
# 导入scrapy框架
import scrapy
# 导入ssl模块,避免证书验证错误
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
class HeaderspiderSpider(scrapy.Spider):
name = 'headerSpider'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['http://quotes.toscrape.com/']
def start_requests(self):
# 设置爬取目标的地址,取前8页的网址
for i in range(8):
yield scrapy.Request(url=self.start_urls[0] + f'page/{i+1}/', callback=self.parse)
def parse(self, response):
# 打印每次网络请求的请求头信息
print('请求信息:', response.request.headers.get('User-Agent'))
pass
1.2.4 打开middlewares.py文件,在该文件中首先导入fake-useragent模块中的UserAgent类(如果没有安装fake-useragent模块,需要提前安装),然后创建RandomHeaderMiddleware类并通过init()函数进行类的初始化工作。代码如下:
from fake_useragent import UserAgent # 导入请求头类
# 自定义随机请求头的中间件
class RandomHeaderMiddleware(object):
def __init__(self, crawler):
self.ua = UserAgent() # 随机请求头对象
# 如果配置文件中不存在就使用默认的Google Chrome请求头
self.type = crawler.settings.get("RANDOM_UA_TYPE", "chrome")
1.2.5 重写from_crawler()方法,在该方法中将cls()实例对象返回,代码如下:
@classmethod
def from_crawler(cls, crawler):
# 返回cls()实例对象
return cls(crawler)
1.2.6 重写process_request()方法,在该方法中实现设置随机生成的请求头信息。代码如下:
# 发送网络请求时调用该方法
def start_requests(self):
# 设置爬取目标的地址,取前8页的网址
for i in range(8):
yield scrapy.Request(url=self.start_urls[0] + f'page/{i+1}/', callback=self.parse)
1.2.7 打开settings.py文件,在该文件中找到DOWNLOADER_MIDDLEWARES配置信息,然后配置自定义的请求头中间件,并把默认生成的下载中间件禁用,最后在配置信息的下面添加请求头类型。代码如下:
DOWNLOADER_MIDDLEWARES = {
# 启动自定义随机请求头中间件
'header.middlewares.RandomHeaderMiddleware':400,
# 设为None,禁用默认创建的下载中间件
'header.middlewares.HeaderDownloaderMiddleware': None,
}
# 配置请求头类型为随机,此处还可以设置为ie、firefox以及chrome
RANDOM_UA_TYPE = "random"
1.2.8 启动"headerSpider"爬虫,控制台将输出八次请求,并分别使用不同的请求头信息。如下所示:
2022-02-18 16:13:13 [scrapy.core.engine] DEBUG: Crawled (404) <GET http://quotes.toscrape.com/robots.txt> (referer: None)
2022-02-18 16:13:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/1/> (referer: None)
请求信息: b'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20130331 Firefox/21.0'
2022-02-18 16:13:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/5/> (referer: None)
2022-02-18 16:13:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/3/> (referer: None)
2022-02-18 16:13:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/2/> (referer: None)
2022-02-18 16:13:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/4/> (referer: None)
2022-02-18 16:13:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/6/> (referer: None)
2022-02-18 16:13:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/8/> (referer: None)
2022-02-18 16:13:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/7/> (referer: None)
请求信息: b'Mozilla/5.0 (Windows NT 5.0; rv:21.0) Gecko/20100101 Firefox/21.0'
请求信息: b'Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/22.0'
请求信息: b'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)'
请求信息: b'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17'
请求信息: b'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36'
请求信息: b'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36 Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10'
请求信息: b'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'
2022-02-18 16:13:13 [scrapy.core.engine] INFO: Closing spider (finished)
总 结
本次自定义中间件的重点是需要重写process_request()方法,该方法是Scrapy发送网络请求时所调用的,参数request表示当前的请求对象,例如请求头、请求方式以及请求地址等信息。参数spider表示爬虫程序。该方法返回值的具体说明如下:
- None:最常见的返回值,表示该方法已经执行完成并向下执行爬虫程序
- response: 停止该方法的执行,并开始执行process_response()方法
- request: 停止当前的中间件,将当前的请求交个Scrapy引擎重新执行
- IgnoreRequest:抛出异常对象,再通过process_exception() 方法处理异常,结束当前的网络请求