1.12那些年你不知道的爬虫面试题

1. 什么是爬虫和反爬虫?

爬虫:使用任何技术手段,批量获取网站信息的一种方式。

反爬虫:使用任何技术手段,阻止别人批量获取自己网站信息的一种方式。

2. 常见的反爬虫机制

通过UA 识别爬虫 有些爬虫的UA是特殊的,与正常浏览器的不一样,可通过识别特征UA,直接封掉爬虫请求

设置IP访问频率,如果超过一定频率,弹出验证码 如果输入正确的验证码,则放行,如果没有输入,则拉入禁止一段时间,如果超过禁爬时间,再次出发验证码,则拉入黑名单。当然根据具体的业务,为不同场景设置不同阈值,比如登陆用户和非登陆用户,请求是否含有refer。

通过并发识别爬虫 有些爬虫的并发是很高的,统计并发最高的IP,加入黑名单(或者直接封掉爬虫IP所在C段)

请求的时间窗口过滤统计 爬虫爬取网页的频率都是比较固定的,不像人去访问网页,中间的间隔时间比较无规则,所以我们可以给每个IP地址建立一个时间窗口,记录IP地址最近12次访问时间,每记录一次就滑动一次窗口,比较最近访问时间和当前时间,如果间隔时间很长判断不是爬虫,清除时间窗口,如果间隔不长,就回溯计算指定时间段的访问频率,如果访问频率超过阀值,就转向验证码页面让用户填写验证码

限制单个ip/api token的访问量 比如15分钟限制访问页面180次,具体标准可参考一些大型网站的公开api,如twitter api,对于抓取用户公开信息的爬虫要格外敏感

识别出合法爬虫 对http头agent进行验证,是否标记为、百度的spider,严格一点的话应该判别来源IP是否为、baidu的爬虫IP,这些IP在网上都可以找到。校验出来IP不在白名单就可以阻止访问内容。

3. 破解反爬虫机制的几种方法

策略1设置下载延迟,比如数字设置为5秒,越大越安全

策略2禁止Cookie,某些网站会通过Cookie识别用户身份,禁用后使得服务器无法识别爬虫轨迹

策略3使用user agent池。也就是每次发送的时候随机从池中选择不一样的浏览器头信息,防止暴露爬虫身份

策略4使用IP池,这个需要大量的IP资源,可以通过抓取网上免费公开的IP建成自有的IP代理池。

策略5分布式爬取,这个是针对大型爬虫系统的,实现一个分布式的爬虫,主要为以下几个步骤:1、基本的http抓取工具,如scrapy; 2、避免重复抓取网页,如Bloom Filter; 3、维护一个所有集群机器能够有效分享的分布式队列; 4、将分布式队列和Scrapy的结合; 5、后续处理,网页析取(如python-goose),存储(如Mongodb)。

策略6:模拟登录—浏览器登录的爬取 设置一个cookie处理对象,它负责将cookie添加到http请求中,并能从http响应中得到cookie,向网站登录页面发送一个请求Request, 包括登录url,POST请求的数据,Http header利用urllib2.urlopen发送请求,接收WEB服务器的Response。

4.动态加载又对及时性要求很高怎么处理?

  1. Selenium+Phantomjs
  2. 尽量不使用 sleep 而使用 WebDriverWait

如何知道一个网站是动态加载的数据?

用火狐或者谷歌浏览器 打开你网页,右键查看页面源代码,ctrl +F 查询输入内容,源代码里面并没有这个值,说明是动态加载数据。

5.python 爬虫有哪些常用框架?

序号 框架名称 描述 官网
1 Scrapy Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。用这个框架可以轻松爬下来如亚马逊商品信息之类的数据。 https://scrapy.org/
2 PySpider pyspider 是一个用python实现的功能强大的网络爬虫系统,能在浏览器界面上进行脚本的编写,功能的调度和爬取结果的实时查看,后端使用常用的数据库进行爬取结果的存储,还能定时设置任务与任务优先级等。 https://github.com/binux/pyspider
3 Crawley Crawley可以高速爬取对应网站的内容,支持关系和非关系数据库,数据可以导出为JSON、XML等。 http://project.crawley-cloud.com/
4 Portia Portia是一个开源可视化爬虫工具,可让您在不需要任何编程知识的情况下爬取网站!简单地注释您感兴趣的页面,Portia将创建一个蜘蛛来从类似的页面提取数据。 https://github.com/scrapinghub/portia
5 Newspaper Newspaper可以用来提取新闻、文章和内容分析。使用多线程,支持10多种语言等。 https://github.com/codelucas/newspaper
7 Grab Grab是一个用于构建Web刮板的Python框架。借助Grab,您可以构建各种复杂的网页抓取工具,从简单的5行脚本到处理数百万个网页的复杂异步网站抓取工具。Grab提供一个API用于执行网络请求和处理接收到的内容,例如与HTML文档的DOM树进行交互。 http://docs.grablib.org/en/latest/#grab-spider-user-manual
8 Cola Cola是一个分布式的爬虫框架,对于用户来说,只需编写几个特定的函数,而无需关注分布式运行的细节。任务会自动分配到多台机器上,整个过程对用户是透明的。 没找着~
9 很多 看自己积累 多百度

6. Scrapy 的优缺点?

优点:scrapy 是异步的

采取可读性更强的 xpath 代替正则强大的统计和 log 系统,同时在不同的 url 上爬行支持 shell 方式,方便独立调试写 middleware,方便写一些统一的过滤器,通过管道的方式存入数据库。

缺点:基于 python 的爬虫框架,扩展性比较差

基于 twisted 框架,运行中的 exception 是不会干掉 reactor,并且异步框架出错后是不会停掉其他任务的,数据出错后难以察觉。

7.scrapy 和 request?

  • scrapy 是封装起来的框架,他包含了下载器,解析器,日志及异常处理,基于多线程, twisted 的方式处理,对于固定单个网站的爬取开发,有优势,但是对于多网站爬取,并发及分布式处理方面,不够灵活,不便调整与括展。
  • request 是一个 HTTP 库, 它只是用来,进行请求,对于 HTTP 请求,他是一个强大的库,下载,解析全部自己处理,灵活性更高,高并发与分布式部署也非常灵活,对于功能可以更好实现。

8.描述下 scrapy 框架运行的机制?

  1. 从 start_urls 里获取第一批 url 并发送请求,请求由引擎交给调度器入请求队列,获取完毕后,调度器将请求队列里的请求交给下载器去获取请求对应的响应资源,并将响应交给自己编写的解析方法做提取处理,如果提取出需要的数据,则交给管道文件处理;
  2. 如果提取出 url,则继续执行之前的步骤(发送 url 请求,并由引擎将请求交给调度器入队列…),直到请求队列里没有请求,程序结束。

9. 实现模拟登录的方式有哪些?

  • 使用一个具有登录状态的 cookie,结合请求报头一起发送,可以直接发送 get 请求,访问登录后才能访问的页面。
  • 先发送登录界面的 get 请求,在登录页面 HTML 里获取登录需要的数据(如果需要的话),然后结合账户密码,再发送 post 请求,即可登录成功。然后根据获取的 cookie信息,继续访问之后的页面。
  • 还可以使用通用方法 selenium Pyppeteer

10.你遇到过的反爬虫的策略?

  1. BAN IP
  2. BAN USERAGENT
  3. BAN COOKIES
  4. 验证码验证
  5. javascript渲染
  6. ajax异步传输

11.你常用的反反爬虫的方案?

1.IP封锁

因为会对用户产生误伤,所以网站一般不会对用户的IP进行长时间的封锁。
解决方案:
(1)修改程序的访问频率
(2)使用IP代理的方式来对网站进行爬取

2.协议头
绝大多数网站,访问时会判断访问来源。
解决方案:
(1)访问时添加协议头

3.验证码
当用户请求频率过高的时候,有些网站就会触发验证码验证机制。
解决方案:
接入打码API,例如云打码。

4.需要登录
有些网站需要用户登录之后才能够获取页面中的信息,那么这种防护能非常有效的防止数据被大批量的被爬取。
解决方案:
(1)小数据量进行爬取(模拟登录后再去爬取,或者使用cookies 直接进行爬取)
(2)申请诸多的账号去养这些号,然后登录,或者获得cookies进行爬取。

5.动态页面的爬取
有一些网站的数据和图片是用JS代码动态生成的,那么服务器端,就会通过判断该用户是否访问了这些资源来判断是否爬虫。
通用解决方案:
(1)使用selenium

12.你用过多线程和异步吗?除此之外你还用过什么方法来提高爬虫效率?

1.协程

2.设置超时时间

3.Scrapy框架

13.有没有做过增量式抓取?

在发送请求之前判断这个URL是不是之前爬取过

在解析内容后判断这部分内容是不是之前爬取过

去重方法

  • 将爬取过程中产生的url进行存储,存储在redis的set中。当下次进行数据爬取时,首先对即将要发起的请求对应的url在存储的url的set中做判断,如果存在则不进行请求,否则才进行请求。
  • 对爬取到的网页内容进行唯一标识的制定,然后将该唯一表示存储至redis的set中。当下次爬取到网页数据的时候,在进行持久化存储之前,首先可以先判断该数据的唯一标识在redis的set中是否存在,在决定是否进行持久化存储

14.给定a、b两个文件,各存放50亿个url,每个url各占用64字节,内存限制是4G,如何找出a、b文件共同的url?

使用位图来进行处理。比如说这10亿个数的范围为【0-10亿】,那么就申请一个10亿的数组,

数组类型为boolen,只有0和1,0表示没有,1表示有。

这样自然而然的就删掉了重复的部分。

方案一:布隆过滤器

至于内存消耗方面的优化,实际上,如果想要内存方面有明显的节省,那就得换一种存储结构,也就是接下来要重点讲的布隆过滤器(Bloom Filter)。在讲布隆过滤器前,我们要来先认识一下另一种存储结构,位图(BitMap)。因为,布隆过滤器本身就是基于位图的,是对位图的一种改进

如果允许有一定的错误率,可以使用Bloom filter,4G内存大概可以表示340亿bit。将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率)

方案二:分治思想

img

Step1:遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,...,a999,每个小文件约300M);

Step2:遍历文件b,采取和a相同的方式将url分别存储到1000个小文件(记为b0,b1,...,b999);

巧妙之处:这样处理后,所有可能相同的url都被保存在对应的小文件(a0vsb0,a1vsb1,...,a999vsb999)中,不对应的小文件不可能有相同的url。然后我们只要求出这个1000对小文件中相同的url即可。

Step3:求每对小文件ai和bi中相同的url时,可以把ai的url存储到hash_set/hash_map中。然后遍历bi的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。

1、Hash取模是一种等价映射,不会存在同一个元素分散到不同小文件中的情况,即这里采用的是mod1000算法,那么相同的url在hash取模后,只可能落在同一个文件中,不可能被分散的。因为如果两个url相等,那么经过Hash(url)之后的哈希值是相同的,将此哈希值取模(如模1000),必定仍然相等。

2、那到底什么是hash映射呢?简单来说,就是为了便于计算机在有限的内存中处理big数据,从而通过一种映射散列的方式让数据均匀分布在对应的内存位置(如大数据通过取余的方式映射成小树存放在内存中,或大文件映射成多个小文件),而这个映射散列方式便是我们通常所说的hash函数,设计的好的hash函数能让数据均匀分布而减少冲突。尽管数据映射到了另外一些不同的位置,但数据还是原来的数据,只是代替和表示这些原始数据的形式发生了变化而已。

15.爬取图片不完整,失败怎么处理

不完整可能是图片较大,导致部分数据丢失,保存不完整,可以边下载边存,如 data.iter_content() 方法;为了防止失败,最保险的办法是先获取文件的大小,下载后判断一下大小是否一样,浏览器开发者模式 response 里面有一个 content-length 可以判断文件大小

16.JS逆向

请求正文里的参数,可以用多个浏览器去请求页面,然后再去观察
可以观察操作时是否加载了 js 文件,查找对应的 js 文件

如何快速查找 js

方案一:通过点击按钮,然后点击Event Listener,部分网站可以找到绑定的事件,对应的,只需要点击即可跳转到js的位置

方案二:部分网站可能没有绑定 js 监听事件,那么我们可以在 js 文件中搜索 请求正文参数的关键字,或者一些类似加密的函数,比如哈希,base64等

还可以从链接调用开始着手

如果加密很少的 js 我们可以用python复写(也可以使用js2py直接把js代码转化为python程序去执行),但是一旦加密函数过于庞大就需要将 js 抽取出用python+node或是webdriver来执行相关函数

如果是几百几千行的 js 加密呢?

用 python 复写其实也不是不能,但是开发时间和破解效率都是一个很关键的问题

如果说开发时间紧急,那么此时我们就需要使用其他的方式来执行这段 js

第一种:使用 selenium+webdriver 来执行本地文件,然后调用本地 html 上的 js 运行
第二种:使用 python 下的库 exec.js 来调用node来执行

17.解决爬取频率问题

分限制方案,是限制账号还是访问频率

账号限制的话,可以准备多个账号去随机访问

访问频率的话,最 low 的方法是 sleep,你还可以使用代理 IP 池,或者分布式爬虫

18.scrapy 中添加请求头

  • 第一种方法,在 scrapy 的spider 中添加请求头

    这种方法的好处是可以比较灵活,可以随意的添加,任意请求头

  • 第二种方法,是在 scrapy 的 setting 里添加

  • 第三种方法,是在 scrapy 的 middware 中添加请求头,

    1 在 spider 将需要爬取的网页 url 发送给 Scrapy Engine

    2 Scrapy Engine 本身不做任何处理,直接发送给 Scheduler

    3 Scheduler 生成 Requests 发送给 Engine

    4 Engine 拿到 Requests,通过 middware 发送给 Downloader

    注意:要在 settings 里启动中间件

posted @ 2020-10-06 22:39  短戈行  阅读(322)  评论(0编辑  收藏  举报