反爬虫策略
- 动态设置User-Agent(随机切换User-Agent,模拟不同用户的浏览器信息)
- 禁用Cookies, 有些网站通过cookie的使用信息发现爬虫行为
- scrapy中可以通过COOKIES_ENABLED 控制 CookiesMiddleware 开启或关闭
- 设置延迟下载(防止访问过于频繁,设置为 2秒 或更高)
- Google Cache 和 Baidu Cache:如果可能的话,使用谷歌/百度等搜索引擎服务器页面缓存获取页面数据。
- 使用IP地址池. scrapy设置downloadmiddleware
开启下载中间件
- 说明:
- 当引擎传递请求给下载器的过程中,下载中间件可以对请求进行处理 (例如增加http header信息,增加proxy信息等)
- 在下载器完成http请求,传递响应给引擎的过程中, 下载中间件可以对响应进行处理(例如进行gzip的解压等)
DOWNLOADER_MIDDLEWARES = {
'mySpider.middlewares.MyDownloaderMiddleware': 543,
}
主要方法
process_request(self, request, spider)
- 说明:
- 当每个request通过下载中间件时,该方法被调用。
- 须返回以下其中之一:None, Response 对象, Request 对象, raise IgnoreRequest
- 返回 None, Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用, 该request被执行(其response被下载)。
- 返回 Response 对象,Scrapy将不会调用 任何 其他的 process_request() 或 process_exception() 方法,或相应地下载函数; 其将返回该response。 已安装的中间件的 process_response() 方法则会在每个response返回时被调用。
- 返回 Request 对象,Scrapy则停止调用 process_request方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。
- raise一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。
process_response(self, request, response, spider)
- 说明:
- 当下载器完成http请求,传递响应给引擎的时候调用
- 必须返回以下其中之一: 返回Response 对象, 返回Request 对象, raise一个 IgnoreRequest 异常。
- 返回 Response (可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。
- 返回 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。
- 如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。
设置user-agent与proxy
USER_AGENTS = [
"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"
]
PROXIES = [
{'ip_port': '111.8.60.9:8123', 'user_passwd': 'user1:pass1'},
{'ip_port': '101.71.27.120:80', 'user_passwd': 'user2:pass2'},
{'ip_port': '122.96.59.104:80', 'user_passwd': 'user3:pass3'},
{'ip_port': '122.224.249.122:8088', 'user_passwd': 'user4:pass4'},
]
COOKIES_ENABLED = False
DOWNLOAD_DELAY = 3
DOWNLOADER_MIDDLEWARES = {
'mySpider.middlewares.RandomUserAgent': 50,
'mySpider.middlewares.ProxyMiddleware': 100
}
import random
import base64
from settings import USER_AGENTS
from settings import PROXIES
class RandomUserAgent(object):
def process_request(self, request, spider):
useragent = random.choice(USER_AGENTS)
request.headers.setdefault("User-Agent", useragent)
class RandomProxy(object):
def process_request(self, request, spider):
proxy = random.choice(PROXIES)
if proxy['user_passwd'] is None:
request.meta['proxy'] = "http://" + proxy['ip_port']
else:
base64_userpasswd = base64.b64encode(proxy['user_passwd'])
request.headers['Proxy-Authorization'] = 'Basic ' + base64_userpasswd
request.meta['proxy'] = "http://" + proxy['ip_port']
中间件处理代理ip异常
- 对失败的请求, scrapy默认重试3次, 如果都失败就放弃该请求.
- 通常购买的ip代理, 每分钟才会更换一次ip, 如果ip不稳定或者被封ip, 重试3次都会失败.
- 重试失败时, scrapy抛出 TCPTimeOutError 异常.
- 在中间件捕获这个异常, 进行重试或更换ip
from twisted.internet.error import TCPTimedOutError
import scrapy
class RetryCustomMiddleware(RetryMiddleware):
def __init__(self):
RetryMiddleware.__init__(self, settings)
def process_response(self, request, response, spider):
if spider.name = 'retry_spider':
if response.status == 404:
origin_url = request.meta['redirect_urls'][0]
return request.replace(
url=origin_url,
)
return response
def process_exception(request, exception, spider):
if isinstance(exception, TCPTimedOutError):
self.remove_broken_proxy(request.meta['proxy'])
return request.copy()
def remove_broken_proxy(self, proxy):
pass