本文首发于个人博客,文章链接为:https://blog.d77.xyz/archives/860d8413.html
前言
本篇文章是这个爬虫练习平台的最后一篇了,由于是学习 Scrapy,所以前面跳过了验证码逆向的题目,后面跳过了 APK 逆向的题目,验证码和 APK 看情况以后再单独写文章。本篇文章写使用代理 IP 突破 IP 地址反爬。
环境配置
本次使用的环境和 ssr1 的环境是一样的,不使用 selenium,数据库仍然使用 Mongo。
在开始写代码之前还需要搭建一个代理池,由于这里只是学习 Scrapy,就不自己搭建了,使用 GitHub 开源的 proxy_pool,就可以满足需求。
代理池地址在这里,搭建教程在 README 中已经写得很清楚了,就不重复了。
但是有一点内容要强调一下,记得将代理验证 URL 改为需要爬取的网站,因为代理池里边的代理 IP 不一定对所有的网站都是可以代理的,默认是能访问百度就认为代理 IP 可用, 但是可能这个代理 IP 已经被对应网站封掉了,所以需要针对性的检测才可以得到质量相对比较高的代理池。
开始爬取
antispider5
antispider5 说明如下:
限制单个 IP 访问频率 5 分钟最多 10 次,如果过多则会封禁 IP 10 分钟。
访问过多会封禁 IP,限制条件是单个 IP 的访问次数。
手动刷新几次,被封禁了,状态码是 403,说明 IP 被封禁时会返回 403,在爬虫代码中要注意判断 403 错误代码,在遇到 403 时要更换代理 IP 重试请求。
开始写代码。
class Antispider5DownloaderMiddleware(LearnscrapyDownloaderMiddleware):
def __init__(self):
super(Antispider5DownloaderMiddleware, self).__init__()
@property
def proxy(self):
return 'http://' + requests.get('http://192.168.233.128:5010/get').json()['proxy']
def process_request(self, request: Request, spider):
# 将请求加上代理
request.meta['proxy'] = self.proxy
def process_response(self, request, response: Response, spider):
# 如果响应代码为403,则将请求更换一个新代理重新请求
if response.status == 403:
print(f'重试:{response.url}')
request.meta['proxy'] = self.proxy
return request
else:
return response
其他代码和之前爬取的代码基本一致,唯一不同的就是下载中间件这里,继承原来的下载中间件,重写两个方法 process_request
和 process_response
,其中 process_request
用来在每个请求被发出去之前添加一个代理 IP,process_response
用来判断响应的状态码,如果状态码为403,证明代理 IP 已经被封禁,需要更换代理 IP,之后返回一个 request 对象,稍后调度器会将它重新添加到下载引擎,重新下载。如果状态码正常,返回 response 对象, 正常对响应进行处理。
在 process_response
还可以添加以下删除代理 IP 的代码,在代理 IP 返回 503 错误代码或者其他错误代码时,删除已经不可用的代理 IP,防止大量失败的重试降低爬取效率。
运行爬虫,查看日志输出。
可以看到有一些代理在我之前的调试过程中已经访问过网站几次,再次访问就被 ban 了,需要更换新的代理 IP 重新请求。
稍等片刻,时间长短取决于代理 IP 的质量,待爬虫爬取结束查看数据是否完整。
完整代码详见https://github.com/libra146/learnscrapy/tree/antispider5
Antispider6
antispider6 说明如下:
限制单个账号访问频率 5 分钟最多 10 次,如果过多则会暂停访问 10 分钟。
限制条件为账号限制,那么就对症下药,准备多个帐号,来将请求分散到多个账号中,逃过单个账号频率限制。
首先需要使用脚本注册多个账号备用。
"""
注册多个账号备用,将多个请求分散到多个账号中。
"""
import requests
def register():
url = 'https://antispider6.scrape.center/register'
# 简单设置账号密码邮箱为同一个值
for a in range(10):
d = f'12345-{a}@qq.com'
data = {
'username': d,
'email': d,
'password1': d,
'password2': d
}
r = requests.post(url=url, data=data)
print(f'12345-{a}@qq.com-{r.status_code}')
if __name__ == '__main__':
register()
很简单的脚本,批量循环注册就可以了。
在 scrapy 启动时先登录这一批账号,建立 cookie 池,之后在发送请求时从 cookie 池中随机选取 cookie 替换当前的 cookie。
调试过程中,发现登录请求只会被发送一次,cookie 池中也只有一条数据,但是日志太少,无法找到问题来源。开启 DEBUG 日志后发现产生了这么一条日志:
2020-10-26 19:59:21 [scrapy.dupefilters] DEBUG: Filtered duplicate request: <GET https://antispider6.scrape.center