使用scrapy框架编写爬虫程序时与直接使用requests包的不同点就在于:scrapy是封装之后的requests很多的功能以及设置都已经默认封装好了,有一些基本的设置直接可以在setting文件中修改,而且创建多个爬虫也非常的方便,但是对于对scrapy框架了解没那么深入的人,想要修改稍微复杂一点的功能又非常的困难(我记得我刚开始做爬虫的时候就是采取scrapy与requests相结合的方法,在一些需要复杂设置的时候scrapy找不到该怎么改,就用requests写功能函数实现,这种方法虽然能实现,但是脱离了使用框架的意义),那么我们就来慢慢的细数scrapy中的稍微复杂的操作以及设置。
一:在middlewares(中间件)中添加ip代理
(1).外部维护的代理池
由于操作与上面的方法有很多类似,所以只介绍理论部分。代码可以从方法(2)中参考
如果你有其他的程序在某个地方,比如mysql数据库中维护了一个可以使用的代理池,那么在中间件中就可以直接封装一个类,随机查询mysql中的某个代理,在封装的类中设置一个scrapy默认在发送url给下载器之前调用的函数process_request(self, request, spider), 默认会有这三个参数,然后在最后添加request.meta[“proxy”] = 你的ip, 这样就可以将代理ip添加到请求中。
但是这种方法有一定的缺陷:假设你的代理池很小,在某一段时间之内,你抽到了同一个ip多次,故而被网站封掉了这次ip,但是维护程序在其他的网站验证时,这个ip还是可使用的,就造成了这个ip一直存在库中,但是对你现在想要抓取的网站已经没有了作用,这时有两个方法可以解决:
1.扩大代理池,使同一段时间内抽到同一个代理的概率变小,或者设置一个判断:只从数据库中几分钟内没有被抽中的代理中抽取。
2.开启retry功能,此功能可以直接在settings文件中设置
HTTPERROR_ALLOWED_CODES = [404, 403, 301, 500, 407]
# 出现这些状态码会重复进行请求
# DOWNLOAD_TIMEOUT = 3 # 设置超时参数 秒
RETRY_ENABLED = True # 是否开启 retry功能
RETRY_TIMES = 20 # retry的次数
可以根据具体情况设置retry的次数和超时时间。
当然在真正的使用代理池时,因为我们需要发送的请求一般都是千万或者亿级的,就算是很小的概率还是有可能造成当次请求的失败。
我一直在使用的一个代理验证的网站,可以验证使用代理成没成功,并且还可以验证代理的“好坏”程度
http://fetch.bestzsj.com/v1/validate_ip_https
(2)跟随scrapy框架随时用随时使用的代理池
在工作中,如果我们不经常使用代理池,假如一个月才有两天的时间使用代理池,那该怎么办?我们并不想维护着数据库中的代理池的程序一直运行着,但是随用随开启维护程序的话,刚开始的池又太小。对这种情况那该怎么办?
对于这种对不需要一直维护代理池的情况,可以在程序中设置维护代理池的程序,随着爬虫的启动而进行维护。
优点在于:在使用代理时被封的代理可以立即剔除,不需要一直维护数据库中的代理池。
缺点在于:在运行时会使用很多不可使用的代理,这时候要设置retry的次数要增大,耗费的时间相比于第一种方法要多一点。
上代码:
middlewares:
# 本次测试根据ip的可用性改变ip列表的长度
class ProxyMiddleware2(object):
"""
过滤全部ip中不能用的ip时
1.首先可以使用一个简单网站先验证这个ip是不是有效的
2.1有效的ip在请求具体网站的时候也会出现 不能使用的情况,这时过滤到刚开始就直接连接不上的ip
2.2 到此时所有的ip都是可用的, 但是也会出现在使用的过程中某个ip的调用太过频繁而被封锁的情况, 也进行删除
3.当ip列表的长度到达一个临界点时 进行重新获取新的ip列表
"""
def __init__(self):
self.ip_list = []
def process_request(self, request, spider):
while True:
if len(self.ip_list) < 3: # 设定代理ip的列表 小于某一个值时重新获取ip 并验证
self.get_ip()
else:
break
ip_raw = random.choice(self.ip_list)
self.ip_list.remove(ip_raw)
request.meta['download_timeout'] = 5
print("本次使用的ip:", ip_raw, "*"*10)
request.meta["proxy"] = ip_raw
def get_ip(self):
url = "你的代理的获取地址"
# 我在这里使用的是云代理的api,解析也是云代理返回的数据的解析,如果不一样则修改下面的代码
r = requests.get(url).json()
for r_one in r:
ip_one = "http://" + r_one["Ip"] + ":" + str(r_one["Port"])
self.ip_list.append(ip_one)
def process_response(self, request, response, spider):
self.ip_list.append(request.meta["proxy"])
print("代理池目前的大小为:", len(self.ip_list), " ############")
return response
完成代理的逻辑之后就可以在settings中将此中间件开启
设置完成之后,可以使用上面的验证网站进行验证,给请求添加代理也就完成了。