前言
之所以在这里写下 python 爬虫常见面试题及解答, 一是用作笔记, 方便日后回忆; 二是给自己一个和大家交流的机会, 互相学习, 进步, 希望不正之处大家能给予指正; 三是我也是互联网寒潮下岗的那批人之一, 为了找工作而做准备.
一, 题目部分
1,scrapy 框架专题部分(很多面试都会涉及到这部分)
(1)请简要介绍下 scrapy 框架.
(2)为什么要使用 scrapy 框架? scrapy 框架有哪些优点?
(3)scrapy 框架有哪几个组件 / 模块? 简单说一下工作流程.
(4)scrapy 如何实现分布式抓取?
2, 其他常见问题.
(1)爬虫使用多线程好? 还是多进程好? 为什么?
(2)http 和 https 的区别?
(3)数据结构之堆, 栈和队列的理解和实现.
二, 解答部分
1,scrapy 框架专题部分
(1)请简要介绍下 scrapy 框架.
scrapy 是一个快速 (fast), 高层次(high-level) 的基于 python 的 web 爬虫构架, 用于抓取 Web 站点并从页面中提取结构化的数据. scrapy 使用了 Twisted 异步网络库来处理网络通讯.
(2)为什么要使用 scrapy 框架? scrapy 框架有哪些优点?
它更容易构建大规模的抓取项目
它异步处理请求, 速度非常快
它可以使用自动调节机制自动调整爬行速度
(3)scrapy 框架有哪几个组件 / 模块? 简单说一下工作流程.
Scrapy Engine: 这是引擎, 负责 Spiders,ItemPipeline,Downloader,Scheduler 中间的通讯, 信号, 数据传递等等!(像不像人的身体?)
Scheduler(调度器): 它负责接受引擎发送过来的 requests 请求, 并按照一定的方式进行整理排列, 入队, 并等待 Scrapy Engine(引擎)来请求时, 交给引擎.
Downloader(下载器): 负责下载 Scrapy Engine(引擎)发送的所有 Requests 请求, 并将其获取到的 Responses 交还给 Scrapy Engine(引擎), 由引擎交给 Spiders 来处理,
Spiders: 它负责处理所有 Responses, 从中分析提取数据, 获取 Item 字段需要的数据, 并将需要跟进的 URL 提交给引擎, 再次进入 Scheduler(调度器),
Item Pipeline: 它负责处理 Spiders 中获取到的 Item, 并进行处理, 比如去重, 持久化存储(存数据库, 写入文件, 总之就是保存数据用的)
Downloader Middlewares(下载中间件): 你可以当作是一个可以自定义扩展下载功能的组件
Spider Middlewares(Spider 中间件): 你可以理解为是一个可以自定扩展和操作引擎和 Spiders 中间'通信'的功能组件(比如进入 Spiders 的 Responses; 和从 Spiders 出去的 Requests)
整体架构如下图:
工作流程:
数据在整个 Scrapy 的流向:
程序运行的时候,
引擎: Hi!Spider, 你要处理哪一个网站?
Spiders: 我要处理 23wx.com
引擎: 你把第一个需要的处理的 URL 给我吧.
Spiders: 给你第一个 URL 是 XXXXXXX.com
引擎: Hi! 调度器, 我这有 request 你帮我排序入队一下.
调度器: 好的, 正在处理你等一下.
引擎: Hi! 调度器, 把你处理好的 request 给我,
调度器: 给你, 这是我处理好的 request
引擎: Hi! 下载器, 你按照下载中间件的设置帮我下载一下这个 request
下载器: 好的! 给你, 这是下载好的东西.(如果失败: 不好意思, 这个 request 下载失败, 然后引擎告诉调度器, 这个 request 下载失败了, 你记录一下, 我们待会儿再下载.)
引擎: Hi!Spiders, 这是下载好的东西, 并且已经按照 Spider 中间件处理过了, 你处理一下(注意! 这儿 responses 默认是交给 def parse 这个函数处理的)
Spiders:(处理完毕数据之后对于需要跟进的 URL),Hi! 引擎, 这是我需要跟进的 URL, 将它的 responses 交给函数 def xxxx(self, responses)处理. 还有这是我获取到的 Item.
引擎: Hi !Item Pipeline 我这儿有个 item 你帮我处理一下! 调度器! 这是我需要的 URL 你帮我处理下. 然后从第四步开始循环, 直到获取到你需要的信息,
注意! 只有当调度器中不存在任何 request 了, 整个程序才会停止,(也就是说, 对于下载失败的URL,Scrapy 会重新下载.)
以上就是 Scrapy 整个流程了.
(4)scrapy 如何实现分布式抓取?
可以借助 scrapy_redis 类库来实现.
在分布式爬取时, 会有 master 机器和 slave 机器, 其中, master 为核心服务器, slave 为具体的爬虫服务器.
我们在 master 服务器上搭建一个 Redis 数据库, 并将要抓取的 url 存放到 Redis 数据库中, 所有的 slave 爬虫服务器在抓取的时候从 Redis 数据库中去链接, 由于 scrapy_redis 自身的队列机制, slave 获取的 url 不会相互冲突, 然后抓取的结果最后都存储到数据库中. master 的 Redis 数据库中还会将抓取过的 url 的指纹存储起来, 用来去重. 相关代码在 dupefilter.py 文件中的 request_seen()方法中可以找到.
去重问题:
dupefilter.py 里面的源码:
- def request_seen(self, request):
- fp = request_fingerprint(request)
- added = self.server.sadd(self.key, fp)
- return not added
去重是把 request 的 fingerprint 存在 Redis 上, 来实现的.
2, 其他常见问题.
(1)爬虫使用多线程好? 还是多进程好? 为什么?
对于 IO 密集型代码(文件处理, 网络爬虫), 多线程能够有效提升效率(单线程下有 IO 操作会进行 IO 等待, 会造成不必要的时间等待, 而开启多线程后, A 线程等待时, 会自动切换到线程 B, 可以不浪费 CPU 的资源, 从而提升程序执行效率).
在实际的采集过程中, 既考虑网速和相应的问题, 也需要考虑自身机器硬件的情况, 来设置多进程或者多线程.
(2)http 和 https 的区别?
A. http 是超文本传输协议, 信息是明文传输, https 则是具有安全性的 ssl 加密传输协议.
B. http 适合于对传输速度, 安全性要求不是很高, 且需要快速开发的应用. 如 Web 应用, 小的手机游戏等等. 而 https 适用于任何场景.
(3)数据结构之堆, 栈和队列的理解和实现.
栈(stacks): 栈的特点是后进先出. 只能通过访问一端来实现数据的储存和检索的线性数据结构.
队列(queue): 队列的特点是先进先出. 元素的增加只能在一端, 元素的删除只能在另一端. 增加的一端称为队尾, 删除的一端称为队首.
栈:
- stack = [1, 2, 3]
- stack.append(4)
- stack.append(5)
- print(stack)
- stack.pop()
- stack.pop()
- print(stack)
输出结果:
- [1, 2, 3, 4, 5]
- [1, 2, 3]
队列:
- from collections import deque
- queue = deque(['Eric', 'John', 'Michael'])
- queue.append('Terry')
- queue.append('Graham')
- print(queue)
- queue.popleft()
- print(queue)
输出结果:
- deque(['Eric', 'John', 'Michael', 'Terry', 'Graham'])
- deque(['John', 'Michael', 'Terry', 'Graham'])
这里还会有一个常见的问题, 栈溢出的常见情况及解决方案.
什么是栈溢出?
因为栈一般默认为 1-2m, 一旦出现死循环或者是大量的递归调用, 在不断的压栈过程中, 造成栈容量超过 1m 而导致溢出.
栈溢出的几种情况?
1, 局部数组过大. 当函数内部数组过大时, 有可能导致堆栈溢出.
2, 递归调用层次太多. 递归函数在运行时会执行压栈操作, 当压栈次数太多时, 也会导致堆栈溢出.
解决方法:
1, 用栈把递归转换成非递归.
2, 增大栈空间.
来源: https://www.cnblogs.com/tianyiliang/p/10219034.html