技术文章:爬虫与反爬虫技术简介

优采云 发布时间: 2022-09-21 15:21

  技术文章:爬虫与反爬虫技术简介

  vivo 互联网安全团队 - 谢鹏

  随着互联网大数据时代的到来,网络爬虫也成为了互联网的重要产业。它是一个自动获取网页数据和信息的爬虫程序,是网站搜索引擎的重要组成部分。通过爬虫,您可以获得您想要的相关数据信息,让爬虫辅助您的工作,从而降低成本,提高业务成功率,提高业务效率。

  本文一方面从爬虫和反反爬虫的角度解释了如何高效爬取网络上的开放数据。采集提供一些关于数据处理服务器过载的建议。

  爬虫是指按照一定的规则自动从万维网上抓取信息的程序。本次主要介绍爬虫、反爬虫、反爬虫的技术原理和实现。对于安全研究和学习,它不会做很多爬虫或商业应用。

  一、爬虫技术原理及实现

  1.1 爬虫的定义

  爬虫分为两类:一般爬虫和重点爬虫。前者的目标是爬取尽可能多的网站,同时保持一定的内容质量。比如百度等搜索引擎就是这类爬虫,如图1是一般搜索引擎的基础架构:

  首先选择互联网中的一部分网页,将这些网页的链接地址作为*敏*感*词*URL;

  将这些*敏*感*词*URL放入待爬取的URL队列中,爬虫从待爬取的URL队列中依次读取;

  通过DNS解析URL,并将链接地址转换为网站服务器对应的IP地址;

  网页下载器通过网站服务器下载网页,下载的网页是网页文档的形式;

  提取网页文档中的网址,过滤掉已抓取的网址;

  继续抓取没有被抓取的网址,直到待抓取的网址队列为空。

  图1.通用搜索引擎的基础架构

  爬虫通常从一个或多个URL开始,在爬取过程中不断将符合要求的新URL放入待爬队列中,直到满足程序的停止条件。

  我们日常看到的爬虫基本都是后者。目标是在抓取少量网站的同时尽可能保持准确的内容质量。一个典型的例子如图2所示,抢票软件,利用爬虫登录票务网络,爬取信息辅助业务。

  图片2.抢票软件

  了解了爬虫的定义之后,我们应该如何编写爬虫程序来爬取我们想要的数据。我们可以先了解一下目前常用的爬虫框架,因为它可以写一些常用爬虫功能的实现代码,然后留下一些接口。在做不同爬虫项目时,我们只需要根据实际情况编写少量改动即可。 ,并根据需要调用这些接口,即可以实现爬虫项目。

  1.2爬虫框架介绍

  常用的搜索引擎爬虫框架如图3所示。首先,Nutch是专门为搜索引擎设计的爬虫,不适合精准爬取。 Pyspider和Scrapy都是用python语言编写的爬虫框架,都支持分布式爬虫。另外,由于其可视化的操作界面,Pyspider比Scrapy的全命令行操作更加人性化,但功能不如Scrapy强大。

  图3.爬虫框架对比

  1.爬虫3个简单例子

  除了使用爬虫框架进行爬取外,还可以从零开始编写爬虫程序。步骤如图4:

  图4.爬虫基本原理

  接下来,我们将通过一个简单的示例来实际演示上述步骤。我们要爬取的是某个应用市场的列表。我们以此为例,因为这个网站没有任何反爬的手段。通过以上步骤,我们可以轻松抓取内容。

  图5.网页及其对应的源码

  网页及其对应的源码如图5所示。对于网页上的数据,假设我们要爬取排行榜上每个应用的名称及其分类。

  我们先分析网页的源码发现可以直接在网页的源码中搜索“抖音”等app的名字,然后看到名字应用的名称、应用的类别等都在一个标签中,所以我们只需要请求网页地址,获取返回的网页源代码,然后对网页源代码进行正则匹配,提取所需数据并保存,如图 6 所示。

  #获取网页源码def get_one_page(url): try: response = requests.get(url) if response.status_code == 200: return response.text return None except RequestException: return None #正则匹配提取目标信息并形成字典def parse_one_page(html): pattern = re.compile('.*?data-src="(.*?)".*?.*?det.*?>(.*?)</a>.*?p.*?(.*?)</a>.*?',re.S) items = re.findall(pattern, html) j = 1 for item in items[:-1]: yield {'index': str(j), 'name': item[1], 'class':item[2] } j = j+1 #结果写入txtdef write_to_file(content): with open(r'test.txt', 'a', encoding='utf-8') as f: f.write(json.dumps(content, ensure_ascii=False)+'\n')

  图6.爬虫代码及结果

  二、反爬虫相关技术

  在了解具体的反爬措施之前,我们先介绍一下反爬的定义和含义。限制爬虫程序访问服务器资源和获取数据的行为称为反爬虫。爬虫的访问速度和目的与普通用户不同。大多数爬虫都会毫无节制地爬取目标应用程序,给目标应用程序的服务器带来巨大压力。机器人发出的网络请求被运营商称为“垃圾流量”。为了保证服务器的正常运行或者降低服务器的压力和运行成本,开发者不得不借助各种技术手段来限制爬虫对服务器资源的访问。

  那么为什么要做反爬虫呢?答案很明显。爬虫流量会增加服务器的负载。过多的爬虫流量会影响服务的正常运行,导致收入损失。另一方面,一些核心数据泄露会使数据所有者失去竞争力。

  常见的反爬虫方法如图7所示,主要包括文本混淆、动态页面渲染、验证码校验、请求签名校验、大数据风控、js混淆和蜜罐等。文本混淆包括css偏移、图像伪装文字、自定义字体等。控制策略的制定往往基于参数验证、行为频率和模式异常。

  图7.常用反爬虫方法

  2.1 CSS 偏移反爬虫

  在构建网页时,需要使用 CSS 来控制各种字符的位置。情况也是如此。可以使用 CSS 将浏览器中无序显示的文本存储在 HTML 中,从而限制爬虫。 CSS 偏移反爬是一种反爬的方法,它使用 CSS 样式将乱序的文本排版成正常的人类阅读顺序。这个概念不是很好理解,我们可以通过对比两段文字来加深对这个概念的理解:

  以上两段浏览器显示的信息应该是正确的。如果我们按照上面提到的爬虫步骤,定期分析网页并提取信息,就会发现学号有误。

  看图8所示的例子,如果我们要爬取这个网页上的机票信息,首先需要对网页进行分析。红框显示的467价格对应的是中国民航石家庄到上海的机票,但是分析网页源码发现代码中有3对b标签,第一对b tags 收录 3 对 i 标签,其中 i 标签中的数字都是 7,也就是说第一对 b 标签的显示结果应该是 777。第二对 b 标签中的数字是 6,而第三对b标签中的数字是4,所以我们无法直接通过正则匹配得到正确的票价。

  图8.CSS偏移反爬虫示例

  

  2.2个图像伪装反爬虫

  图片伪装反爬虫,其本质是用图片替换原创内容,使爬虫程序无法正常获取,如图9所示。这个反爬虫的原理很简单,就是在前端页面中,应该是普通文本内容的部分被替换为图片。这种情况下,可以直接用ocr来识别图片中的文字,绕过。而且因为是用图片而不是文字来显示,所以图片本身会比较清晰,没有很多噪声干扰,ocr识别的结果会非常准确。

  图片9.图像伪装反爬虫示例

  2.3 自定义字体反爬虫

  在 CSS3 时代,开发者可以使用@font-face 来指定网页的字体。开发人员可以将自己喜欢的字体文件放在 Web 服务器上,并在 CSS 样式中使用它。当用户使用浏览器访问网页应用时,浏览器会下载对应的字体到用户的电脑,但是当我们使用爬虫程序时,由于没有对应的字体映射关系,无法直接获取到有效数据爬行。

  如图10所示,该网页中每个店铺的评论数、人均、品味、环境等信息都是乱码,爬虫无法直接读取内容。

  图10.自定义字体反爬虫示例

  2.4页动态渲染反爬虫

  根据渲染方式的不同,网页大致可以分为客户端渲染和服务器端渲染。

  客户端渲染和服务端渲染最重要的区别就是谁来完成html文件的完整拼接。如果是在服务端做完再返回给客户端,就是服务端渲染,如果是前端做更多的工作完成html的拼接,就是客户端渲染。

  图11.客户端渲染示例

  2.5个验证码反爬虫

  几乎所有应用都会弹出验证码供用户识别涉及用户信息安全的操作,确保操作是人的行为,而不是大型机器。那为什么会出现验证码呢?在大多数情况下,这是因为 网站 被访问太频繁或行为不端,或者是直接限制某些自动化操作。它们分类如下:

  在许多情况下,例如登录和注册,这些验证码几乎总是可用的。其目的是限制恶意注册、恶意爆破等,也是一种反爬取手段。

  当一些网站遇到访问频率过高的行为时,可能会直接弹出登录窗口,要求我们登录后才能继续访问。此时验证码直接绑定在登录表单上。 ,即使检测到异常,也会采用强制登录的方式进行反爬。

  如果一些比较常规的网站遇到访问频率稍高的情况,会弹出验证码供用户识别提交,验证当前访问者网站是否为一个真人,用于限制某些机器的行为和实现反爬虫。

  常见的验证码形式包括图形验证码、行为验证码、短信、扫描验证码等,如图12所示。对于是否成功通过验证码,除了能够根据验证码的要求准确完成相应的点击、选择、输入等,通过验证码风控也很关键;比如对于滑块验证码,验证码风控可能是检测滑动轨迹,如果检测到的轨迹是非人为的,则判断为高风险,导致无法通过成功。

  图12.验证码反爬虫方法

  2.6 请求签名验证反爬虫

  签名验证是防止服务器被恶意链接和篡改数据的有效方法之一,也是后端API最常用的保护方法之一。签名是根据数据源进行计算或加密的过程。用户签名后,会生成一个一致且唯一的字符串,这是您访问服务器的身份标志。由于其一致性和唯一性两大特点,可以有效防止服务器将伪造数据或篡改数据当作正常数据处理。

  上面2.4小节中提到的网站是通过客户端渲染网页,通过ajax请求获取数据,使得爬虫的难度增加到一定程度程度。接下来分析ajax请求,如图13所示,会发现ajax请求是用请求签名的,分析的是加密参数,如果要破解请求接口,需要破解的加密方法参数,这无疑进一步增加了难度。

  图13.Ajax请求排名数据

  2.7个蜜罐反爬虫

  蜜罐反爬虫是一种隐藏链接的方法,用于检测网页中的爬虫程序。隐藏的链接不会显示在页面上,普通用户无法访问,但是爬虫程序可能会将链接进入待爬队列,向链接发起请求。开发者可以利用这个特性来区分普通用户和爬虫程序。如图14,查看网页源码,页面上只有6个产品,col-md-3

  有8对

  标签。这个 CSS 样式的作用是隐藏标签,所以我们在页面上只看到 6 个 item,爬虫会提取 8 个 item 的 URL。

  图14.蜜罐反爬虫示例

  三、防反爬相关技术

  对于上节提到的反爬技术,反爬技术主要有以下几种:CSS偏移反爬、自定义字体反爬、动态页面渲染反爬、验证码破解等. . ,下面将详细介绍这些方法。

  3.1 CSS 偏移反爬

  3.1.1 CSS 偏移逻辑介绍

  那么对于上述2.1css偏移反爬虫的例子,如何才能得到正确的机票价格呢?仔细看CSS样式,可以发现每个带数字的标签都有一个样式集,第一对b标签中的i标签对的样式是一样的,都是width: 16px;另外,还要注意最外层span标签对的样式是width:48px。

  如果按照css样式的线索,第一对b标签中的3对i标签正好占据span标签对的位置,它们的位置如图15所示。页面上显示的价格此时应该是777,但是由于第2对和第3对b标签里面有值,所以我们还需要计算它们的位置。由于第二对b标签的位置样式是left:-32px,所以第二对b标签中的值6会覆盖原来第一对b标签中的第二个数字7,页面应该显示的数字是767.

  根据这个规则,第三对b标签的位置样式是left:-48px,这个标签的值会覆盖第一对b标签中的第一个数字7,最终显示的票价是467 .

  图15.偏移逻辑

  3.1.2 CSS偏移反爬代码实现

  那么接下来,我们将根据上面的css样式规则编写代码来爬取网页以获取正确的机票价格。代码和结果如图16所示。

  if __name__ == '__main__': url = 'http://www.porters.vip/confusion/flight.html' resp = requests.get(url) sel = Selector(resp.text) em = sel.css('em.rel').extract() for element in range(0,1): element = Selector(em[element]) element_b = element.css('b').extract() b1 = Selector(element_b.pop(0)) base_price = b1.css('i::text').extract() print('css偏移前的价格:',base_price) alternate_price = [] for eb in element_b: eb = Selector(eb) style = eb.css('b::attr("style")').get() position = ''.join(re.findall('left:(.*)px', style)) value = eb.css('b::text').get() alternate_price.append({'position': position, 'value': value}) print('css偏移值:',alternate_price) for al in alternate_price: position = int(al.get('position')) value = al.get('value') plus = True if position >= 0 else False index = int(position / 16) base_price[index] = value print('css偏移后的价格:',base_price)

  图16. CSS偏移反爬代码及结果

  

  3.2自定义字体反爬

  针对上述2.3自定义字体反爬的情况,解决方法是在网页中提取自定义字体文件(通常是WOFF文件),并将映射关系收录到爬虫代码中,您可以获取有效数据。解决步骤如下:

  发现问题:查看网页源码,发现关键字符被编码替换,如&#xefbe

  分析:查看网页,发现css自定义字符集被隐藏了

  查找:查找css文件的url,得到字符集对应的url,如PingFangSC-Regular-num

  查找:查找并下载字符集url

  对比:将字符集中的字符与网页源代码中的代码进行比较,发现代码的后四位对应的字符,即与网页源代码对应的味道网页是 8.9 分

  3.3页动态渲染反爬

  客户端渲染的反爬虫,页面代码在浏览器源码中看不到,需要进行渲染,进一步获取渲染结果。破解这个反爬虫有几种方法:

  在浏览器中,可以通过开发者工具直接查看ajax的具体请求方式、参数等;

  使用 selenium 模拟真人操作浏览器获取渲染结果。后续操作步骤与服务端渲染流程相同;

  如果渲染的数据隐藏在html结果的js变量中,可以直接定时提取;

  如果有JS生成的加密参数,可以找出加密部分的代码,然后用pyexecJS模拟JS的执行,并返回执行结果。

  3.4验证码破解

  以下是识别滑块验证码的示例。如图17所示,是使用目标检测模型识别滑块验证码间隙位置的结果示例。这种破解滑块验证码的方法对应的是模拟真人的方法。不使用接口破解的原因是加密算法很难破解,而且加密算法可能每天都在变化,所以破解的时间成本比较大。

  图17.通过目标检测模型识别滑块验证码的差距

  3.4.1爬取滑块验证码图片

  由于yolov5使用的目标检测模型是监督学习,所以需要爬取滑块验证码的图片并做标记,然后输入到模型中进行训练。通过模拟真人在场景中爬取一些验证码。

  图18.爬取的滑块验证码图片

  3.4.2 手动打标

  这次使用labelImg 手动标注图片。手动标记需要很长时间。 100张照片通常需要大约40分钟。自动标记码写起来比较复杂,主要是需要把验证码的所有背景图片和gap图片分别提取出来,然后随机生成gap位置作为标签,把gap放到对应的gap中位置生成图片作为输入。

  图19.标记验证码图片和标记后生成的xml文件

  3.4.3目标检测模型yolov5

  clone yolov5的官方代码直接从github下载,基于pytorch。

  接下来的步骤如下:

  数据格式转换:将手动标注的图片和标签文件转换为yolov5接收到的数据格式,得到yolov5格式的1100张图片和1100个标签文件;

  新建数据集:新建custom.yaml文件,创建自己的数据集,包括目录、类别个数、训练集和验证集的类别名称;

  训练调优:修改模型配置文件和训练文件后,根据训练结果进行训练和超参数调优。

  将xml文件转换为yolov5格式的部分脚本:

  for member in root.findall('object'): class_id = class_text.index(member[0].text) xmin = int(member[4][0].text) ymin = int(member[4][1].text) xmax = int(member[4][2].text) ymax = int(member[4][3].text) # round(x, 6) 这里我设置了6位有效数字,可根据实际情况更改 center_x = round(((xmin + xmax) / 2.0) * scale / float(image.shape[1]), 6) center_y = round(((ymin + ymax) / 2.0) * scale / float(image.shape[0]), 6) box_w = round(float(xmax - xmin) * scale / float(image.shape[1]), 6) box_h = round(float(ymax - ymin) * scale / float(image.shape[0]), 6) file_txt.write(str(class_id)) file_txt.write(' ') file_txt.write(str(center_x)) file_txt.write(' ') file_txt.write(str(center_y)) file_txt.write(' ') file_txt.write(str(box_w)) file_txt.write(' ') file_txt.write(str(box_h)) file_txt.write('\n') file_txt.close()

  训练参数设置:

  parser = argparse.ArgumentParser()parser.add_argument('--weights', type=str, default='yolov5s.pt', help='initial weights path')parser.add_argument('--cfg', type=str, default='./models/yolov5s.yaml', help='model.yaml path')parser.add_argument('--data', type=str, default='data/custom.yaml', help='data.yaml path')parser.add_argument('--hyp', type=str, default='data/hyp.scratch.yaml', help='hyperparameters path')# parser.add_argument('--epochs', type=int, default=300)parser.add_argument('--epochs', type=int, default=50)# parser.add_argument('--batch-size', type=int, default=16, help='total batch size for all GPUs')parser.add_argument('--batch-size', type=int, default=8, help='total batch size for all GPUs')parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='[train, test] image sizes')parser.add_argument('--rect', action='store_true', help='rectangular training')parser.add_argument('--resume', nargs='?', const=True, default=False, help='resume most recent training')parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')parser.add_argument('--notest', action='store_true', help='only test final epoch')parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')parser.add_argument('--image-weights', action='store_true', help='use weighted image selection for training')parser.add_argument('--device', default='cpu', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')parser.add_argument('--single-cls', action='store_true', help='train multi-class data as single-class')parser.add_argument('--adam', action='store_true', help='use torch.optim.Adam() optimizer')parser.add_argument('--sync-bn', action='store_true', help='use SyncBatchNorm, only available in DDP mode')parser.add_argument('--local_rank', type=int, default=-1, help='DDP parameter, do not modify')parser.add_argument('--workers', type=int, default=8, help='maximum number of dataloader workers')parser.add_argument('--project', default='runs/train', help='save to project/name')parser.add_argument('--entity', default=None, help='W&B entity')parser.add_argument('--name', default='exp', help='save to project/name')parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')parser.add_argument('--quad', action='store_true', help='quad dataloader')parser.add_argument('--linear-lr', action='store_true', help='linear LR')parser.add_argument('--label-smoothing', type=float, default=0.0, help='Label smoothing epsilon')parser.add_argument('--upload_dataset', action='store_true', help='Upload dataset as W&B artifact table')parser.add_argument('--bbox_interval', type=int, default=-1, help='Set bounding-box image logging interval for W&B')parser.add_argument('--save_period', type=int, default=-1, help='Log model after every "save_period" epoch')parser.add_argument('--artifact_alias', type=str, default="latest", help='version of dataset artifact to be used')opt = parser.parse_args()

  3.4.4个目标检测模型的训练结果

  该模型在 50 次迭代时基本达到了精度、召回率和 mAP 的瓶颈。预测结果还存在以下问题:大部分gap都可以准确框起来,但也有少量的frame错误,两个gap,没有gap。

  图20.上:模型训练结果图;

  底部:模型在部分验证集上的预测结果

  四、总结

  这次简单介绍一下爬虫和反爬虫的技术手段。介绍的技术和案例仅用于安全研究和学习,不会用于大量爬虫或商业应用。

  对于爬虫来说,为了抓取互联网上的公共数据进行数据分析等目的,我们应该遵守网站机器人协议,以免影响网站的正常运行并*敏*感*词*进行数据爬取;对于反爬虫来说,因为只要人类可以正常访问网页,爬虫当然可以用相同的资源抓取它们。因此,反爬虫的目的是防止爬虫在海量采集网站信息的过程中使服务器过载,从而防止爬虫行为阻碍用户体验,提高用户使用率网站 对服务很满意。

  结束

  (附源码)node.js知识分享网站 毕业设计 202038

  研究背景

  系统管理也都将通过计算机进行整体智能化操作,对于知识分享网站所牵扯的管理及数据保存都是非常多的,例如管理员;首首页、站点管理(轮播图、公告栏)用户管理(管理员、普通用户)内容管理(知识分享、分享分类列表、知识分类、知识分类列表)更多管理(知识推广情况)这给管理者的工作带来了巨大的挑战,面对大量的信息,传统的管理系统,都是通过笔记的方式进行详细信息的统计,后来出现电脑,通过电脑输入软件将纸质的信息统计到电脑上,这种方式比较传统,而且想要统计数据信息比较麻烦,还受时间和空间的影响,所以为此开发了知识分享网站;为用户提供了方便管理平台,方便管理员查看及维护,并且可以通过需求进行内容的编辑及维护等;对于用户而言,可以随时进行查询所需信息,管理员可以足不出户就可以获取到系统的数据信息等,而且还能节省用户很多时间,所以开发知识分享网站给管理者带来了很大的方便,同时也方便管理员对用户信息进行处理。

  

  本论文知识分享网站主要牵扯到的程序,数据库与计算机技术等。覆盖知识面大,可以大大的提高系统人员工作效率。

  1.2 研究现状

  

  与其他国家相比,我国的软件产业相对落后,在信息化建设方面起步也比较晚,但是随着我国经济的不断发展,以及网络技术的不断提高,我国也在不断的进行软件行业的摸索,也得到了一些成果,我国的软件产业得到了快速的发展,越来越多的软件系统出现在人们的视线中,也逐渐改变着人们生活工作的方式。但是,对于信息化的建设,与很多发达国家相比,由于信息化程度的落后以及经费的不足,我国的知识分享网站开发方面还是相对落后的,因此,要不断的努力探索,争取开发出一个实用的信息化的知识分享网站,来实现商品管理的信息化。因此本课题以商品为例,目的是开发一个实用的知识分享网站。

  知识分享网站的开发运用node.js的koa技术,以及MYSQL、的支持下共同完成了该网站系统的开发,实现了商品管理的信息化,用户可以有一个非常好的体验,管理员也可以通过该系统进行更加方便的管理操作,实现了之前指定好的计划。

  通过对管理员和用户的需求分析,我们将该知识分享网站的功能逐步进行了添加,然后进行功能分析和检测,而且针对这两方面进行了深入研究探讨,该知识分享网站主要对开发背景、市场需求、数据库分析、功能模块以及开发技术进行了着重介绍和分析。最后对系统中的功能信息进行测试和分析。本次毕业实现的知识分享网站,不管是可行性分析、系统整体框架设计还是编码,都需要严格遵守软件开发的三个周期八个阶段,在该系统的开发过程中,要保证系统具有良好的时效性、易安装性以及稳定性。在代码编写时一定要按照要求进行,让代码编写看起来更美观,开发出一个便于用户的使用的知识分享网站是本次开发的主要目标。在系统完成之后,利用电脑来将系统进行安置,并且用户可以通过电脑随时查看商品信息管理。此次在知识分享网站的开发中,对系统要进行可行性分析、系统需求分析等基本分析,并且完成系统的部署和测试,在这些功能都实现之后,通过电脑进行操作系统。系统规划分析中,需要按照以下所示的技术路线。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线