摘要:之前在学校曾经用过的方法做过一些爬虫脚本来玩,从正式转前端之后,出于兴趣,我对爬虫和反爬虫又做了一些了解,并且做了一些爬虫攻防的实践。爬虫脚本通常会很频繁的进行网络请求,比如要爬取豆瓣排行榜的电影,就会连续发送个网络请求。
之前在学校曾经用过request+xpath的方法做过一些爬虫脚本来玩,从ios正式转前端之后,出于兴趣,我对爬虫和反爬虫又做了一些了解,并且做了一些爬虫攻防的实践。
我们在爬取网站的时候,都会遵守 robots 协议,在爬取数据的过程中,尽量不对服务器造成压力。但并不是所有人都这样,网络上仍然会有大量的恶意爬虫。对于网络维护者来说,爬虫的肆意横行不仅给服务器造成极大的压力,还意味着自己的网站资料泄露,甚至是自己刻意隐藏在网站的隐私的内容也会泄露,这也就是反爬虫技术存在的意义。
下面开始我的攻防实践。
先从最基本的requests开始。requests是一常用的http请求库,它使用python语言编写,可以方便地发送http请求,以及方便地处理响应结果。这是一段抓取豆瓣电影内容的代码。
import requests from lxml import etree url = "https://movie.douban.com/subject/1292052/" data = requests.get(url).text s=etree.HTML(data) film=s.xpath("//*[@id="content"]/h1/span[1]/text()") print(film)
代码的运行结果,会输出
["肖申克的救赎 The Shawshank Redemption"]
这就是最简单的完整的爬虫操作,通过代码发送网络请求,然后解析返回内容,分析页面元素,得到自己需要的东西。
这样的爬虫防起来也很容易。使用抓包工具看一下刚才发送的请求,再对比一下浏览器发送的正常请求。可以看到,两者的请求头差别非常大,尤其requests请求头中的user-agent,赫然写着python-requests。这就等于是告诉服务端,这条请求不是真人发的。服务端只需要对请求头进行一下判断,就可以防御这一种的爬虫。
当然requests也不是这么没用的,它也支持伪造请求头。以user-agent为例,对刚才的代码进行修改,就可以很容易地在请求头中加入你想要加的字段,伪装成真实的请求,干扰服务端的判断。
import requests from lxml import etree user_agent = "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)" headers = { "User-Agent" : user_agent } url = "https://movie.douban.com/subject/1292052/" data = requests.get(url,headers=headers).text s=etree.HTML(data) film=s.xpath("//*[@id="content"]/h1/span[1]/text()") print(film)提高
现阶段,就网络请求的内容上来说,爬虫脚本已经和真人一样了,那么服务器就要从别的角度来进行防御。
有两个思路,第一个,分析爬虫脚本的行为模式来进行识别和防御。
爬虫脚本通常会很频繁的进行网络请求,比如要爬取豆瓣排行榜top100的电影,就会连续发送100个网络请求。针对这种行为模式,服务端就可以对访问的 IP 进行统计,如果单个 IP 短时间内访问超过设定的阈值,就给予封锁。这确实可以防御一批爬虫,但是也容易误伤正常用户,并且爬虫脚本也可以绕过去。
这时候的爬虫脚本要做的就是ip代理,每隔几次请求就切换一下ip,防止请求次数超过服务端设的阈值。设置代理的代码也非常简单。
import requests proxies = { "http" : "http://111.155.124.78:8123" # 代理ip } user_agent = "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)" headers = { "User-Agent" : user_agent } url = "https://movie.douban.com/subject/1292052/" res = requests.get(url = http_url, headers = headers, proxies = proxies)
第二个思路,通过做一些只有真人能做的操作来识别爬虫脚本。最典型的就是以12306为代表的验证码操作。
增加验证码是一个既古老又相当有效果的方法,能够让很多爬虫望风而逃。当然这也不是万无一失的。经过多年的发展,用计算机视觉进行一些图像识别已经不是什么新鲜事,训练神经网络的门槛也越来越低,并且有许多开源的计算机视觉库可以免费使用。例如可以在python中引入的tesseract,只要一行命令就能进行验证码的识别。
import pytesseract from PIL import Image ... #get identifying code img ... im=Image.open("code.png") result = pytesseract.image_to_string(im)
再专业一点的话,还可以加上一些图像预处理的操作,比如降噪和二值化,提高验证码的识别准确率。当然要是验证码原本的干扰线, 噪点都比较多,甚至还出现了人类肉眼都难以辨别的验证码(12306),计算机识别的准确度也会相应下降一些。但这种方法对于真实的人类用户来说实在是太不友好了,属于是杀敌一千自损八百的做法。
进阶验证码的方法虽然防爬效果好,但是对于真人实在是不够友好,开发人员在优化验证操作的方面也下了很多工夫。如今,很多的人机验证操作已经不再需要输入验证码,有些只要一下点击就可以完成,有些甚至不需要任何操作,在用户不知道的情况下就能完成验证。这里其实包含了不同的隐形验证方法。
有些隐形验证采用了基于JavaScript的验证手段。这种方法主要是在响应数据页面之前,先返回一段带有JavaScript 代码的页面,用于验证访问者有无 JavaScript 的执行环境,以确定使用的是不是浏览器。例如淘宝、快代理这样的网站。通常情况下,这段JS代码执行后,会发送一个带参数key的请求,后台通过判断key的值来决定是响应真实的页面,还是响应伪造或错误的页面。因为key参数是动态生成的,每次都不一样,难以分析出其生成方法,使得无法构造对应的http请求。
有些则更加高级一些,通过检测出用户的浏览习惯,比如用户常用 IP 或者鼠标移动情况等,然后自行判断人机操作。这样就用一次点击取代了繁琐的验证码,而且实际效果还更好。
对于这类的反爬手段,就轮到selenium这个神器登场了。selenium是一个测试用的库,可以调用浏览器内核,也就是说可以打开一个真的浏览器,并且可以手动进行操作。那就完美可以完美应对上述两种隐形验证手段。
selenium的使用也很简单,可以直接对页面元素进行操作。配合根据页面元素等待页面加载完成的时延操作,基本上把人浏览页面的过程整个模拟了一遍。而且因为selenium会打开一个浏览器,所以如果有点击的验证操作,一般这种操作也就在开始的登录页会有,人来点一下就是了。
from selenium import webdriver browser = webdriver.Chrome() browser.get("url") #获得dom节点 node = browser.find_elements_by_id("id") nodes = browser.find_elements_by_css_selector("css-selector") nodelist = browser.find_elements_by_class_name("class-name") #操作dom元素 browser.find_element_by_xpath("xpath-to-dom").send_keys("password") browser.find_element_by_xpath("xpath-to-dom").click() #等待页面加载 locator = (By.CLASS_NAME, "page-content") try: WebDriverWait(driver, 10, 0.5).until(EC.presence_of_element_located(locator)) finally: driver.close()
这么看起来仿佛selenium就是无解的了,实际上并不是。较新的智能人机验证已经把selenium列入了针对目标中,使得即使手动点击进行人机验证也会失败。这是怎么做的呢?事实上,这是对于浏览器头做了一次检测。如果打开selenium的浏览器控制台输入window.navigator.webdriver,返回值会是“true”。而在正常打开的浏览器中输入这段命令,返回的会是“undefined”。在这里,我找到了关于webdriver的描述:navigator.webdriver
)。可以看到,webdriver属性就是用来表示用户代理是否被自动化控制,也就是这个属性暴露了selenium的存在,人机验证就无法通过。而且,这个属性还是只读的,那么就不能直接修改。当然硬要改也不是不行,通过修改目标属性的get方法,达到属性修改的目的。这时的webdriver属性就是undefined了,然后再进行智能人机验证,就可以通过了。但这是治标不治本的,此时如果浏览器打开了新的窗口,或者点击链接进入新的页面,我们会发现,webdriver又变回了true。当然,在每次打开新页面后都输入这段命令也可以,不过事实上,虽然点击验证可以被绕过去,但如果直接在页面中加入检测webdriver的JS代码,一打开页面就执行,那么在你改webdriver之前,网站已经知道你到底是不是爬虫脚本了。
道高一尺,魔高一丈。事实上即使这样的反爬手段,也还是可以绕过去。在启动Chromedriver之前,为Chrome开启实验性功能参数excludeSwitches,它的值为["enable-automation"],像这样
from selenium.webdriver import Chrome from selenium.webdriver import ChromeOptions option = ChromeOptions() option.add_experimental_option("excludeSwitches", ["enable-automation"]) driver = Chrome(options=option) driver.get("url")
这时候,不管怎么打开新页面,webdriver都会是undefined。对于这个级别的爬虫脚本,还不知道要怎么防御,检测的成本太高了。
不过,事实上,换个思路,还有一些有趣的反爬方法。比如猫眼电影的实时票房和起点中文网,在浏览器里能看到内容,但是打开网页代码一看,全变成了方块。这就是一种很好地反爬方法,简单地说就是后端搭一套字体生成接口,随机生成一个字体,然后返回这个字体文件,以及各个数字的unicode对应关系,前端页面进行数据填充,就可以隐藏敏感数据。
还有些充分利用了css进行的反爬,脑洞更大。搞两套数据,显示的时候用css定位将真实的覆盖假的。或者搞一些干扰字符,显示的时候将opacity设为0进行隐藏。甚至还有设置一个背景,让它和显示的内容拼接在一起,成为真正要展示的内容。这些都是非常有趣的反爬手段。
不过对于前端来说,毕竟所有的数据和代码,都给到了客户端,爬虫脚本总是能想出办法来爬到数据,各种反爬的手段,也就是加大爬数据的难度而已。主要还是要自觉,拒绝恶意爬虫。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/45165.html
摘要:之前在学校曾经用过的方法做过一些爬虫脚本来玩,从正式转前端之后,出于兴趣,我对爬虫和反爬虫又做了一些了解,并且做了一些爬虫攻防的实践。爬虫脚本通常会很频繁的进行网络请求,比如要爬取豆瓣排行榜的电影,就会连续发送个网络请求。 之前在学校曾经用过request+xpath的方法做过一些爬虫脚本来玩,从ios正式转前端之后,出于兴趣,我对爬虫和反爬虫又做了一些了解,并且做了一些爬虫攻防的实践...
摘要:极简爬虫攻防战纪要爬虫是构建搜索引擎的基础负责抓取网页信息并对网页识别分类及过滤。爬虫方终于锁定了第一场战役的胜局由于断崖式技术的出现,反爬方在浏览器识别战役上望风披靡。经过反爬方的精心运作,逐渐有效削弱了敌方的攻势。 极简爬虫攻防战纪要 爬虫是构建搜索引擎的基础, 负责抓取网页信息并对网页识别、分类及过滤。我们熟识的电商、搜索、新闻及各大门户网站都有强大的爬虫集群在每...
摘要:而搜索引擎如百度和微软搜索,搜索等通过什么方式才能收录我们的个人网站呢答案是搜索引擎的网络爬虫。网络爬虫是一个很形象的名词,是属于搜索引擎的工具,只有被这些网络爬虫爬过的内容才有机会出现在对应搜索引擎的搜索结果中。 不知道广大程序员朋友们注意到一个现象么?使用百度是无法搜索到淘宝网的网页。为什么会造成这种现象?这就要从网络爬虫说起了。 咱们程序员假如自己搭设个人网站,在上面分享少量自己...
摘要:摘要今年的先知白帽大会,与会者将能够亲身感受到非常多有趣的技术议题,如在国际赛事中屡夺佳绩的团队,其队长将亲临现场,分享穿针引线般的漏洞利用艺术。从数据视角探索安全威胁阿里云安全工程师议题解读本议题讨论了数据为安全人员思维方式带来的变化。 摘要: 今年的先知白帽大会,与会者将能够亲身感受到非常多有趣的技术议题,如HITCON在国际赛事中屡夺佳绩的CTF团队,其队长Orange将亲临现场...
阅读 2789·2021-11-24 09:39
阅读 2546·2021-11-23 09:51
阅读 1797·2021-11-17 09:33
阅读 1735·2021-10-22 09:54
阅读 1870·2021-08-16 11:00
阅读 3418·2019-08-30 15:53
阅读 1731·2019-08-30 13:19
阅读 2899·2019-08-30 12:49