1.中间件
根据scrapy
运行流程中所在位置不同分为
- 下载中间件
- 爬虫中间件
- 自定义中间件
中间件的作用
预处理request
和response
对象
- 如果响应状态码不是
200
则请求重试(重新构造Request
对象返回给引擎) - 可以对
header
以及cookie
进行更换和处理 - 使用代理
ip
等
但在Scrapy
默认的情况下,两种中间件都在middlewares.py
一个文件中。爬虫中间件使用方法和下载中间件相同,且功能重复,常使用下载中间件。
下载中间件的内部方法
Downloader Middlewares
默认的方法:
process_request(self, request, spider)
-
- 当每个
request
通过下载中间件时,该方法被调用 - 返回
None
值:没有return
也是返回None
,该request
对象传递给下载器,或通过引擎传递给其他权重低的process_request
方法 - 返回
Response
对象:不再请求,把response
返回给引擎 - 返回
Request
对象:把request
对象通过引擎交给调度器,此时将不通过其他权重低的process_request
方法
- 当每个
process_response(self, request, response, spider)
-
- 当下载器完成
http
请求,传递响应给引擎的时候调用 - 返回
Resposne
对象:通过引擎交给爬虫处理或交给权重更低的其他下载中间件的process_response
方法 - 返回
Request
对象:通过引擎交给调度器继续请求,此时将不通过其他权重低的process_request
方法
- 当下载器完成
注意:需要在settings.py
文件中开启中间件,权重越小越优先执行。
原始中间件一共封装五个方法,一般以下载中间件为例
def from_crawler(cls, crawler):d
作用:发送信号
def process_request(self, request, spider):
作用:处理请求对象
def process_response(self, request, response, spider):
作用:处理响应对象
def process_exception(self, request, exception, spider):
作用:捕获异常
def spider_opened(self, spider):
作用:日志记录,爬虫开始前的准备
2.设置动态UA
scrapy自动中间件基本不用,也可以用。咱们自定义设置UA中间件,虽然说可以设置在一个py文件里,但把中间件放在一个文件夹里,方便改动。
一般中间件设在爬虫开始前,也就是def process_request(self, request, spider):里面
from scrapy import signals
# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter
import random
class UserAgentMiddleware:
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"
]
def process_request(self, request, spider):
print("------下载中间件----")
# 随机选择UA
user_agent = random.choice(self.USER_AGENTS_LIST)
request.headers['User-Agent'] = user_agent
# 不写return
"""
如果返回None, 表示当前的response提交下一个权重低的process_request。
如果传递到最后一个process_request,则传递给下载器进行下载。
"""
动态UA封装好了,下一步往中间件里开启请求中间件,注意路径,注意是下载器中间件。
DOWNLOADER_MIDDLEWARES = {
# 'myspider.middlewares.MyspiderDownloaderMiddleware': 543,
'myspider.middlewares.UA_middlewares.UserAgentMiddleware': 100,
}
以百度为例,测试:
测试如下:
3.设置ip代理池
以快代理文档为例,照着官方文档
往项目根目录下添加文件
#!/usr/bin/env python
# -- coding: utf-8 --
import time
import threading
import requests
from scrapy import signals
# 提取代理IP的api
api_url = 'https://dps.kdlapi.com/api/getdps/?secret_id=ognun8vlg7vrdu7v79dh&signature=m3omx2nq0e7ve3jij071jw2xbkehs6th&num=10&format=json&sep=1'
foo = True
class Proxy:
def __init__(self, ):
self._proxy_list = requests.get(api_url).json().get('data').get('proxy_list')
@property
def proxy_list(self):
return self._proxy_list
@proxy_list.setter
def proxy_list(self, list):
self._proxy_list = list
pro = Proxy()
print(pro.proxy_list)
class MyExtend:
def __init__(self, crawler):
self.crawler = crawler
# 将自定义方法绑定到scrapy信号上,使程序与spider引擎同步启动与关闭
# scrapy信号文档: https://www.osgeo.cn/scrapy/topics/signals.html
# scrapy自定义拓展文档: https://www.osgeo.cn/scrapy/topics/extensions.html
crawler.signals.connect(self.start, signals.engine_started)
crawler.signals.connect(self.close, signals.spider_closed)
@classmethod
def from_crawler(cls, crawler):
return cls(crawler)
def start(self):
t = threading.Thread(target=self.extract_proxy)
t.start()
def extract_proxy(self):
while foo:
pro.proxy_list = requests.get(api_url).json().get('data').get('proxy_list')
#设置每15秒提取一次ip
time.sleep(15)
def close(self):
global foo
foo = False
然后在设置一个中间件
设置好自己的账号和密码
#!/usr/bin/env python
# -- coding: utf-8 --
"""
middlewares.py 中新增 ProxyDownloaderMiddleware 即代理中间件
请注意替换代码中的部分信息:username:用户名,password:密码
"""
from scrapy import signals
from myspider.myextend import pro
import random
class ProxyDownloaderMiddleware:
def process_request(self, request, spider):
proxy = random.choice(pro.proxy_list)
# 用户名密码认证(私密代理/独享代理)
username = ""
password = ""
request.meta['proxy'] = "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": username, "pwd": password, "proxy": proxy}
# 白名单认证(私密代理/独享代理)
# request.meta['proxy'] = "http://%(proxy)s/" % {"proxy": proxy}
return None
最后在setting设置
既可以成功设置ip代理池,携带代理ip在请求回调参数里的meta['proxy']里面。
原来是通过中间件设置快代理api,然后可以设置一次性获得几个ip,然后随机携带代理ip,并且还可以几秒后重新获取ip。
还是按以下测试:
结果如下:
但有一个问题是,如果并不是每一个爬虫需要代理ip。直接设置在中间件,相当于每次运行都要提取ip,造成ip浪费。
4.爬虫自设置ip代理。
第一步在setting.py注释掉ip中间件
第二步 custom_setting自定义设置ip中间件
custom_settings = {}
在爬虫添加这个,即表示添加代理
custom_settings = {
'DOWNLOADER_MIDDLEWARES': {
'myspider.middlewares.ProxyDownloaderMiddleware.ProxyDownloaderMiddleware': 200,
},
}
效果如下:
但是注意,一旦爬虫自定义中间件的话,以爬虫自定义的中间件为主。
第四阶段,结束。