1 :列表生成式和生成器的区别 ?
列表生成式直接生成一个列表,所有元素对象被立即创建在内存中,当元素过多时,势必会占用过多内存,
不可取,要用到生成器,它即时创建一个生成器对象,未创建任何元素
生成器来生成一个列表,它不会立即创建大量的对象在内存中
生成器的缺点,没有列表的方法,如append、len、index等等
通过next方法来访问其元素
可通过循环打印出所有的元素
2、:如何不用任何循环快速筛掉列表中的奇数元素 ?
用内置函数filter配合匿名函数过滤掉数组中不符合条件的元素
print (filter(lambda x:x%2 ==0, [1,2,3,4,5])) #[2,4]
3、:map和reduce的用法 ?
#map
#使用函数来处理序列中的元素 :包含数字的元组、列表;
#可以接收2个数序列,生成一个列表
print map(lambda x,y:(x,y) , [1,2,3],[4,5,6]) #[(1, 4), (2, 5), (3, 6)]
#reduce
#使用函数来累积处理序列中的元素,可实现阶乘、求数组内的数字和;reduce
把一个函数作用在一个序列[x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算.
4、装饰器的作用 ?
答:装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,提高了代码的复用性。比如,在函数调用前后自动打印日志,但又不希望修改now()
函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)
5、如何处理封IP的反爬 ?
答:因为网络上的免费代理平台可用的IP数量太少,所以自己写一个模块去抓取平台的IP来维护是没有什么意义的。我选择的是付费代理,通过使用平台的api在本地动态维护一个IP缓存池来供给分布式架构的爬虫节点使用。这个缓存池不需要做IP有效性验证,因为我的爬虫若下载某个Request彻底失败后会把这个Request重新放回Request队列,而且选择一个好的代理平台可以大大提高代理IP质量。我常用的是快代理。缓存池的IP被取走一个,池中的数量就减少一个,当数量少于M时,再从平台获取N个。
6、如何处理验证码 ?
答:简单的验证码可以通过预处理(灰度、二值化、去除干燥点)验证码图片再使用tesseract库来识别;复杂一点的则接入付费平台识别。当然,如果这个目标网站的app端没有验证码的话,会优先通过app端爬取。
7、说几个redis中的数据类型和命令
答: 字符 串、列表、set集合;set key 123,,,lpush key 1 2 3,,,sadd key 1 2 3
8、MySQL中的inner join和left join的区别 ?
答: NNER JOIN(内连接,或等值连接):取得两个表中存在连接匹配关系的记录。
LEFT JOIN(左连接):取得左表(table1)完全记录,即使右表(table2)并无对应匹配记录。
扩展:RIGHT JOIN(右连接):与 LEFT JOIN 相反,取得右表(table2)完全记录,即使左表(table1)并无匹配对应记录。
9、Python中__new__与__init方法的区别
__new__:它是创建对象时调用,会返回当前对象的一个实例,可以用_new_来实现单例
__init__:它是创建对象后调用,对当前对象的一些实例初始化,无返回值
10、python中的设计模式
11、常用的网络数据爬取方法
正则表达式
Beautiful Soup
Lxml
12、设计一个基于session登录验证的爬虫方案
13、什么是lambda函数?它有什么好处?
lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数
lambda函数:首要用途是指点短小的回调函数
lambda [arguments]:expression
>>> a=lambdax,y:x+y
>>> a(3,11)
二.框架问题(scrapy)可能会根据你说的框架问不同的问题,但是scrapy还是比较多的
1.scrapy的基本结构(五个部分都是什么,请求发出去的整个流程)
2.scrapy的去重原理 (指纹去重到底是什么原理)
(1).Scrapy本身自带有一个中间件;
(2).scrapy源码中可以找到一个dupefilters.py去重器;
(3).需要将dont_filter设置为False开启去重,默认是True,没有开启去重;
(4) .对于每一个url的请求,调度器都会根据请求得相关信息加密得到一个指纹信息,并且将指纹信息和set()集合中的指纹信息进 行 比对,如果set()集合中已经存在这个数据,就不在将这个Request放入队列中;
(5).如果set()集合中没有存在这个加密后的数据,就将这个Request对象放入队列中,等待被调度。
3.scrapy中间件有几种类,你用过那些中间件,
4.scrapy中间件再哪里起的作用(面向切面编程)
(1)、scrapy的中间件理论上有三种(Schduler Middleware,Spider Middleware,Downloader Middleware),在应用上一般有以下两种
1.爬虫中间件Spider Middleware
主要功能是在爬虫运行过程中进行一些处理.
2.下载器中间件Downloader Middleware
主要功能在请求到网页后,页面被下载时进行一些处理.
(2)、使用
1.Spider Middleware有以下几个函数被管理:
- process_spider_input 接收一个response对象并处理,
位置是Downloader-->process_spider_input-->Spiders(Downloader和Spiders是scrapy官方结构图中的组件)
- process_spider_exception spider出现的异常时被调用
- process_spider_output 当Spider处理response返回result时,该方法被调用
- process_start_requests 当spider发出请求时,被调用
位置是Spiders-->process_start_requests-->Scrapy Engine(Scrapy Engine是scrapy官方结构图中的组件)
2.Downloader Middleware有以下几个函数被管理
- process_request request通过下载中间件时,该方法被调用
- process_response 下载结果经过中间件时被此方法处理
- process_exception 下载过程中出现异常时被调用
编写中间件时,需要思考要实现的功能最适合在那个过程处理,就编写哪个方法.
中间件可以用来处理请求,处理结果或者结合信号协调一些方法的使用等.也可以在原有的爬虫上添加适应项目的其他功能,这一点在扩展中编写也可以达到目的,实际上扩展更加去耦合化,推荐使用扩展.
5.代理问题
(1).为什么会用到代理
(2).代理怎么使用(具体代码,请求在什么时候添加的代理)
(3).代理失效了怎么处理
6.验证码处理
(1).登陆验证码处理
(2).爬取速度过快出现的验证码处理
(3).如何用机器识别验证码
7.模拟登陆问题
(1).模拟登陆流程
(2).cookie如何处理
(3).如何处理网站传参加密的情况
8、分布式
(1).分布式原理
这里重要的就是我的队列通过什么维护?
这里一般我们通过Redis为维护,Redis,非关系型数据库,Key-Value形式存储,结构灵活。
并且redis是内存中的数据结构存储系统,处理速度快,提供队列集合等多种存储结构,方便队列维护
如何去重?
这里借助redis的集合,redis提供集合数据结构,在redis集合中存储每个request的指纹
在向request队列中加入Request前先验证这个Request的指纹是否已经加入集合中。如果已经存在则不添加到request队列中,如果不存在,则将request加入到队列并将指纹加入集合
如何防止中断?如果某个slave因为特殊原因宕机,如何解决?
这里是做了启动判断,在每台slave的Scrapy启动的时候都会判断当前redis request队列是否为空
如果不为空,则从队列中获取下一个request执行爬取。如果为空则重新开始爬取,第一台丛集执行爬取向队列中添加request
如何实现上述这种架构?
这里有一个scrapy-redis的库,为我们提供了上述的这些功能
scrapy-redis改写了Scrapy的调度器,队列等组件,利用他可以方便的实现Scrapy分布式架构
(2).分布式如何判断爬虫已经停止了
查一下爬虫的状态:
spider.getStatus();//获取爬虫状态
(3).分布式去重原理
(4).分布式爬虫的实现:
- (1).使用两台机器,一台是win10,一台是ubuntu16.04,分别在两台机器上部署scrapy来进行分布式抓取一个网站.
- (2).ubuntu16.04的ip地址为39.106.155.194,用来作为redis的master端,win10的机器作为slave.
- (3).master的爬虫运行时会把提取到的url封装成request放到redis中的数据库:“dmoz:requests”,并且从该数据库中提取request后下载网页,再把网页的内容存放到redis的另一个数据库中“dmoz:items”.
- (4).slave从master的redis中取出待抓取的request,下载完网页之后就把网页的内容发送回master的redis.
- (5).重复上面的3和4,直到master的redis中的“dmoz:requests”数据库为空,再把master的redis中的“dmoz:items”数据库写入到mongodb中.
- (6).master里的reids还有一个数据“dmoz:dupefilter”是用来存储抓取过的url的指纹(使用哈希函数将url运算后的结果),是防止重复抓取的.
三、selenium模拟登录,遇到验证码:
截图,找到验证码的位置,进行识别
#因为验证码不能一次就正确识别,我加了循环,一直识别,直到登录成功
while True:
#清空验证码输入框,因为可能已经识别过一次了,里面有之前识别的错的验证码
driver.find_element_by_name("verificationCode").clear()
# 截图或验证码图片保存地址
screenImg = "H:\screenImg.png"
# 浏览器页面截屏
driver.get_screenshot_as_file(screenImg)
# 定位验证码位置及大小
location = driver.find_element_by_name('authImage').location
size = driver.find_element_by_name('authImage').size
#下面四行我都在后面加了数字,理论上是不用加的,但是不加我这截的不是验证码那一块的图,可以看保存的截图,根据截图修改截图位置
left = location['x']+530
top = location['y']+175
right = location['x'] + size['width']+553
bottom = location['y'] + size['height']+200
# 从文件读取截图,截取验证码位置再次保存
img = Image.open(screenImg).crop((left, top, right, bottom))
#下面对图片做了一些处理,能更好识别一些,相关处理再百度看吧
img = img.convert('RGBA') # 转换模式:L | RGB
img = img.convert('L') # 转换模式:L | RGB
img = ImageEnhance.Contrast(img) # 增强对比度
img = img.enhance(2.0) # 增加饱和度
img.save(screenImg)
# 再次读取识别验证码
img = Image.open(screenImg)
code = pytesseract.image_to_string(img)
#打印识别的验证码
#print(code.strip())