当前位置: 首页 > Web前端 > HTML

爬虫攻防实践

时间:2023-04-02 11:55:55 HTML

之前在学校用request+xpath的方式做了一些爬虫脚本。在我正式从ios转到前端后,出于兴趣,我对爬虫和反爬虫有了更多的了解,也做了一些爬虫攻防的实践。我们在爬取网站的时候会遵守robots协议,在爬取数据的过程中尽量不给服务器造成压力。但并不是所有人都是这样,互联网上还是会存在大量的恶意爬虫。对于网络维护者来说,爬虫的肆意横行不仅给服务器带来了极大的压力,也意味着自己网站的信息泄露,甚至他们刻意隐藏在网站上的隐私内容也会被泄露。这就是反爬虫技术存在的意义。下面开始我的攻防练习吧。让我们从最基本的请求开始。requests是一个常用的http请求库,用python语言编写,可以方便的发送http请求和处理响应结果。这是一段抓取豆瓣电影内容的代码。importrequestsfromlxmlimportetreeurl='https://movie.douban.com/subject/1292052/'data=requests.get(url).texts=etree.HTML(data)film=s.xpath('//*[@id="content"]/h1/span[1]/text()')print(film)代码会输出['ShawshankRedemptionTheShawshankRedemption']这是最简单的完整爬虫操作,通过发送网络请求代码,然后解析返回的内容,分析页面元素,得到你需要的。防止此类爬虫也非常容易。使用抓包工具查看刚刚发送的请求,与浏览器发送的正常请求进行对比。可以看出两者的请求头差别很大,尤其是requests请求头中的user-agent赫然写的是python-requests。这相当于告诉服务器这个请求不是真人发出的。服务器只需要对请求头做判断就可以防御这种爬虫。当然requests也不是那么没用,它也支持伪造请求头。以user-agent为例,通过修改刚才的代码,可以很方便的在请求头中添加自己想要添加的字段,伪装成真实的请求,干扰服务器的判断。importrequestsfromlxmlimportetreeuser_agent='Mozilla/4.0(compatible;MSIE5.5;WindowsNT)'headers={'User-Agent':user_agent}url='https://movie.douban.com/subject/1292052/'data=requests.get(url,headers=headers).texts=etree.HTML(data)film=s.xpath('//*[@id="content"]/h1/span[1]/text()')打印(电影)改进现阶段,就网络请求的内容而言,爬虫脚本已经和真人无异,因此服务器端需要从另一个角度进行防御。有两种想法。第一个是分析爬虫脚本的行为模式,进行识别和防御。爬虫脚本通常会发出频繁的网络请求。比如爬取豆瓣排名前100的电影,会连续发送100个网络请求。针对这种行为模式,服务端可以对访问的IP进行统计,如果短时间内单个IP的访问超过了设定的阈值,就会被拦截。这样确实可以防御一批爬虫,但是也容易误伤正常用户,爬虫脚本也可以绕过。这时候爬虫脚本需要做的就是ipproxy,每隔几次请求就切换ip,防止请求次数超过服务器设置的阈值。设置代理的代码也很简单。importrequestsproxies={"http":"http://111.155.124.78:8123"#代理ip}user_agent='Mozilla/4.0(compatible;MSIE5.5;WindowsNT)'headers={'User-Agent':user_agent}url='https://movie.douban.com/subject/1292052/'res=requests.get(url=http_url,headers=headers,proxies=proxies)第二种思路是做一些只有真人才能做的操作识别爬虫脚本。最典型的是以12306为代表的验证码操作。添加验证码是一种古老而有效的方法,可以使许多爬虫逃跑。当然,这并非万无一失。经过多年的发展,利用计算机视觉进行一些图像识别已经不是什么新鲜事,而且训练神经网络的门槛也越来越低,并且有很多开源的计算机视觉库可以免费使用。比如tesseract,可以在python中引入,一行命令就可以识别验证码。importpytesseractfromPILimportImage...#getidentifyingcodeimg...im=Image.open('code.png')result=pytesseract.image_to_string(im)如果你比较专业,还可以加入一些图片预处理通过降噪、二值化等操作,提高验证码的识别准确率。当然,如果验证码的原始干扰线有很多噪声,甚至出现人眼难以分辨的验证码(12306),计算机识别的准确率也会相应下降。但是这种方法对于真正的人类用户来说太不友好了,属于杀敌一千自损八百的做法。高级验证码方式虽然有很好的防爬效果,但是对于真人来说还是不够友好。开发人员在优化验证操作方面也付出了很多努力。如今,很多人机验证操作不再需要输入验证码,有的只需点击一下即可完成,有的甚至不需要任何操作,在用户不知情的情况下完成验证。这里实际上包含了不同的隐身身份验证方法。一些隐形身份验证使用基于JavaScript的身份验证方法。该方法主要是在响应数据页面之前返回一个带有JavaScript代码的页面,用于验证访问者是否具有JavaScript执行环境,从而判断是否使用浏览器。比如淘宝、快的等网站。通常,这段JS代码执行后,会发送一个带有参数key的请求,后台会通过判断key的值来决定响应的是真实页面还是假冒错误页面。因为key参数是动态生成的,每次都不一样,所以很难分析它的生成方式,导致无法构造出对应的http请求。有的更高级一些,通过检测用户的浏览习惯,比如用户常用IP或者鼠标移动等,然后自己判断人机操作。这可以一键取代繁琐的验证码,而且效果更好。对于这种反爬方法,轮到神器selenium了。Selenium是一个用于测试的库,可以调用浏览器内核,也就是说可以打开一个真实的浏览器,手动操作。那么Perfect就可以完美应对以上两种隐形验证方式。selenium的使用也很简单,直接对页面元素进行操作即可。配合根据页面元素等待页面加载完成的延时操作,基本模拟了人浏览页面的全过程。而且因为selenium会打开一个浏览器,如果有点击验证的操作,一般初始登录页面也会有这个操作,点击即可。fromseleniumimportwebdriver.Chrome()browser.get("url")#获取dom节点node=browser.find_elements_by_id("id")nodes=browser.find_elements_by_css_selector("css-selector")"class-name")#操作dom元素browser.find_element_by_xpath('xpath-to-dom').send_keys('password')browser.find_element_by_xpath('xpath-to-dom').click()#等待pagetoloadlocator=(By.CLASS_NAME,'page-content')try:WebDriverWait(driver,10,0.5).until(EC.presence_of_element_located(locator))finally:driver.close()看来selenium无解了。其实并不是。较新的智能验证码在目标中包含硒,即使您手动单击验证码也会失败。这是怎么做到的?其实这是对浏览器头部的检测。如果打开selenium的浏览器控制台并输入window.navigator.webdriver,返回值将是“true”。但是,如果你在正常打开的浏览器中输入这个命令,返回值将是“undefined”。在这里,我找到了关于webdriver的描述:navigator.webdriver)。可以看出webdriver属性是用来表示是否自动控制useragent的,即这个属性暴露了selenium的存在,人机验证无法通过。而且这个属性还是只读的,不能直接修改。当然,也不是不可以改。通过修改目标属性的get方法,可以达到修改属性的目的。此时webdriver属性是undefined,然后智能人机验证就可以通过了。但这是治标不治本。这时候如果浏览器打开一个新的窗口,或者点击一个链接进入一个新的页面,我们会发现webdriver又变回了true。当然你也可以每次打开新页面都输入这个命令,但实际上虽然可以绕过点击验证,但是如果直接在页面中添加检测webdriver的JS代码,页面一到就执行打开,那么在你改webdriver之前,网站已经知道你是不是爬虫脚本了。多道高一尺,魔高一尺。事实上,即便是这样的反爬方法,还是可以绕过的。在启动Chromedriver之前,启用Chrome的实验特性参数excludeSwitches,它的值为['enable-automation'],像这样,["enable-automation"])driver=Chrome(options=option)driver.get('url')这时候无论怎么打开新页面,webdriver都会是undefined。对于这种级别的爬虫脚本,不知如何防御,检测成本太高。不过,其实如果换个思路,还有一些有趣的反爬方法。比如猫眼电影和起点中文网的实时票房,在浏览器中可以看到,但是当你打开网页的代码时,却变成了方块。这是一个很好的反爬方法。简单的说就是在后端设置一个字体生成接口,随机生成一个字体,然后返回字体文件和每个数字的unicode对应,在前端页面填充数据。可以隐藏敏感数据。还有一些充分利用css进行反爬的,脑洞比较大。做两组数据,展示的时候用css定位覆盖真实的。或者搞一些干扰字符,在显示的时候设置不透明度为0隐藏。甚至还有背景可以和显示的内容拼接在一起,成为真正要显示的内容。这些都是很有意思的反攀爬手段。但是对于前端来说,毕竟所有的数据和代码都交给了客户端,爬虫脚本总能想办法爬到数据上去。各种反爬取手段只是增加了爬取数据的难度。主要是要自觉,拒绝恶意爬虫。