爬虫面试流程:
- 先自我介绍(年龄学历毕业时间学校入职时间工作年限经验等等,顺带之前的公司说一下,还有介绍一下自己会的技术,说说自己的技能点)
- 介绍项目,简历上的项目都说一下,有些有难点的可以多说说,难点不高的直接简介(这个时候,有的人会直接打断你,问你项目中的啥啥啥是如何实现的),介绍项目的时候竟然多说些知识点和难点
- 这一步基本就等他提问了
面试官会问到的问题范围:
- 首先会根据你的技能点来问
- 会根据你的项目来问
- 会问你搞过最难的网站是啥,遇到过最难的反爬是什么等
- 会根据他们的招聘需求来提问
- python的一些问题
python基础-------------------> 重点
一、基础语法
1.输入与输出
1.1 代码中要修改不可变数据会出现什么问题? 抛出什么异常?
答:代码不会正常运行,抛出 TypeError 异常。
1.2 不使用中间变量交换a和b的值?
相加相减法
异或运算
a,b=b,a
1.3 print 调用 Python 中底层的什么方法?
答:sys.stdout.write()方法
1.4 简述对input()函数的理解?
答:在 Python3 中,input()获取用户输入,不论用户输入的是什么,获取到的都是字符串类型的。
2.条件与循环
2.1 range和xrange的区别?
答:二者用法相同,不同:range返回列表,xrange返回生成器。xrange比range性能更好。
3.read、readline 和 readlines 的区别?
read读取整个文件
readline生成器方法
readlines返回迭代器供我们遍历
4.异常处理
try:
可能触发异常的语句
except 错误类型1 [as 变量1]:
处理语句1
except 错误类型2 [as 变量2]:
处理语句2
except Exception [as 变量3]:
不是以上错误类型的处理语句
else:
未发生异常的语句
finally:
无论是否发生异常的语句
4.1 在 except 中 return 后还会不会执行 finally 中的代码?怎么抛出自定义异常?
答:会继续处理 finally 中的代码;用 raise 方法可以抛出自定义异常。
5.模块和包
5.1 常用的 Python 标准库都有哪些?
标准库
os 操作系统
time 时间模块
random 随机
pymysql 连接数据库
threading 线程
multiprocessing 进程
queue 队列
第三方库
django
flask
pytorch和tensorflow
numpy和pandas
5.2 赋值、浅拷贝和深拷贝的区别?(重点)
赋值的本质就是将一个对象的内存空间地址赋值给一个变量,让变量指向该内存空间地址。
浅拷贝是拷贝了源对象的引用,并创建了一个新的内存空间地址。但是引用的对象的子对象的地址仍然是源对象的,所以当源对象的子对象发生改变时,拷贝对象内的子对象同时也跟着改变。
深拷贝就是彻底的拷贝,完全的拷贝了父对象及子对象,同时指向一个新的内存空间地址。源对象与拷贝对象之间的修改互不影响。
5.3 init 和__new__的区别?
init 在对象创建之后,对对象进行初始化
new是在对象创建之前创建一个对象,并将对象返回给init进行初始化
init是在new的基础上进行的初始化操作
new是一个静态方法,init是一个实例方法
new方法必须要有一个返回值,init方法没有返回值
new方法是在init方法之前被调用的
new方法是一个类方法,init方法是一个实例方法
new方法可以控制对象的创建过程,init方法不能控制对象的创建过程
new方法可以返回当前类的对象,init方法不能返回当前类的对象
5.4 unittest 是什么?
答:在 Python 中,unittest 是 Python 中的单元测试框架。它拥有支持共享搭建、自动测试、在测试
中暂停代码、将不同测试迭代成一组,等的功能。
5.5 模块和包是什么?
答:在 Python 中,模块是搭建程序的一种方式。每一个 Python 代码文件都是一个模块,并可以引用其他的模块,比如对象和属性。
一个包含许多 Python 代码的文件夹是一个包。一个包可以包含模块和子文件夹。
6.Python特性
6.1Python 是强语言类型还是弱语言类型?
Python是强类型的动态脚本语言。
强类型:不允许不同类型相加
动态:不使用显示数据类型声明,且确定一个变量的类型是在第一次给它赋值的时候
脚本语言:解释性语言,不需要编译。
6.2 谈一下什么是解释性语言,什么是编译性语言?
解释性语言:边运行便翻译
编译型语言:翻译成机器语言后再执行
6.3 Python 中有日志吗?怎么使用?
答:使用logging模块,调用logging.basicConfig()方法
6.4 关于 Python 程序的运行方面,有什么手段能提升性能?
使用多进程、多线程
使用C或C++编写性能影响较大的部分代码
IO阻塞可以使用IO多路复用
尽量使用Python内建函数
尽量使用局部变量
6.5 Python的作用域?
Local)—>被嵌入函数作用域Enclosing locals—>Global—>内置作用域(Built-in)
6.6 什么是 Python 自省?
答:自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.
简单一句就是运行时能够获得对象的类型.比如 type(),dir(),getattr(),hasattr(),isinstance().
6.7 什么是 Python 的命名空间?
答:在 Python 中,所有的名字都存在于一个空间中,它们在该空间中存在和被操作——这就是命名空间。它就好像一个盒子,每一个变量名字都对应装着一个对象。当查询变量的时候,会从该盒子里面寻找相应的对象。
6.8 你所遵循的代码规范是什么?请举例说明其要求?(重点)
PEP8规范
变量
函数和方法
类
模块和包:除特殊模块 init 之外,模块名称都使用不带下划线的小写字母
关于参数
一些数字
一个函数不超过30行代码
一个类不超过200行代码,不超过10个方法,一个模块不超过500行
pep8工具验证是否符合规范
二、数据类型
1.字典
2.字符串
3.列表
4.元组
5.集合
第三章 Python高级
一、元类
Python 中类方法、类实例方法、静态方法有何区别?
类方法:是类对象的方法,在定义时需要在上方使用“@classmethod”进行装饰,形参为 cls,表示类对象,类对象和实例对象都可调用
类实例方法:是类实例化对象的方法,只有实例对象可以调用,形参为 self,指代对象本身;
静态方法:是一个任意函数,在其上方使用“@staticmethod”进行装饰,可以用对象直接调用,静态方法实际上跟该类没有太大关系。
2.Python中如何动态获取和设置对象的属性?
if hasattr(Parent,‘x’):
print(getattr(Parent,‘x’))
setattr(Parent,‘x’,3)
print(getattr(Parent,‘x’))
二、内存管理和垃圾回收机制
1.Python的内存管理机制及调优手段?
垃圾回收:
引用计数
标记清除
分代回收
当某些内存块 M 经过了 3 次垃圾收集的清洗之后还存活时,我们就将内存块 M 划到一个集合A 中去,而新分配的内存都划分到集合 B 中去。当垃圾收集开始工作时,大多数情况都只对集合 B 进行垃圾回收,而对集合 A 进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合 B 中的某些内存块由于存活时间长而会被转移到集合 A 中,当然,集合 A 中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。
调优手段:
1.手动垃圾回收
2.调高垃圾回收阈值
3.避免循环引用(手动解循环引用和使用弱引用)
2.内存泄漏是什么?如何避免?
指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。导致程序运行速度减慢甚至系统崩溃等严重后果。
原因:有 del() 函数的对象间的循环引用是导致内存泄漏的主凶。
解决:不使用一个对象时使用:del object 来删除一个对象的引用计数就可以有效防止内存泄漏问题
判断是否内存泄漏:sys.getrefcount(obj),如果返回的引用计数不为 0,说明在此刻对象 obj 是不能被垃圾回收器回收掉的。
三、函数
- 函数参数
1.1 Python 函数调用的时候参数的传递方式是值传递还是引用传递?
答:Python 的参数传递有:位置参数、默认参数、可变参数、关键字参数。
函数的传值到底是值传递还是引用传递,要分情况:
不可变参数用值传递(拷贝)
可变参数用引用传递
1.2 对缺省参数的理解 ?
答:缺省参数指在调用函数的时候没有传入参数的情况下,调用默认的参数,在调用函数的同时赋值时,所传入的参数会替代默认参数。
*args 是不定长参数
**kwargs 是关键字参数
1.3 为什么函数名字可以当做参数用?
答:Python 中一切皆对象,函数名是函数在内存中的空间,也是一个对象。
2.内建函数
2.1 map函数和reduce函数?
答:map()方法会将 一个函数 映射到序列的每一个元素上,生成新序列返回。(函数可以传递1或多个参数)
reduce()是将传入的函数作用在序列的第一个元素得到结果后,把这个结果继续与下一个元素作用(累积计算),返回一个数。(函数只能传递两个参数)
2.2 递归函数停止的条件?
递归的终止条件一般定义在递归函数内部,在递归调用前要做一个条件判断,根据判断的结果选择是继续调用自身,还是 return;返回终止递归。
终止的条件:
判断递归的次数是否达到某一限定值
判断运算的结果是否达到某个范围等,根据设计的目的来选择
2.3 回调函数如何通信的?
答:回调函数是把**函数的指针(地址)**作为参数传递给另一个函数,将整个函数当作一个对象,赋值给调用的函数。
2.4 一句话解决阶乘函数?
reduce(lambda x,y:x*y,range(1,n+1))
3.Lambda
3.1 什么是匿名函数?
答:匿名函数是指没有具体名称的函数,也就是没有 def 语句定义的函数,匿名函数的语法是:lambda 参数列表:表达式
匿名函数的特点:
1.匿名函数只能有一个表达式,不用写 return,返回值就是该表达式的结果
2.匿名函数也是一个对象,可以赋值给一个变量
3.匿名函数可以作为参数传递给其他函数
4.匿名函数可以作为其他函数的返回值
5.匿名函数不能直接调用 print,因为 lambda 需要一个表达式
6.匿名函数不能包含命令
3.2 为什么要用匿名函数?
答:匿名函数的主要作用是简化代码,减少代码量,提高代码的可读性。
四、设计模式
1.单例
1.1 手写一个单例?
class Singleton:
def new(cls,*args,**kwargs):
if not hasattr(cls,“_instance”):
cls._instance =super(Singleton,cls).new(cls)
return cls._instance
1.2 单例模式的应用场景?
共享资源,避免由于资源操作时导致的性能或损耗等,如日志文件、应用配置
控制资源的情况下,方便资源之间的互相通信,如线程池
2.装饰器
2.1对装饰器的理解 ,并写出一个计时器记录方法执行性能的装饰器?
答:装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
import time
def timeit(func):
def wrapper():
start = time.clock()
func()
end =time.clock()
print ‘used:’, end - start
return wrapper
@timeit
def foo():
print ‘in foo()’
2.2 什么是闭包三要素?
函数嵌套
内调用外
外返回内
2.3 函数装饰器有什么作用?
答:装饰器本质上是一个 Python 函数,它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能。装饰器的返回值也是一个函数的对象,它经常用于有切面需求的场景。 比如:插入日志、性能测试、事务处理、缓存、权限的校验等场景 有了装饰器就可以抽离出大量的与函数功能本身无关的雷同代码并发并继续使用。
3.生成器
3.1生成器、迭代器的区别?
答:生成器是一种特殊的迭代器,迭代器是一种特殊的可迭代对象。
3.2 yield 用法?
答:yield 用于定义一个生成器函数,生成器函数返回的是一个生成器对象,生成器对象是一个迭代器对象,可以使用 next() 函数获取下一个值,也可以使用 for 循环遍历。
五、面向对象
1.类
类是创建对象的模板。
2.对象
2.1 Python 中的可变对象和不可变对象?
答:可变对象:list、dict、set
2.2 Python 中 is 和= =的区别?
答:is是通过id判断,而==是通过值判断。
2.3Python中的魔法方法?
创建对象自动调用
init 构造器,当一个实例被创建的时候初始化的方法。但是它并 不是实例化调用的第一个方法。
__new__才是实例化对象调用的第一个方法,它只取下 cls 参数,并把 其他参数传给 init。 __new__很少使用,但是也有它适合的场景,尤其 是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。
call 允许一个类的实例像函数一样被调用 。
getitem 定义获取容器中指定元素的行为,相当于 self[key] 。
getattr 定义当用户试图访问一个不存在属性的时候的行为 。
setattr 定义当一个属性被设置的时候的行为 。
getattribute 定义当一个属性被访问的时候的行为
2.4面向对象中怎么实现只读属性?
答:使用@property装饰器来包装getter方法,使其看起来像属性访问。
2.5谈谈你对面向对象的理解?
答:面向对象是相对于面向过程而言的。面向过程语言是一种基于功能分析的、以算法为中心的程序设计方法;而面向对象是一种基于结构分析的、以数据为中心的程序设计思想。在面向对象语言中有一个有很重要东西,叫做类。
面向对象有三大特性:封装、继承、多态。
封装:封装就是把一些属性和方法封装到一个类中,然后通过实例化对象来调用这些属性和方法。
继承:继承就是子类继承父类的属性和方法,子类可以直接调用父类的属性和方法,也可以重写父类的属性和方法。
多态:多态就是子类重写父类的方法,然后通过子类的实例化对象来调用这个方法,这样就实现了多态。
1.python中的类方法和静态方法
2.列表最快的去重方式
可以使用 set 函数将列表转换为集合,然后再转换回列表,这样就可以去除列表中的重复元素了
3.深拷贝和浅拷贝的区别
浅拷贝:只拷贝父对象,不会拷贝对象的内部的子对象。
深拷贝:拷贝父对象及其子对象。
4.装饰器的原理和使用方法
装饰器是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
装饰器的使用:在不改变原有函数代码的基础上,为其增加额外功能,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
5.工厂函数
工厂函数是一种用来创建对象的函数,而不是用类来创建对象。工厂函数可以返回任意类型的对象,而不仅仅是类的实例。
6.python的资源竞争
python的资源竞争是指多个线程同时访问同一个资源,由于线程切换的原因,导致最终结果与预期不一致,这就是资源竞争。
python的资源竞争可以通过锁来解决,锁可以保证同一时刻只有一个线程访问资源,从而避免资源竞争。
7.Python垃圾回收机制
Python的垃圾回收机制是引用计数,当一个对象的引用计数为0时,就会被垃圾回收机制回收。
引用计数的优点是简单,实时性高,缺点是无法解决循环引用的问题。
爬虫基础
什么是爬虫?
爬虫是一种按照一定的规则,自动地抓取网络信息的程序或者脚本。
-
Python里match与search的区别?
match是从头开始匹配,search是从任意位置开始匹配。 -
Python字符串查找和替换?
答:使用replace()方法
str.replace(old, new[, max])
old – 将被替换的子字符串。
new – 新字符串,用于替换old子字符串。
max – 可选字符串, 替换不超过 max 次
3.用 Python 匹配 HTML g tag 的时候,<.> 和 <.?> 有什么区别?
答:<.>是贪婪匹配,会从第一个“<”开始匹配,直到最后一个“>”中间所有的字符都会匹配到,中间可能会包含“<>”。
<.?>是非贪婪匹配,从第一个“<”开始往后,遇到第一个“>”结束匹配,这中间的字符串都会匹配到,但是不会有“<>”。
4.正则元字符的含义?
答:. 匹配除换行符以外的任意字符
^ 匹配字符串的开始
$ 匹配字符串的结束
* 匹配0个或多个的表达式
+ 匹配1个或多个的表达式
? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
{n} 匹配n个前面表达式
{n,} 匹配n个或者多个前面表达式
{n,m} 匹配n到m个前面表达式
a|b 匹配a或b
[xyz] 匹配所包含的任意一个字符
[^xyz] 匹配未包含的任意字符
[a-z] 匹配指定范围内的任意字符
[^a-z] 匹配任何不在指定范围内的任意字符
\b 匹配一个单词边界,也就是指单词和空格间的位置
\d 匹配一个数字字符。等价于[0-9]
\D 匹配一个非数字字符。等价于[^0-9]
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[\f\n\r\t\v]
\S 匹配任何非空白字符。等价于[^\f\n\r\t\v]
\w 匹配包括下划线的任何单词字符。等价于’[A-Za-z0-9_]’
\W 匹配任何非单词字符。等价于’[^A-Za-z0-9_]’
\n 匹配一个换行符
\t 匹配一个制表符
\uxxxx 匹配一个以十六进制数xxxx(xxxx的值在1到FFFF之间)所指定的Unicode字符
12.scrapy的优缺点
优点:
1)scrapy 是异步的,可以灵活调节并发量
2)采取可读性更强的 xpath 代替正则,速度快
3)写 middleware,方便写一些统一的过滤器
4)同时在不同的 url 上爬行
5)支持 shell 方式,方便独立调试
6)通过管道的方式存入数据库,灵活,可保存为多种形式
缺点:
1)无法用它完成分布式爬取
2)自身去重效果差,消耗内存,且不能持久化
3)对于需要执行js才能获取数据的网页,爱莫能助
4)兼容了下载图片与视频,但是可用性比较差
5)自身扩展的log模块,不好用,经常需要自定义
6)基于 twisted 框架,运行中的 exception 是不会干掉 reactor(反应器),并且异步框架出错后 是不会停掉其他任务的,数据出错后难以察觉,预警系统简单的使用邮件,很不友好
13.scrapy使用
1.安装
pip install scrapy
2.创建项目
scrapy startproject myproject # myproject 为项目名称
3.创建爬虫
cd myproject # 进入项目目录
scrapy genspider example example.com # example 为爬虫名称,example.com 为爬取的域名
4.运行爬虫
scrapy crawl example # example 为爬虫名称
scrapy运行流程
1.引擎从调度器中取出一个url
2.引擎把url封装成一个请求对象交给下载器
3.下载器把网页下载下来交给引擎
4.引擎把网页内容交给爬虫
5.爬虫解析网页内容,提取url和数据
6.爬虫把提取的url交给引擎,把数据交给项目管道
7.引擎把url交给调度器,把数据交给项目管道
8.项目管道把数据存储起来
scrapy核心组件
1.引擎
2.调度器
3.下载器
4.爬虫
5.项目管道
6.下载器中间件
7.爬虫中间件
常见加密算法
1.对称加密
2.非对称加密
3.哈希加密
4.MD5
5.SHA1
6.SHA256
7.SHA512
8.RSA
9.DES
10.AES
11.AES
12.base64
13.scrapy-redis的去重原理
Scrapy 的去重是利用集合来实现的,而在 Scrapy 分布式中的去重就需要利用共享的集合,那么这里使用的就是 Redis 中的集合数据结构。我们来看看去重类是怎样实现的,源码文件是 dupefilter.py,其内实现了一个 RFPDupeFilter 类
这里同样实现了一个 request_seen 方法,和 Scrapy 中的 request_seen 方法实现极其类似。不过这里集合使用的是 server 对象的 sadd 操作,也就是集合不再是一个简单数据结构了,而是直接换成了数据库的存储方式
14.透明代理IP、普通匿名代理IP和高级匿名代理IP的区别含义
透明代理(Transparent Proxy):透明代理虽然可以直接“隐藏”你的IP地址,但是还是可以查到你是谁。
匿名代理(Anonymous Proxy):使用匿名代理,别人只能知道你用了代理,无法知道你是谁。
高匿代理(Elite proxy或High Anonymity Proxy):高匿代理让别人根本无法发现你是在用代理,所以是最好的选择。
15.nginx是啥,正向代理和反向代理
正向代理:代理服务器位于客户端和目标服务器之间,客户端向代理服务器发送请求,代理服务器再向目标服务器发送请求,然后将目标服务器的响应返回给客户端
反向代理:代理服务器位于客户端和目标服务器之间,客户端向代理服务器发送请求,代理服务器再向目标服务器发送请求,然后将目标服务器的响应返回给客户端
正向代理和反向代理的区别:
1)正向代理是客户端主动发起请求,反向代理是服务器主动发起请求
2)正向代理是客户端知道代理服务器的存在,反向代理是客户端不知道代理服务器的存在
16.断点续爬
17.通用爬虫和聚焦爬虫
通用爬虫:通用爬虫是指爬取互联网上的任意网站,不需要指定爬取的网站,只需要指定爬取的入口即可
聚焦爬虫:聚焦爬虫是指爬取互联网上的某个网站,需要指定爬取的网站,不需要指定爬取的入口
两者的区别:
1)通用爬虫是爬取互联网上的任意网站,聚焦爬虫是爬取互联网上的某个网站
2)通用爬虫是不需要指定爬取的网站,只需要指定爬取的入口即可,聚焦爬虫是需要指定爬取的网站,不需要指定爬取的入口
18.爬虫的反爬虫策略
1)IP代理池
2)cookie池
3)验证码识别
4)js逆向
5)滑块验证码
6)指纹识别
7)反爬虫框架
1.如何实现多进程、多线程?
实现多进程:multiprocessing模块
【1】 将需要新进程执行的事件封装为函数
【2】 通过模块的Process类创建进程对象,关联函数
【3】 通过进程对象调用start启动进程
【4】join()等待子进程结束
实现多线程:threading模块
【1】 将需要新线程执行的事件封装为函数
【2】 通过模块的Thread类创建线程对象,关联函数
【3】 通过线程对象调用start启动线程
【4】 join()等待子进程结束
- 谈谈你对多进程,多线程,以及协程的理解,项目是否用?
答:性能要求特别高的项目,才会用到多进程、多线程,小项目用不到。
进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大。
线程:调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。
协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
3.什么是多线程竞争
线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态即:
数据几乎同步会被多个线程占用,造成数据混乱 ,即所谓的线程不安全
那么怎么解决多线程竞争问题?-- 锁。
锁的好处:
确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资源竞争下
的原子操作问题。
锁的坏处:
阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
锁的致命问题:死锁。
网络编程
1.UDP和TCP的实现流程?
答:UDP是面向无连接的协议,发送数据之前不需要建立连接,只需要知道对方的IP地址和端口号就可以了。
TCP是面向连接的协议,发送数据之前需要先建立连接,连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
TCP 连接的建立需要经过三次握手,断开连接需要经过四次挥手。
2.简述TCP和UDP的区别以及优缺点?
UDP 是面向无连接的通讯协议。
优点:速度快、操作简单、要求系统资源比较少,可以实现广播模式。
缺点:由于传输数据前不与对方建立连接,不重复发送,不可靠。
TCP 是面向连接的通讯协议,通讯前三次握手建立连接,通讯完成时四次挥手断开连接。
优点:保障数据的准确性,较为可靠。
速度慢,消耗系统资源较多。
3.请简单说一下三次握手和四次挥手?
三次握手:建立连接
客户端向服务器发送消息报文请求连接
服务器收到请求后,回复报文确定可以连接
客户端收到回复,发送最终报文连接建立
四次挥手:断开连接
客户端发送报文请求断开连接
服务端收到请求后,立即回复,表示准备断开
服务端准备就绪,再次发送报文表示可以断开
客户端收到确定,发送最终报文完成断开
4.HTTP 请求方法都有什么?
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。
HTTP1.1 新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法
5.七层网络模型?
应用层
表示层
会话层
传输层
网络层
数据链路层
物理层
6.简述TCP/IP协议的四层模型?
应用层
传输层
网络层
数据链路层
6.url的形式?
形式: scheme://host[:port#]/path/…/[?query-string][#anchor]
scheme:协议类型,如http、https、ftp、file等
host:域名或者ip地址
port:端口号,如http默认端口为80,https默认端口为443
path:访问路径
query-string:查询字符串,如?name=xxx&age=xxx
anchor:锚点,如#top
1.如何请求http2.0?
使用httpx、httpx[http2]进行请求
2.多线程和多进程多携程
多线程:多线程是指在一个进程中同时运行多个线程,每个线程都有自己的一块栈空间,线程之间共享进程的数据段。
多进程:多进程是指在一个程序中同时运行多个进程,每个进程都有自己的一块数据段,进程之间不共享数据。
多协程:多协程是指在一个进程中同时运行多个协程,每个协程都有自己的一块栈空间,协程之间共享进程的数据段。
3.scrapy的架构组成,五大件
引擎:引擎负责整个系统的数据处理流程,触发事务(如:启动爬虫、暂停爬虫、关闭爬虫)。
调度器:调度器接受引擎发过来的请求,压入队列中,并在引擎再次请求的时候返回。
下载器:下载器负责下载网页内容,并将网页内容返回给蜘蛛。
spider:蜘蛛从下载器中获取网页内容,并通过解析网页,提取(爬取)数据。
项目管道:项目管道负责处理蜘蛛从网页中提取的数据,主要的功能是持久化数据。
4.http和https的区别
HTTPS比HTTP更安全,但是性能更低
HTTP:超文本传输协议,默认端口号是80
超文本:是指超过文本,不仅限于文本;还包括图片、音频、视频等文件
传输协议:是指使用共用约定的固定格式来传递转换成字符串的超文本内容
HTTPS:HTTP + SSL(安全套接字层),即带有安全套接字层的超本文传输协,默认端口号:443
SSL对传输的内容(超文本,也就是请求体或响应体)进行加密
5.http的三次握手四次挥手,http协议是什么(大公司喜欢问这个)
1.HTTP是应用层协议,TCP是传输层协议!
2.TCP协议属于传输层协议(UDP也属于传输层协议,但是UDP协议是无状态的)。建立一个TCP连接需要三次握手,断开一个TCP连接需要四次挥手。
3.三次握手:客户端发送一个SYN包(SYN=j)到服务器,并进入SYN_SEND状态,等待服务器确认;服务器收到SYN包,必须确认客户的SYN(ACK=j+1),
同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=k+1),
此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
4.四次挥手:客户端发送一个FIN,用来关闭客户端到服务器的数据传送,客户端进入FIN_WAIT_1状态;服务器收到FIN后,发送一个ACK给客户端,
确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),服务器进入CLOSE_WAIT状态;客户端收到ACK后,检查是否还有未发送的数据,如果有,继续发送FIN,
如果没有,客户端进入FIN_WAIT_2状态;服务器收到FIN后,进入LAST_ACK状态,客户端收到FIN后,进入TIME_WAIT状态,接着服务器发送ACK,客户端收到ACK后,
进入CLOSED状态,完成四次挥手。
6.80、443端口代表什么
80代表http,443代表https
7.常见状态码
状态码200:
状态码200代表请求成功
状态码302:
状态码302代表临时跳转
状态码301 :
状态码301代表永久跳转
状态码304:
状态码304代表资源未修改
状态码403:
状态码403代表服务器拒绝访问
状态码404:
状态码404代表请求的服务器资源不存在
状态码500:
状态码500代表服务器内部错误
状态码502:
状态码502代表网关错误
常用数据库基操
19.mongobd数据库的增删改查
id是可以自己指定的, 但是自己指定的话, 是要确保唯一
无模式, 灵活的文档类型, 无需固定的结构.
1)增加数据
db.集合名.insert(数据) # 数据为字典
2)删除数据
db.集合名.remove(条件) # 条件为字典
3)修改数据
db.集合名.update(条件,修改后的数据) # 条件为字典
4)查询数据
db.集合名.find(条件) # 条件为字典
db.集合名.find(条件).limit(条数) # 查询条数
db.集合名.find(条件).sort(排序条件) # 排序条件为字典
db.集合名.fingOne(条件) # 查询一条数据
db.集合名.update(查询条件, 新对象). # update默认只更新第一个, 要更新多个加上multi参数. 设置为true
20.Redis数据库的增删改查
redis的key的长度最好不要超过44(39)
redis特点.
1. key-value型, value支持多种不同的数据结构, 功能比较丰富.
2. 核心命令执行是单线程的. 每个命令都具有原子性. 6.0以后加入的多线程只是用来处理网络数据和协议.
3. 基于内存, 低延迟, 效率高.
4. 基于内存, 也支持数据持久化.
5. 支持集群.
1)增加数据
set key value # key为键,value为值
2)删除数据
del key # key为键
3)修改数据
set key value # key为键,value为值
4)查询数据
get key # key为键
21.Mysql数据库的增删改查
1)增加数据
insert into 表名(字段名) values(值)
2)删除数据
delete from 表名 where 条件
3)修改数据
update 表名 set 字段名=值 where 条件
4)查询数据
select * from 表名 where 条件
面试会涉及到的具体问题:
1.用过什么数据库,数据库之间进行比较,mysql和mogodb、redis之间的性能,什么场景用什么数据库
2.通常反爬手段都有什么
验证码识别、ip代理池、cookie池、js逆向、滑块验证码、指纹识别、反爬虫框架等等
3.用过什么爬虫框架,然后会问你一些关于这个爬虫框架的问题
4.一般用什么提取数据,xpath、正则、BeautifulSoup之间的区别,优缺点
5.是否做过数据清晰去重过滤,是怎么实现的
6.分布式爬虫的搭建,爬取的量级,至少日均百万起等等
7.js逆向的问题,对称加密和不对称加密算法,遇到过什么加密算法,是如何解决的,更细节的或许会问加密的密文长度或者key的长度等等
8.hook问题,如何hook,hook过什么,是如何实现的
hook:
1)hook是一种编程技术,用于修改或扩展其他代码,无需修改被扩展的代码本身
function hook(target, key, descriptor) {
const fn = descriptor.value;
descriptor.value = function() {
console.log(‘before’);
const ret = fn.apply(this, arguments);
console.log(‘after’);
return ret;
};
10.Linux命令
11.日志处理方式
日志分级,根据日志级别进行日志处理
12.爬虫是如何管理的等等gerapy,crawlab
gerapy:爬虫管理平台,可以管理爬虫,可以查看爬虫的运行状态,可以查看爬虫的运行日志,可以查看爬虫的运行数据等等
crawlab:爬虫管理平台,可以管理爬虫,可以查看爬虫的运行状态,可以查看爬虫的运行日志,可以查看爬虫的运行数据等等
两者区别在于:
1)gerapy是基于python的,crawlab是基于nodejs的
2)gerapy是基于scrapy的,crawlab是基于scrapy-redis的
3)gerapy是基于flask的,crawlab是基于vue的
4)gerapy是基于mongodb的,crawlab是基于mysql的
5)gerapy是基于redis的,crawlab是基于redis的
13.自己爬虫每秒最大的请求量等
14.如果说了我们做了web页面,会问你请求量级,然后会问用的什么数据库,数据库是如何设计的
15.会问你常用的一些工具,爬虫工具,抓包工具等
16.抓包工具抓不到包怎么办,web页面上全局搜索搜索不到变量名怎么办
20.抓包不返回数据给你怎么办,也就是抓包工具被检测到了,抓包工具是如何被检测到的
1)抓包工具被检测到了,那么就需要换一个抓包工具,或者换一个抓包工具的版本
21.如何高效的剔除无用代理
进行request请求,如果请求成功则说明代理可用,如果请求失败则说明代理不可用
“”"
“”"
7.Linux基础和git
7.1 Linux 的基本命令(怎么区分一个文件还是文件夹)
答:ls -F 在显示名称的时候会在文件夹后添加“/”,在文件后面加“*”
7.2 日志以什么格式,存放在哪里?
答:日志以文本可以存储在“/var/log/”目录下后缀名为.log
7.3 Linux 查看某个服务的端口?
netstat -nlp | grep 80
7.4 在 linux 中 find 和 grep 的区别?
grep 是查找匹配条件的行,find 是搜索匹配条件的文件
7.5 Linux 重定向命令有哪些?有什么区别?
重定向> 将命令执行结果重定向到一个文件test.txt(存在则覆盖,不存在则新建)
重定向> 将命令执行结果追加到一个文件test.txt(原内容不影响)
7.6 软链接和硬链接的区别?
答:软连接类似 Windows 的快捷方式,当删除源文件时,那么软链接也失效了。硬链接可以理解为源文件的一个别名,多个别名所代表的是同一个文件。当 rm 一个文件的时候,那么此文件的硬链接数减1,当硬链接数为 0 的时候,文件被删除。
7.7 git 合并文件有冲突,如何处理?
a)git merge 冲突了,根据提示找到冲突的文件,解决冲突如果文件有冲突,那么会有类似的标记;
b)修改完之后,执行 git add 冲突文件名
c)git commit 注意:没有-m 选项 进去类似于 vim 的操作界面,把 conflict 相关的行删除掉直接 push 就可以了,因为刚刚已经执行过相关 merge 操作了。
8.flaks的开发模式MVC分别代表什么
17.消息队列机制,有那些消息队列celery,RocketMQ,Kafka
18.如何实现分布式爬虫
18.简单的sql语句
select * from table where id = 1 # 等于
select * from table where id = 1 and name = ‘zhangsan’ # 与
select * from table where id = 1 or name = ‘zhangsan’ # 或
select * from table where id in (1,2,3) # in
select * from table where id not in (1,2,3) # not in
select * from table where id between 1 and 10 # between
select * from table where id not between 1 and 10 # not between
select * from table where id is null # 无值
select * from table where id is not null # 有值
“”"
数据库
一、Mysql
1.Python操作Mysql的步骤?
(1)连接数据库
(2)创建游标
(3)执行sql语句
(4)提交事务
(5)关闭游标
(6)关闭数据库连接
2.SQL 的 select 语句完整的执行顺序?
(1)FROM <left_table> <join_type> JOIN <right_table> ON <on_predicate>
(2)WHERE <where_predicate>
(3)GROUP BY <group_by_specification>
(4)HAVING <having_predicate>
(5)SELECT DISTINCT <select_list>
(6)ORDER BY <order_by_list>
(7)LIMIT <limit_number> offset number
3.事务的四大特性?
原子性
一致性
隔离性
持久性
4.数据库的索引?
索引的实现通常使用B_Tree,加快数据的查询速度。
5.数据库如何优化查询效率?
存储引擎,InnoDB实务,MyISAM查询速度更快
分表分库
对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
避免在 where 子句中对字段进行 null 值判断
避免在 where 子句中使用 != 或 <> 操作符
避免在 where 子句中使用 or 来连接条件
Update 语句,如果只更改 1、2 个字段,不要 Update 全部字段,否则频繁调用会引起明显的
性能消耗,同时带来大量日志
对于多张大数据量(这里几百条就算大了)的表 JOIN,要先分页再 JOIN,否则逻辑读会很高,性能很差。
6.Mysql集群的优缺点?
优点:
高可用
快速的自动失效切换
灵活的分布式体系结构,没有单点故障
高吞吐量和低延迟
可扩展性强,支持在线扩容
缺点:
存在很多限制,比如:不支持外键
部署、管理、配置很复杂
占用磁盘空间大、内存大
备份和恢复不方便
重启的时候,数据节点将数据 load 到内存需要很长的时间
7.你用的 Mysql 是哪个引擎,各引擎之间有什么区别?
主要 MyISAM 与 InnoDB 两个引擎,其主要区别如下:
InnoDB 支持事务,MyISAM 不支持
InnoDB 支持外键,MyISAM 不支持
InnoDB 支持行锁,MyISAM支持表级锁
8.数据库设计范式?
第一范式:列不可拆分
第二范式:记录可以唯一区分
第三范式:外键建立在主键上
二、Redis
非关系型数据库
类型 特点 使用场景
string 简单key-value类型,value可为字符串和数字 常规计数(微博数, 粉丝数等功能)
hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象 存储部分可能需要变更的数据(比如用户信息)
list 有序可重复列表 消息队列等
set 无序不可重复集合 存储并计算关系(如微博,关注人或粉丝存放在集合,可通过交集、并集、差集等操作实现如共同关注、共同喜好等功能)
sortedset 每个元素带有分值的集合 各种排行榜
“”"
“”"
Q:在Python中如何实现单例模式。
单例模式是一种常用的软件设计模式,它的主要特点是保证一个类仅有一个实例,并提供一个访问它的全局访问点。
Python中可以使用__new__方法实现单例模式。
Q:不使用中间变量,交换两个变量a和b的值。
a = a + b
b = a - b
a = a - b
Q:写一个删除列表中重复元素的函数,要求去重后元素相对位置保持不变。
def remove_duplicate_element(l):
return list(set(l))
Q:Lambda函数是什么,举例说明的它的应用场景。
Lambda函数是一个匿名函数,它的定义形式为:lambda 参数列表:表达式
Lambda函数通常应用在需要一个函数,但是又不想费神去命名一个函数的场合。
Q:说说Python中的浅拷贝和深拷贝。
浅拷贝:只拷贝父对象,不会拷贝对象的内部的子对象。
深拷贝:拷贝对象及其子对象。
Q:Python是如何实现内存管理的?
Python中的内存管理是通过引用计数来实现的,当一个对象的引用计数为0时,该对象就会被垃圾回收机制回收。
Q:说一下你对Python中迭代器和生成器的理解。
迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。
字符串,列表或元组对象都可用于创建迭代器:
str = “runoob”
it = iter(str)
print (next(it)) # 输出 r
print (next(it)) # 输出 u
print (next(it)) # 输出 n
print (next(it)) # 输出 o
生成器是一个返回迭代器的函数,只能用于迭代操作,生成器是一个简单的迭代器。
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()
Q:正则表达式的match方法和search方法有什么区别?
match方法是从字符串的开头开始匹配,search方法是在字符串中搜索匹配。
Q:Python中为什么没有函数重载?
Python中函数的参数没有类型,所以不存在函数重载。
Q:用Python代码实现Python内置函数max。
def my_max(*args):
if len(args) == 0:
return None
max = args[0]
for arg in args:
if arg > max:
max = arg
return max
Q:写一个函数统计传入的列表中每个数字出现的次数并返回对应的字典。
def count_num(nums):
d = {}
for num in nums:
if num in d:
d[num] += 1
else:
d[num] = 1
return d
Q:使用Python代码实现遍历一个文件夹的操作。
with os.scandir(path) as it:
for entry in it:
if not entry.name.startswith(‘.’) and entry.is_file():
print(entry.name)
Q:现有2元、3元、5元共三种面额的货币,如果需要找零99元,一共有多少种找零的方式?
def count(n):
if n == 0:
return 1
if n < 0:
return 0
return count(n-2) + count(n-3) + count(n-5)
print(count(99))
Q:写一个函数,给定矩阵的阶数n,输出一个螺旋式数字矩阵。
def spiral(n):
matrix = [[0 for i in range(n)] for j in range(n)]
i, j, di, dj = 0, 0, 0, 1
for k in range(n*n):
matrix[i][j] = k + 1
if matrix[(i+di)%n][(j+dj)%n]:
di, dj = dj, -di
i += di
j += dj
return matrix
print(spiral(5))
Q:说说你用过Python标准库中的哪些模块。
os、sys、re、time、datetime、random、math、json、pickle、logging、threading、multiprocessing、
socket、urllib、urllib2、urllib3、requests、BeautifulSoup、lxml、pymysql、pymongo、redis、
numpy、pandas、matplotlib、scipy、sklearn、tensorflow、keras、flask、django、tornado、pyqt、pyqt5、wxpython、pyglet、
Q:__init__和__new__方法有什么区别?
__init__方法是用来初始化一个实例,而__new__方法是用来创建一个实例。
Q:输入年月日,判断这个日期是这一年的第几天。
def day_of_year(year, month, day):
days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
days[1] = 29
return sum(days[:month-1]) + day
print(day_of_year(2019, 3, 1))
Q:平常工作中用什么工具进行静态代码分析。
静态代码分析一般使用pylint、pyflakes、pychecker、pyroma、flake8等工具。
Q:说一下你知道的Python中的魔术方法。
Python中的魔术方法有__init__、new、str、repr、call、getattr、setattr、delattr、
getattribute、getitem、setitem、delitem、len、add、sub、mul、truediv、
floordiv、mod、pow、and、or、xor、lt、le、eq、ne、gt、ge、
contains、iter、next、enter、__exit__等。
Q:函数参数*arg和**kwargs分别代表什么?
*arg代表任何多个无名参数,它是一个tuple;
**kwargs代表关键字参数,它是一个dict。
Q:写一个记录函数执行时间的装饰器。
def timeit(func):
def wrapper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
end = time.time()
print(‘time: %s’ % (end - start))
return wrapper
@timeit
def foo():
print(‘foo’)
foo()
Q:什么是鸭子类型(duck typing)?
鸭子类型是动态类型的一种风格,它要求“如果走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子”。
Q:说一下Python中变量的作用域。
Python中变量的作用域分为全局作用域、局部作用域、嵌套作用域和内建作用域。
Q:说一下你对闭包的理解。
闭包是指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。
Q:说一下Python中的多线程和多进程的应用场景和优缺点。
多线程适用于I/O密集型任务,多进程适用于CPU密集型任务。
多线程的优点是切换开销小,缺点是无法利用多核CPU。
多进程的优点是可以利用多核CPU,缺点是切换开销大。
Q:谈谈你对“猴子补丁”(monkey patching)的理解。
猴子补丁是指在程序运行时动态修改代码。
实例:
class A:
def foo(self):
print(‘foo’)
def bar(self):
print(‘bar’)
a = A()
a.foo()
A.foo = bar
a.foo()
Q:编写一个函数实现对逆波兰表达式求值,不能使用Python的内置函数。
def evalRPN(tokens):
stack = []
for token in tokens:
if token in ‘±/':
b = stack.pop()
a = stack.pop()
if token == ‘+’:
stack.append(a + b)
elif token == ‘-’:
stack.append(a - b)
elif token == '’:
stack.append(a * b)
else:
stack.append(int(a / b))
else:
stack.append(int(token))
return stack.pop()
print(evalRPN([‘2’, ‘1’, ‘+’, ‘3’, ‘*’]))
Q:Python中如何实现字符串替换操作?
Python中的字符串是不可变的,所以字符串替换操作实际上是创建了一个新的字符串。
Q:如何剖析Python代码的执行性能?
使用cProfile模块。
Q:如何使用random模块生成随机数、实现随机乱序和随机抽样?
使用random模块的random、shuffle、sample函数。
Q:解释一下线程池的工作原理。
线程池的工作原理是:当有新的任务提交到线程池时,如果线程池中有空闲线程,则立即执行;
如果没有空闲线程,则会将新的任务放入任务队列中,等待有空闲线程时再执行。
Q:举例说明什么情况下会出现KeyError、TypeError、ValueError。
KeyError:字典中查找一个不存在的键。
TypeError:类型错误。
ValueError:值错误。
Q:如何读取大文件,例如内存只有4G,如何读取一个大小为8G的文件?
使用Python的生成器。
Q:说一下你对Python中模块和包的理解。
模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。
Q:说一下你知道的Python编码规范。
PEP8。
Q:说一下namedtuple的用法和作用。
namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,
并可以用属性而不是索引来引用tuple的某个元素。
Q:写一个函数实现字符串反转,尽可能写出你知道的所有方法。
def reverse(s):
return s[::-1]
print(reverse(‘hello’))