反反爬虫相关机制
(有些网站使用不同程度的复杂性规则防止爬虫访问,绕过这些规则是困难和复杂的,有时可能需要特殊的设置)
通常反爬措施
1. 基于请求头
动态设置User-Agent(随机切换User-Agent,模拟不同用户的浏览器信息)
2. 基于cookie的反爬
禁用Cookies(前提是爬取的网站不需要cookies参数)
(cookie池,文件存储,数据库存储)
(如何获取cookies,如何验证cookie,如何进行模拟登陆)可以使用request,手动添加,使用selenium
3. 基于IP
- 代理:代理的原理?,付费代理,免费代理?代理池
4. 基于动态加载的网页
- ajax
- js
- jq
- (使用selenium)
- 无头浏览器,有头浏览器,selenium的方法?
5. 数据加密?(一般会写在js代码里)
- app
- web网页
- 使用selenium
针对于反爬手段
方案一
使用 Crawlera(专用于爬虫的代理组件),正确配置和设置下载中间件后,项目所有的request都是通过crawlera发出。 官方网站:https://scrapinghub.com/crawlera 参考网站:https://www.aliyun.com/jiaocheng/481939.html
方式二
自定义下载中间件(Downloader Middlewares)
下载中间件是处于引擎(crawler.engine)和下载器(crawler.engine.download())之间的一层组件,可以用来修改Request和Response。
当引擎传递请求(Request)给下载器的过程中,下载中间件可以对请求进行处理 (例如增加http header信息(User-Agent),增加proxy代理等);
在下载器完成http请求,传递响应给引擎的过程中, 下载中间件可以对响应进行处理
要激活下载器中间件组件,将其加入到 DOWNLOADER_MIDDLEWARES 设置中。 该设置是一个字典(dict),键为中间件类的路径,值为其中间件的优先级。值越低,代表优先级越高。
自定义中间键需要了解middlewares.py下载中间件相关方法
每个中间件组件是一个定义了以下一个或多个方法
class DownloadwareDownloaderMiddleware(object):
# Not all methods need to be defined. If a method is not defined,
# scrapy acts as if the downloader middleware does not modify the
# passed objects.
@classmethod
def from_crawler(cls, crawler):
从settings中获取值
# This method is used by Scrapy to create your spiders.
s = cls()
#信号量链接
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
def process_request(self, request, spider):
所有的request请求在交给下载器之前都会经过这个
# Called for each request that goes through the downloader
# middleware.
# Must either:
# - return None: continue processing this request
# - or return a Response object
# - or return a Request object
# - or raise IgnoreRequest: process_exception() methods of
# installed downloader middleware will be called
return None
def process_response(self, request, response, spider):
# Called with the response returned from the downloader.
所有的响应结果会经过这个方法
# Must either;
# - return a Response object 返回是个相应结果
# - return a Request object 会把request放在调度器进行调度
# - or raise IgnoreRequest 异常
return response
def process_exception(self, request, exception, spider):
# Called when a download handler or a process_request()
# (from other downloader middleware) raises an exception.
处理异常错误
# Must either:
# - return None: continue processing this exception
# - return a Response object: stops process_exception() chain
# - return a Request object: stops process_exception() chain
pass
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
from_crawler(cls, crawler)
如果存在该函数,from_crawler会被调用使用crawler来创建中间器对象,必须返回一个中间器对象,通过这种方式,可以访问到crawler的所有核心部件,如settings、signals等
def process_request(self, request, spider)
当每个request对象通过下载中间件时该方法被调用。
process_request() 必须返回以下其中之一:
1. None
- 如果返回 None: Scrapy将继续处理request,执 行其他的中间件的相应方法。
2. Response 对象
- 如果返回 Response 对象: Scrapy不会再调用任 何其他的中间件的 process_request() 或相应地下 载函数; 直接返回这个response对象。 已激活的中间件的 process_response()方法则会在 每个 response 返回时被调用。
3. Request 对象
- 如果返回 Request 对象,Scrapy则停止调用 其他中间件的process_request方法,并重新将返回的 request对象放置到调度器等待下载。
4. IgnoreRequest异常
- 如果返回raise IgnoreRequest 异常: 下载中间件的 process_exception() 方法会被用。 如果没有捕获该异常, 则request发情请求时设置的 errback(Request.errback)方法会被调用。如果也 没有设置异常回调,则该异常被忽略且不记录。
process_request()有两个参数:
- request (Request 对象) – 处理的request
- spider (Spider 对象) – 该request对应的spider
process_response(self, request, response, spider)
当下载器完成http请求,传递Response给引擎的时调用
优先级越高的中间件,越晚被调用,与process_request()相反
process_response() 必须返回以下其中之一:
1. Response 对象
如果返回 Request: 更低优先级的下载中间件的 process_response方法不会继续调用,该Request会被 重新放到调度器任务队列中等待调度,相当于一个新的 Request。
(scrapy会继续调用其他中间件的process_response方法)
2. Request 对象
如果返回 Response 对象,更低优先级的下载中间 件的process_response方法会被继续调用对Response对象进行处理
(停止中间器调用,将其放置到调度器待调度下载)
3. IgnoreRequest异常
如果抛出 IgnoreRequest 异常,则调用request 设置的errback(Request.errback)函数。 如果异常没有 被处理,则该异常被忽略且不记录。
(会被调用来处理函数,如果没有处理,它将会被忽略且不会写进日志)
process_response()有三个参数:
- request (Request 对象) – response所对应的request
- response (Response 对象) – 被处理的response
- spider (Spider 对象) – response所对应的spider
process_exception(request, exception, spider)
当process_exception()和process_request()抛出异常时会被调用,应该返回以下对象:None/Response对象/Request对象;
如果返回None:scrapy会继续调用其他中间件的process_exception();
如果返回Response对象:中间件链的process_response()开始启动,不会继续调用其他中间件的process_exception();
如果返回Request对象:停止中间器的process_exception()方法调用,将其放置到调度器待调度下载。
User-Agent
class UserAgentDown(object):
def process_request(self,request,spider):
from fake_useragent import UserAgent
userAgent = UserAgent()
#引入第三方
random_ua = userAgent.random
if random_ua:
print('经过了下载中间件',random_ua)
request.headers["User-Agent"] = random_ua
---------------------------------------------------------------
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",
]
class UserAgentDown(object):
def __init__(self,User_Agents):
self.User_agents = User_Agents
@classmethod
def from_crawler(cls,crawler):
#在settings中提前设置好
User_Agent = crawler.settings['USER_AGENTS']
return cls(User_Agent)
import random
#spider.settings和crawler.settings结果一样
#User_Agent = spider.settings['USERAGENT']
random_ua = random.choice(User_Agent)
if random_ua:
print('经过了下载中间件',random_ua)
request.headers["User-Agent"] = random_ua
代理
为什么HTTP代理要使用base64编码:
HTTP代理的原理很简单,就是通过HTTP协议与代理服务器建立连接,协议信令中包含要连接到的远程主机的IP和端口号,如果有需要身份验证的话还需要加上授权信息,服务器收到信令后首先进行身份验证,通过后便与远程主机建立连接,连接成功之后会返回给客户端200,表示验证通过,就这么简单,下面是具体的信令格式:
我们在settings.py中
PROXIES = [
{"ip":"127.0.0.1:6379","pwd":"ljk123456"},
{"ip":"127.0.0.1:6379","pwd":None},
{"ip":"127.0.0.1:6379","pwd":None},
{"ip":"127.0.0.1:6379","pwd":None},
]
------------------------------------------------------
class ProxyDownloadMiddlerware(object):
def process_request(self,request,spider):
proxies = spider.settings['PROXIES']
import random
proxy_rm = random.choice(proxies)
if proxy_rm['pwd']:
#有账号密码的代理
#对账号密码进行base64编码
import base64
base64_pwd = base64.b64encode(proxy_rm['pwd'].encode('utf-8')).decode('utf-8')
# 对应到代理服务器的信令格式里
request.headers['Proxy-Authorization'] = 'Basic ' + base64_pwd
#设置ip
request.meta['proxy'] = proxy_rm['ip']
else:
request.meta['proxy'] = proxy_rm['ip']
Cookie
同样现在settings.py中设置好cookie
class CookiesDownloadMiddlerware(object):
def process_request(self,request,spider):
Cookies = spider.settings['COOKIES']
import random
cookie_rm = random.choice(Cookies)
if cookie_rm:
request.cookies = cookie_rm
srapy从不支持动态加载
因此
4. 设置selenium中间件。
class SeleniumDownloadMiddlerware(object):
def __init__(self):
self.driver = webdriver.Firefox(
executable_path='E://Firefox/geckodriver'
)
# 设置超时时间
self.driver.set_page_load_timeout(10)
def process_request(self,request,spider):
if spider.name == 'test':
#获取url
url = request.url
if url:
try:
self.driver.get(url)
page = self.driver.page_source
if page:
return HtmlResponse(url=url,status=200,body=page.encode('utf-8'),request=request)
except TimeoutException as err:
print("请求超时",url)
return HtmlResponse(url=url, status=408, body=b"", request=request)
会发现selenium的浏览器一直处于打开状态
在scrapy.Spider中的
def _set_crawler(self, crawler):
self.crawler = crawler
self.settings = crawler.settings
#监控爬虫结束
crawler.signals.connect(self.close, signals.spider_closed)
改进之后
爬虫文件
class TestDemoSpider(scrapy.Spider):
name = 'test_demo'
allowed_domains = ['baidu.com']
start_urls = ['http://www.baidu.com/']
driver = webdriver.Firefox(
executable_path='E://Firefox/geckodriver'
)
# 设置超时时间
driver.set_page_load_timeout(10)
------------------------------------------------------
class SeleniumDownloadMiddlerware(object):
@classmethod
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
# 信号量链接
crawler.signals.connect(s.close, signal=signals.spider_closed)
return s
def close(self,spider):
#driver就写在spider中
spider.driver.close()
def process_request(self,request,spider):
if spider.name == 'test':
#获取url
url = request.url
if url:
try:
spider.driver.get(url)
page = spider.driver.page_source
if page:
"""
self, url, status=200, headers=None, body=b''(相应结果), flags=None, request=None):
self.headers = Headers(headers or {}
"""
return HtmlResponse(url=url,status=200,body=page.encode('utf-8'),request=request)
except TimeoutException as err:
print("请求超时",url)
return HtmlResponse(url=url, status=408, body=b"", request=request)
切记
激活中间件
SPIDER_MIDDLEWARES = {
'downloadware.middlewares.DownloadwareSpiderMiddleware': 543,
}