详细解析反爬手段以及处理方案
详细解析反爬手段以及处理方案
前言
互联网时代,无论在工作上,还是生活上都离不开网络,而网络能给我们带来什么?
新闻,小说,资料,各行业的数据或者报表等等;
比如:快毕业了为了论文,在各种网站上爬取需要的数据进行分析;还有一些为了兴趣爱好,爬取各种类型的图片,视频,文章,数据等。
各网站的开发人员为了约束这种行为,开始绞尽脑汁,采取各种手段去约束爬虫,于是,有了反爬机制!
常见反爬机制
1,通过对 User-Agent 过滤来控制访问
无论是浏览器,程序,还是爬虫,在向服务器发起网络请求时,都会先发送一个请求头文件 headers ,
比如:
{
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,zh-HK;q=0.8",
"Host": "httpbin.org",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-5fe2b4fe-6e4edc1c4dbbe85a3c25492b"
}
}
# "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"
请求头大部分的字段主要是浏览器向服务端 “表明自己的身份”用的,很多网站都会建立 user-agent 白名单,只有在正常范围内的 user-agent 才能正常访问 。
user-agent 是一个阅读器标志,用户都是一中阅读器,网站很粗糙的辨别你有咩有作弊,必须要结构不同的阅读器标志,不然就会认为你是爬虫,宁杀错,不放过,你说气不气;
缺点:很容易伪造头部
处理方案:
修改阅读器标志,模拟其他阅读器的标志(定义一个标志库,随机获取一个),能够通过API接口实现各种阅读器的收集模拟;
# 定义 user-agent/标志库
# 第一种方法
def get_user_agent():
"""
模拟headers的user-agent字段,
返回一个随机的user-agent字典类型的键值对
"""
agents = [
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0;) Gecko/20100101 Firefox/61.0",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15",
]
fakeheader = {}
fakeheader['User-agent'] = agents[random.randint(0,len(agents))]
return fakeheader
def get_html(url):
try:
r= requests.get(url, timeout=30,headers=get_user_agent())
r.raise_for_status
r.encoding = r.apparent_encding
return r.status_code
except:
return "someting wrong!"
# 第二种方法
def get_html():
agents = [...]
headers = {
'User-Agent': random.choice(user_agent_list),
'Referer': 'https://www.baidu.com',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7',
'Cookie': '...'
}
try:
r= requests.get(url, timeout=30,headers=headers)
r.raise_for_status
r.encoding = r.apparent_encding
return r.status_code
except:
return "someting wrong!"
2,IP访问频率限制
如果一个固定的IP在短暂的时间内,访问太快或者快速大量的访问一个网站,网站就会认为你不是一个人后台管理员可以编写IP限制,不让该IP继续访问
解决方案:
设定频率的阈值,限制访问时间;
比如,每隔5秒访问一次页面,但是如果遇到比较***钻的网站,会去检测你的访问时间,如果你长时间访问了几十个页面,且每次访问的时间间隔都刚好5秒钟,又不是机器怎么可能做到这么准确的时间间隔呢?如果使用的是selenium来访问的话,也就不会出现这个问题了,因为它本来也要打开页面,虽然效率不咋地,但是绕开了频率检查的反爬机制。
访问时间设定一个随机值。例如0-10之间的随机秒数
修改访问频率的场景如下:
(1)使用 request 脚本爬虫
# 放在request请求之后
random = random.randint(3,10)
time.sleep(random)
(2)scrapy 脚本爬虫或者scrapy_redis 分布式爬虫
RETRY_TIMES = 10 # 重新请求次数
DOWNLOAD_DELAY = 1 # 间隔时间
CONCURRENT_REQUESTS = 5 # 请求并发数
https://www.cnblogs.com/beiyi888/p/11283823.html
(3)针对业务性写爬虫
个别网站,比如,hwt,服务器会限制你的访问频率,但是不会封IP,页面将持续显示403(服务器拒绝访问),偶尔显示200(请求成功),那么就证明(前提是我们设置过请求头等信息),这样的反爬机制,只是限制了请求的频率,但是并不会影响到正常的内容采集,这种可以忽略的情况并不多见,所以我们还是更具网站业务有针对性的编写爬虫
(4)服务器性能原因,相应较慢(响应超时,导致终止请求)
小网站出现的比较多(DYW),在我们将请求的参数都安排好后,却发现,由于服务器的性能原因,采集程序持续报网页404,出现这种情况只能延长响应超时的时长;
try:
resp = requests.get(url=url,headers=headers,proxies=proxies,verify=False,timeout=120)
(5)代理IP或者分布式爬虫
如果对页的爬虫的效率有要求,那就不能通过设定访问时间间隔的方法来绕过频率检查了。
代理IP访问可以解决这个问题。如果用100个代理IP访问100个页面,可以给网站造成一种100个人,每个人访问1页的错觉。
但是代理IP经常不稳定,随便搜一个”免费代理“,会出现很多网站,每个网站也会给你很多的代理IP,但实际上,真正可以用的代理IP并不多。你需要维护一个可用的代理IP池,但是一个免费的代理IP,也许会在几分钟后失效,这也是很正常的事情。网上有免费和付费的,但是质量都层次不齐。如果是企业里需要的话,可以通过自己购买集群云服务来自建代理池。
可以使用http://icanhazip.com/ 这个网站来检测你的代理IP是否设定成功。当你直接使用浏览器访问这个网站的时候,它会返回你的IP地址。
通过requests,我们可以设置代理访问网站,在requests的get方法中,有一个proxies参数,它接收的数据是一个字典,在这个字典中我们可以设置代理。
大家可以在requests的官方中文文档中看到关于设置代理的更多信息:http://docs.python-requests.org/zh_CN/latest/user/advanced.html#proxies
我选择第一个HTTP类型的代理来给大家做测试,运行效果如下图所示:
import requests
proxy = {'http': 'http://182.253.69.34:8080'}
html = requests.get('http://icanhazip.com/')
html_proxy = requests.get('http://icanhazip.com/', proxies=peoxy)
分布式爬虫会部署在多台服务器上,每个服务器上的爬虫统一从一个地方拿网址。这样平均下来每个服务器访问网站的频率也就降低了。
设定IP代理池
def get_proxy():
"""
模拟代理池
返回一个字典类型的键值对
"""
proxy = [
"http://182.253.69.34:8080",
"http://39.81.63.233:9000",
"http://58.253.153.164:9999",
"http://182.92.233.137:8118",
"http://41.65.201.162:8080",
"http://45.224.149.219:8080",
]
fakepxs = {}
fakepxs['http'] = proxy[random.randint(0, len(proxy))]
return fakepxs
验证代理IP是否失效?
ip.txt 文件的内容如下:
182.253.69.34:8080
39.81.63.233:9000
58.253.153.164:9999
182.92.233.137:8118
41.65.201.162:8080
142.93.57.37:80
45.224.149.219:8080
27.220.49.194:9000
5.9.112.247:3128
36.250.156.230:9999
36.249.48.6:9999
proxy_agent.py 文件
import random
import requests
ua_list = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36",
"Mozilla / 5.0(Windows NT 6.1;WOW64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 45.0.2454.101Safari / 537.36"
]
def _load_text_file(txt_file):
"""
加载text文件
:param txt_file:
:return:
"""
text_content = []
with open(txt_file, 'r', encoding='utf-8-sig') as text_file:
while True:
content = text_file.readline()
if not content:
break
line = content.strip('\n')
text_content.append(line)
FileUtils._check_format(txt_file, text_content)
return text_content
# 随机取出一个Ip
def get_random_ip(path):
black_ = []
client = _load_text_file(path)
print(client)
url_for_test = 'https://baidu.com/'
user_agent = random.choice(ua_list)
headers = {
'Accept': 'text/html, application/xhtml+xml, application/xml;',
'Accept-Encoding': 'gzip, deflate, sdch',
'Accept-Language': 'zh-CN,zh;q=0.8',
'Referer': 'https://baidu.com/',
'User-Agent': user_agent
}
for useful_proxy in client:
proxy = {
'http': 'http://' + useful_proxy,
#'https': 'http://' + useful_proxy
}
response = requests.get(url_for_test,headers=headers,proxies=proxy,timeout=10)
if response.status_code == 200:
return useful_proxy
else:
print('此ip - {0} 已失效!!!'.format(useful_proxy))
black_.append(useful_proxy)
get_random_ip(path)
if __name__ == '__main__':
path = r'ip.txt'
print(get_random_ip(path))
session 访问限制
后台统计登录用户的操作,比如短时间的点击时间,请求数据事件,与正常值比对,用于区分用户是否处理异常状态,如果是,则限制登录用户操作权限。比如,每次访问之后,铲除缓存,这样能有用躲避部分网站的检测,如果都是新链接从IP宣布,也会被断定回绝(直接403回绝访问),因此有些爬虫会去剖析网站的cookies 缓存内容,然后进行修改。
缺点:需要增加数据埋点功能,阈值设置不好,容易造成误操作
解决方案:注册多个账号,模拟正常操作
Spider Trap
蜘蛛陷阱导致网络爬虫进入无限循环之类的东西,这回浪费蜘蛛的资源,降低生产力,并且在编写得不好得爬虫得情况下,可能导致程序崩溃。礼貌蜘蛛在不同主机之间交替请求,并且不会每隔几秒钟从同一服务器请求多次文档,这意味着”礼貌“网络爬虫比”不礼貌“爬虫得影响程度要小得多。
反爬方式:创建无限深度得目录结构
缺点:反爬方式1,2会增加很多无用目录或文件,造成资源浪费,也对正常的SEO十分不友好,可能会被惩罚。
# 比如:
HTTP://example.com/bar/foo/bar/foo/bar/foo/bar /
动态页面,为网络爬虫生成无限数量的文档。如由算法生成杂乱的文章页面。
文档中填充了大量字符,使解析文档的词法分析器崩溃。
此外,带蜘蛛陷阱的网站通常都有robots.txt告诉机器人不要进入陷阱,因此合法的“礼貌”机器人不会陷入陷阱,而忽视robots.txt设置的“不礼貌”机器人会受到陷阱的影响。
解决方案:
把网页按照所引用的css文件进行聚类,通过控制类里最大能包含的网页数量防止爬虫进入trap后出不来,对不含css的网页会给一个penalty,限制它能产生的链接数量。这个办法理论上不保证能避免爬虫陷入死循环,但是实际上这个方案工作得挺好,因为绝大多数网页都使用了css,动态网页更是如此。
验证码验证
验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式,我们利用比较简易的方式实现了这个功能。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于 计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。
(1)图片验证码
复杂性
打码平台雇佣了人力,专门帮人识别验证码。识别完把结果传回去。总共的过程用不了几秒时间。这样的打码平台还有记忆功能。图片被识别为“锅铲”之后,那么下次这张图片再出现的时候,系统就直接判断它是“锅铲”。时间一长,图片验证码服务器里的图片就被标记完了,机器就能自动识别了。
简单型
OCR识别技术(利用python第三方库--tesserocr)来识别,经过灰度变换和二值化后,由模糊的验证码背景变成清晰可见的验证码。对于容易迷惑人得图片验证码,在这种验证码,语言一般自带图形库,添加上扭曲就成了这个样子,我们可以利用9万张图片进行训练,完成类似人的精准度,到达识别验证码的效果
(2)短信验证码
用Webbrowser技术,模拟用户打开短信的行为,最终获取短信验证码。
(3)计算题图片验证码
把所有可能出现的汉字都人工取出来,保存为黑白图片,把验证码按照字体颜色二值化,去除噪点,然后将所有图片依次与之进行像素对比,计算出相似值,找到最像的那张图片
(4)滑动验证码
我们可以利用图片的像素作为线索,确定好基本属性值,查看位置的差值,对于差值超过基本属性值,我们就可以确定图片的大概位置。
(5)图案验证码
对于这种每次拖动的顺序不一样,结果就不一样,我们怎么做来识别呢?
利用机器学习所有的拖动顺序,利用1万张图片进行训练,完成类似人的操作,最终将其识别
利用selenium技术来模拟人的拖动顺序,穷尽所有拖动方式,这样达到是别的效果
(6)标记倒立文字验证码
首先点击前两个倒立的文字,可确定7个文字的坐标, 验证码中7个汉字的位置是确定的,只需要提前确认每个字所在的坐标并将其放入列表中,然后人工确定倒立文字的文字序号,将列表中序号对应的坐标即可实现成功登录。
解决方法:接入第三方验证码平台,实时破解网站得验证码
缺点:影响正常得用户体验操作,验证码越复杂,网站体验感越差。
通过robots.txt 来限制爬虫
robots.txt(统一小写)是一种存放于网站根目录下的ASCII编码的文本文件,它通常告诉网络搜索引擎的漫游器(又称网络蜘蛛),此网站中的哪些内容是不应被搜索引擎的漫游器获取的,哪些是可以被漫游器获取的。因为一些系统中的URL是大小写敏感的,所以robots.txt的文件名应统一为小写。robots.txt应放置于网站的根目录下。如果想单独定义搜索引擎的漫游器访问子目录时的行为,那么可以将自定的设置合并到根目录下的robots.txt,或者使用robots元数据(Metadata,又称元数据)。
robots.txt协议并不是一个规范,而只是约定俗成的,所以并不能保证网站的隐私。注意robots.txt是用字符串比较来确定是否获取URL,所以目录末尾有与没有斜杠“/”表示的是不同的URL。robots.txt允许使用类似"Disallow: *.gif"这样的通配符。
https://itunes.apple.com/robots.txt
User-agent: *
Disallow: /WebObjects/MZFastFinance.woa
Disallow: /WebObjects/MZFinance.woa
Disallow: /WebObjects/MZPersonalizer.woa
Disallow: /WebObjects/MZStoreElements.woa
Disallow: /station/idst.
Disallow: /WebObjects/*
Allow: /WebObjects/MZStore.woa/wa/viewMultiRoom?*
Disallow: /search*
Disallow: /*/rss/*
Disallow: /*/lookup?
User-agent: Googlebot
Disallow: /*/album/*/*?i=*
Disallow: /*/tv-season/*/*?i=*
Disallow: /*/podcast/*/*?i=*
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_1.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_2.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_3.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_4.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_5.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_6.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_7.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_8.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_9.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_10.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_31.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_41.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_51.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_61.xml
可以参考 https://www.moyublog.com/notes/323.html
解决方案:
如果使用scrapy框架,只需将settings文件里的ROBOTSTXT_OBEY 设置值为 False
数据动态加载
python的requests库只能爬取静态页面,爬取不了动态加载的页面。使用JS加载数据方式,能提高爬虫门槛。
解决方案:
抓包获取数据url
通过抓包方式可以获取数据的请求url,再通过分析和更改url参数来进行数据的抓取。
示例:
看 https://image.baidu.com 这部分的包。可以看到,这部分包里面,search下面的那个 url和我们访问的地址完全是一样的,但是它的response却包含了js代码。
通过抓包工具,寻找URL得规律,对URL进行构造便可获取所有照片
使用selenium
通过使用selenium来实现模拟用户操作浏览器,然后结合BeautifulSoup等包来解析网页通过这种方法获取数据,简单,也比较直观,缺点是速度比较慢。
缺点:如果数据API没做加密处理,容易曝光接口,让爬虫用户更容易获取数据。
数据加密-使用加密算法
前端加密
通过对查询参数、user-agent、验证码、cookie等前端数据进行加密生成一串加密指令,将加密指令作为参数,再进行服务器数据请求。该加密参数为空或者错误,服务器都不对请求进行响应。
网站的请求如果加密过,那就看不清请求的本来面目,这时候只能靠猜想,通常加密会选用简略的编码,如:base64、urlEncode等,如果过于复杂,只能穷尽的去尝试;
服务器端加密
在服务器端同样有一段加密逻辑,生成一串编码,与请求的编码进行匹配,匹配通过则会返回数据。
缺点:加密算法明文写在JS里,爬虫用户还是可以分析出来。
解决方案:
JS加密破解方式,就是要找到JS的加密代码,然后使用第三方库js2py在Python中运行JS代码,从而得到相应的编码。
案例参考:
https://blog.csdn.net/lsh19950928/article/details/81585881
数据加密-使用字体文件映射
服务器端根据字体映射文件先将客户端查询的数据进行变换再传回前端,前端根据字体文件进行逆向解密。
映射方式可以是数字乱序显示,这样爬虫可以爬取数据,但是数据是错误的。
破解方式:
其实,如果能看懂JS代码,这样的方式还是很容易破解的,所以需要做以下几个操作来加大破解难度。
对JS加密
使用多个不同的字体文件,然后约定使用指定字体文件方式,比如时间戳取模,这样每次爬取到的数据映射方式都不一样,映射结果就不一样,极大提高了破解的难度。
该种方式相比使用加密算法方式难度更高,因为加密算法是固定的几种,对方很容易获取并破解,而字体文件映射可以按任意规则映射,正常的数据使之错误显示,爬虫不容易察觉。
参考案例:https://www.jianshu.com/p/f79d8e674768
缺点:需要生成字体文件,增加网站加载资源的体量。
非可视区域遮挡
此方式主要针对使用senlium进行的爬虫,如果模拟界面未进入可视区域,则对未见数据进行遮挡,防止senlium的click()操作。这种方式只能稍稍降低爬虫的爬取速度,并不能阻止继续进行数据爬取。