您的位置:新闻资讯 >文章内容
浅析反反爬虫策略之请求对象添加随机代理IP
来源:本站 作者:jinglingdaili 时间:2019-04-15 16:49:09

爬虫的目的就是为了模拟点击浏览器操作的行为,在反反爬策略中,最基础的就是更换User-Agent。


User-Agent的作用是方便服务器识别,当前请求对象的身份信息。无法从身份属性来识别是否是机器操作,网站服务器只能通过其他信息来辨别,区别机器和正常用户。识别IP访问频率,判断cookie信息,添加验证码操作等都是常见的网站反爬操作。


今天,主要说的就是突破网站根据IP访问频率的反反爬策略:随机更换请求对象的IP信息。

Scrapy中,更换请求对象的IP信息非常的方便,只需要在request对象进入下载器之前,修改request对象的参数信息。


所以我们需要在下载器中间件 Download_middleware中自定义一个下载器中间件ProxiesMiddleware,在process_request()函数中修改request对象信息。


from random import choice

from .settings import PROXIES_LIST

class ProxiesMiddleware(object):

def process_request(self, request, spider):

request.meta["proxy"]=random.choice(PROXIES_LIST)

return None


其中 PROXIES_LIST 是构建的代理列表。


process_request的参数分别是request当前请求对象,spider当前的爬虫对象。

返回None,Scrapy将继续处理该Request;


返回Request对象时,这个Reuqest会重新放到调度队列里,更低优先级的process_reqyest()函数不会执行;


返回Response对象时,直接将Response对象发送给Spider来处理。


现在每次发送一次请求,请求对象的IP信息就会从配置中的代理IP池中随机挑选一个。不同的IP就会迷惑服务器监控,不会对本次请求做反爬处理了。至此,我们的爬虫就能顺顺当当的获取数据了。


代理ip池


你以为这样就完事了吗,并不是,这才只是代理的第一步。没有哪个代理是能保证百分之百成功的。付费代理也好,免费代理也好,即使是高匿代理也只不过是提高了防反爬的几率,并没有说一定会成功的。


问题来了: 如果加入了随机代理的请求被反爬了,应该如何解决呢? 换下一家网站?收拾铺盖跑路?还是跟它死磕呢?


请求失败的解决方案有两种:


1. 多试几次,直到请求成功,不成功就报错。

2. 换一个代理试试,直到换成请求成功的代理。对代理质量要求必须高。如果代理池质量很差,项目就会陷入死循环中。


解决逻辑是: 设置重试次数,达到指定次数就换代理。


幸运的是Scrapy框架中自带了处理失败请求的中间件RetryMiddleware。

源码如下:


class RetryMiddleware(objec):

# IOError is raised by the HttpCompression middleware when trying to

# decompress an empty response

EXCEPTIONS_TO_RETRY=(defer.TimeoutError, TimeoutError, DNSLookupError,

ConnectionRefusedError, ConnectionDone, ConnectError,

ConnectionLost, TCPTimedOutError, ResponseFailed,

IOError, TunnelError)

def __init__(self, settings):

'''

RETRY_ENABLED: 用于开启中间件,默认为TRUE

RETRY_TIMES: 重试次数, 默认为2

RETRY_HTTP_CODES: 遇到哪些返回状态码需要重试, 一个列表,默认为[500, 503, 504, 400, 408]

RETRY_PRIORITY_ADJUST:调整相对于原始请求的重试请求优先级,默认为-1

'''

if not settings.getbool('RETRY_ENABLED'):

raise NotConfigured

self.max_retry_times=settings.getint('RETRY_TIMES')

self.retry_http_codes=set(int(x) for x in settings.getlist('RETRY_HTTP_CODES'))

self.priority_adjust=settings.getint('RETRY_PRIORITY_ADJUST')

@classmethod

def from_crawler(cls, crawler):

return cls(crawler.settings)

# def process_request(self, request, spider):

# 将IP放入请求头中

# request.meta["proxy"]="http://" + self.get_random_proxy()

# 设置请求不过滤

# request.dont_filter=True

# return None

def process_response(self, request, response, spider):

# 判断请求参数中是否有 重新发送请求的参数 不重新请求,跳过执行下次请求, False重新请求

if request.meta.get('dont_retry', False):

return response

# 判断状态码是否在需要重试的状态码列表中, 如果在,重新发送请求

if response.status in self.retry_http_codes:

reason=response_status_message(response.status)

return self._retry(request, reason, spider) or response

return response

def process_exception(self, request, exception, spider):

#  self.request_url=request.url

# 捕获异常

if isinstance(exception, self.EXCEPTIONS_TO_RETRY) \

and not request.meta.get('dont_retry', False):

return self._retry(request, exception, spider)

def _retry(self, request, reason, spider):

# 设置当前请求次数的参数

retries=request.meta.get('retry_times', 0) + 1

retry_times=self.max_retry_times

# 设置最大请求次数

if 'max_retry_times' in request.meta:

retry_times=request.meta['max_retry_times']

stats=spider.crawler.stats

# 判断是否超出最大请求次数

if retries <=retry_times:

logger.debug("Retrying %(request)s (failed %(retries)d times): %(reason)s",

{'request': request, 'retries': retries, 'reason': reason},

extra={'spider': spider})

retryreq=request.copy()

retryreq.meta['retry_times']=retries

# 重试的请求加入新代理

#  retryreq.meta['proxy']="http://" + self.get_random_proxy()

retryreq.dont_filter=True

retryreq.priority=new_request.priority + self.priority_adjust

# retryreq.priority=request.priority + self.priority_adjust

if isinstance(reason, Exception):

reason=global_object_name(reason.__class__)

stats.inc_value('retry/count')

stats.inc_value('retry/reason_count/%s' % reason)

return retryreq

else:

print("重试次数超出,报错")

stats.inc_value('retry/max_reached')

logger.debug("Gave up retrying %(request)s (failed %(retries)d times): %(reason)s",

{'request': request, 'retries': retries, 'reason': reason},

extra={'spider': spider})


注释的部分是添加的加入代理的逻辑,需要继承RetryMiddleware之后再重写。


# 重试请求状态码

RETRY_HTTP_CODES=[500, 502, 503, 504, 400, 403, 404, 408, 302]

# 重试次数

RETRY_TIMES=4


在settings中设置最大重试次数以及需要进行重试的异常状态码列表。

关闭Scrapy自带的RetryMiddleware中间件,开启自定义的Retry中间件。


DOWNLOADER_MIDDLEWARES={

'demo2.middlewares.RandomUserAgentMiddleware': 200,

'demo2.new_Retrymiddlewares.NewRetryMiddlewares': 600,

'scrapy.downloadermiddlewares.retry.RetryMiddleware': None  # 关闭自动重试

}


启动项目测试。遇到失败的请求会重新发送,超过最大重试次数以后返回一个空的数据。

这就是我们预期的结果了,剩下的就是提高代理池的质量,就能最大程度的保证爬虫的可用性了。


# 限制下载速度

# DOWNLOAD_DELAY=2

# 开启自动限速,防止反爬

# AUTOTHROTTLE_ENABLED=True

# 初始下载延迟

# AUTOTHROTTLE_START_DELAY=2

# 高延迟最大下载延迟

# AUTOTHROTTLE_MAX_DELAY=30


可以在settings中开启限制爬取速度的配置,使得我们的爬虫项目更接近点击行为操作,提高反反爬效率。

相关文章内容简介
在线客服
大客户VIP渠道
点击这里给我发消息
讨论QQ群
HTTP代理IP爬虫
客服电话
13318873961