Python基础篇:
1 python的8种数据类型并分别列出你会的方法?
number数字、string字符串、none空值、list列表、dict字典、set集合、tuple元组、boolean布尔值
2 哪些是可变元素哪些不可变?
可变:list、dict、set
不可变:number、string、tuple
3 为什么函数的参数不能传可变数据类型,举例说明?
传递可变数据类型,会导致函数原始的参数发生改变;
def changeme(mylist):
mylist.append([1,2,3,4])
print("函数内取值:",mylist)
return
mylist=[10,20,30]
changeme(mylist)
print("函数外取值:",mylist)
4 什么是闭包?为什么使用闭包?闭包有什么优点有什么缺点?
闭包即是在函数内部定义的函数,能够读取其他函数内部变量。
闭包可以读取外层函数内部的变量;也可以让这些变量始终保持在内存中。
缺点:外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大;在退出函数之前,将不使用的局部变量全部删除
优点:可以封装变量,保护作用;可以延伸变量作用范围
5 什么是生成器迭代器和可迭代对象?
生成器:用特殊方式定义的迭代器就是生成器,目前两种定义方式:(1)用()的列表生成式(2)用yield写的函数
迭代器:调用next函数并不断能返回下一个值的就是迭代器
可迭代对象:能放循环遍历的,如for循环遍历,列表等
6 使用生成器生出斐波那契数列
import sys
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()
7使用生成器生出杨辉三角形
def yangHuiSanJiao(max_line):
n=1
ali = []
while n<max_line:
ali2 = []
if n<=2:
ali.append(1)
yield ali
else:
for inn in range(1,len(ali)):
ali2.append(ali[inn-1]+ali[inn])
ali2.insert(0,1)
ali2.append(1)
ali = ali2
yield ali
n+=1
for li in yangHuiSanJiao(9):
print(li)
8 什么是魔术方法,举例说明你知道的魔术方法和具体作用?
魔术方法即python中的特殊方法,允许在类中自定义函数,由两个下划线包围构成;
_init_:用于初始化新创建的对象;
_len_:定义对象的长度
9 说说什么是类方法,静态方法,类属性,实例属性,他们有什么区别?
类方法:需要在普通方法上面增加一个装饰器:
@classmethod
类方法存在于类的内存中,其所有实例都会拿到一个引用,通过类方法就可以修改和查看类属性;
静态方法:类似于类方法,在方法上加装饰器:
@staticmethod
静态方法同样可以通过类名修改类属性。
类属性属于所有对象共有的,也就是所有对象都会使用同一个类属性,类属性定义在类的内部,
类属性可以直接通过类名调用,修改类属性则所有对象使用时就都会改变;
实例属性是对象所拥有的,每个对象会有不同的实例属性,
实例属性需要通过实例进行调用,在类内部也相同。
类属性单独存在于一块内存区域,而所有实例则引用了他们,
修改类属性应该通过类名调用修改,若通过实例修改,则会增加实例的属性。
10 什么是单例模式?请你实现一个单例模式?
一个类只允许创建一个实例对象,并提供访问其唯一的对象的方式。这个类就是一个单例类,这种设计模式叫作单例模式。
11 说说三种高级方法的使用, map reduce filter
map函数
接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素。如果传入了多个iterable参数,function 必须接受相同个数的实参并被应用于从所有可迭代对象中并行获取的项。
语法:map(function, iterable, ...)
function:函数
iterable:一个或多个序列
list(map(abs,[-1,3,-5,8]))
[1, 3, 5, 8]
list(map(lambda x: x.center(3,'#'),['马云','马化腾','李彦宏']))
['#马云', '马化腾', '李彦宏']
#自定义函数,计算3次方
def square(x) :
return x ** 3
list(map(square, [1,2,3,4,5]))
[1, 8, 27, 64, 125]
# 使用 lambda 匿名函数
list(map(lambda x: x ** 3, [1, 2, 3, 4, 5]))
[1, 8, 27, 64, 125]
# 提供了两个列表,对相同位置的列表数据进行相加
list(map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10]))
[3, 7, 11, 15, 19]
list(map(lambda x: x%2==1, [1,3,2,4,1]))
[True, True, False, False, True]
reduce函数
reduce方法,顾名思义就是减少,假设你有一个由数字组成的可迭代对象,并希望将其缩减为单个值。把一个函数作用在一个序列上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算reduce(f,[x1,x2,x3,x4,x5]) = f(f(f(x1,x2),x3),x4)。
语法:reduce(function,sequence[,initial]=>value)
function:函数
iterable:一个或多个序列
from functools import reduce
nums = [6,9,4,2,4,10,5,9,6,9]
print(nums)
[6, 9, 4, 2, 4, 10, 5, 9, 6, 9]
print(sum(nums))
64
print(reduce(lambda val,x: val+x,nums))
64
# 累计减法
reduce(lambda x,y:x-y,[1,2,3,4])
-8
#累计乘法
def multi(x,y):
return x*y
reduce(multi,[1,2,3,4])
24
reduce(lambda x,y:x*y,[1,2,3,4])
24
filter函数
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
过滤器,构造一个序列,等价于:[ item for item in iterables if function(item)]
在函数中设定过滤条件,逐一循环迭代器中的元素,将返回值为True时的元素留下,形成一个filter类型数据。
语法:filter(function, iterable)
function:判断函数
iterable :可迭代对象
fil = filter(lambda x: x>10,[1,11,2,45,7,6,13])
fil
<filter at 0x28b693b28c8> # 可迭代对象,不能直接查看
list(fil)
[11, 45, 13]
def isodd(num):
if num % 2 == 0:
return True
else:
return False
list(filter(isodd,range(1,13)))
[2, 4, 6, 8, 10, 12]
12 说说什么是装饰器,举个例子具体说明装饰器的作用
装饰器是一种函数,它接受一个函数作为参数,并返回一个新的函数或修改原来的函数;允许你动态地修改函数或类的行为。
装饰器的语法使用 @decorator_name 来应用在函数或方法上。
Python 还提供了一些内置的装饰器,比如 @staticmethod 和 @classmethod,用于定义静态方法和类方法。
def decorator_function(original_function):
def wrapper(*args, **kwargs):
# 这里是在调用原始函数前添加的新功能
before_call_code()
result = original_function(*args, **kwargs)
# 这里是在调用原始函数后添加的新功能
after_call_code()
return result
return wrapper
# 使用装饰器
@decorator_function
def target_function(arg1, arg2):
pass # 原始函数的实现
13 说说什么是断言?有什么用?
assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。
断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况,例如我们的代码只能在 Linux 系统下运行,可以先判断当前系统是否符合条件。
14 说如何读写文件,读文件写文件写二进制文件应该使用什么方法?
读写文件:Python 提供一个内置函数 open() 来打开文件;
'r':只读模式。
'w':只写模式。
'a':追加模式。
'x':独占模式,只有在文件不存在时才创建。
'b':二进制模式。
't':文本模式。
如:
# 打开文件
file = open("example.txt", "r")
# 读取文件内容
content = file.read()
# 打印文件内容
print(content)
# 关闭文件
file.close()
使用 open() 函数打开一个名为 "example.txt" 的文件来读取文件内容。然后使用 read() 方法一次性读取整个文件。最后,使用 close() 方法关闭文件。
要写一个文本文件,我们可以将打开模式设置为 w (写模式)或 a (追加模式)。
# 打开文件,可以使用 mode="a" 将内容追加到文件末尾
file = open("example.txt", "w")
# 写入文件内容
file.write("This is some example text#")
# 关闭文件
file.close()
读写二进制文件:
'b':二进制模式。
'r+b':二进制模式,允许读取和写入文件。
读取:
# 打开二进制文件,模式为只读
file = open("example.dat", "rb")
# 读取文件内容
content = file.read()
# 关闭文件
file.close()
写入:
# 打开二进制文件,模式为只写
file = open("example.dat", "wb")
# 写入文件内容
file.write(b"Example contents for binary file.")
# 关闭文件
file.close()
15 如何读取大文件
常见的就是分块读取,按照固定大小读取,用read就可以
16 写出冒泡排序
冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
def bubbleSort(arr):
n = len(arr)
# 遍历所有数组元素
for i in range(n):
# Last i elements are already in place
for j in range(0, n-i-1):
if arr[j] > arr[j+1] :
arr[j], arr[j+1] = arr[j+1], arr[j]
arr = [64, 34, 25, 12, 22, 11, 90]
bubbleSort(arr)
print ("排序后的数组:")
for i in range(len(arr)):
print ("%d" %arr[i]),
结果为
排序后的数组:
11
12
22
25
34
64
90
17 如何实现人工报错?
raise [Exception [, args [, traceback]]]
如:
def functionName( level ):
if level < 1:
raise Exception("Invalid level!", level)
# 触发异常后,后面的代码就不会再执行
18 mysql 三表联查如何操作?写出伪代码?
SELECT 别名1.字段1....别名1.字段n,别名2.字段1....别名2.字段n,别名3.字段1....别名3.字段n
FROM 表1 别名1
连接关键字(INNER/LEFT/RIGHT) JOIN 表2 别名2
ON 别名1.公共字段=别名2.公共字段
连接关键字(INNER/LEFT/RIGHT) JOIN 表3 别名3
ON 新表.公共字段=别名3.公共字段
如:
SELECT e.ename '员工姓名',d.dname '部门名称',e.sal '薪水',s.grade '薪水等级'
FROM emp e
LEFT JOIN dept d
ON e.deptno=d.deptno
LEFT JOIN salgrade s
ON e.sal BETWEEN s.losal AND s.hisal;
/*获取员工姓名、员工的部门名称、员工薪水、员工薪水等级*/
19 如果现在已经知道有三个班级 A班 B班 C班,如何知道每个班级第二名的成绩?
对每个班级所有学生的成绩列表排序,选择每个班级排序后的第二个学生的成绩
20 说说python中的序列化反序列化以及具体作用?
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
反序列化 (Deserialization)是将有序的二进制序列转换成某种对象(字典,列表等)的过程。
Js 基础篇:
21 如何定义一个匿名函数?(尽可能多的写出方法)
- 使用函数表达式定义一个匿名函数,并将其赋值给变量 anonymousFunc。在这种情况下,函数只能通过变量名 anonymousFunc 调用;
- 使用箭头函数表达式定义一个匿名函数,并将其赋值给变量 anonymousFunc2。与第一种方式类似,函数只能通过变量名 anonymousFunc2 调用;
- 定义一个立即执行的匿名函数,这种函数也称为自执行函数。它使用一个函数表达式创建一个匿名函数,并立即调用该函数。在这种情况下,函数体中的代码只会执行一次。
22 说说js中定义变量的三种方法?他们的区别?
Var、const、let
1、var声明的变量属于函数作用域,而let和const声明的变量属于块级作用域;(js作用域在上篇文章)
2、var声明的变量存在变量提升,而let和const没有
3、var声明的变量可以重复声明,而在同一块级作用域,let变量不能重新声明,const常量不能修改
(对象的属性和方法,数组的内容可以修改)
23 说说构造函数,原型对象 实例对象都是什么意思?他们三个之间有什么联系?
构造函数:用来在创建对象时初始化对象。特点:构造函数名一般为大写字母开头;与new运算符一起使用来实例化对象。
原型对象:构造函数在创建的过程中,系统自动创建出来与构造函数相关联的一个空的对象。
可以由构造函数.prototype来访问到。
实例对象:通过构造函数使用 new 关键字创建的对象。
联系:
构造函数是根据具体的事物抽象出来的抽象模板
实例对象是根据抽象的构造函数模板得到的具体实例对象
实例对象由构造函数而来,一个构造函数可以生成很多具体的实例对象,而每个实例对象都是独一无二的;
每个构造函数都有一个属性prototype,这个属性就指向他的原型对象,
每个对象都有一个constructor属性,该属性指向创建该实例的构造函数(null没有自己的原型对象。 )
每个实例对象都有一个__proto__ 属性,这个属性指向原型对象。
24 说说js中一个函数的形参是否要与实参对应?如果一个函数的形参为2个,实际传递了
一个实参会发生什么情况?
在js中函数的形参不一定要与实参相互一一对应,如果形参为两个,但是在实际运用的过程中只是传了一个参数的话,那么传递的参数将会传到第一个参数中去,第二个参数将不会定义(undefined)。
25 如何看待数组?数组和对象的用法是否完全一样?
在js中数组可以看做一种特殊的对象,与普通对象的功能相似,但是可以用来存储一些对象存不了的一些值,所以存储性能要比普通对象要好。
在用法上,数组主要是通过索引存取值,对象是通过键值对;在使用场景上,在面对有序的情况下用到数组,在无序的情况下主要用到对象;
26 在js中如何查看数据类型?
tpyeof运算符
27 如何设置一个对象的具体属性?比如是否可修改是否可配置是否可遍历?
Object.defineProperty为对象添加属性
28 如何将一个对象设置为另一个对象的原型对象?
Object.setPrototypeOf
29 如何遍历一个对象上所有的属性?
Object.getOwnPropertyNames
30 如果遍历一个对象上的可遍历属性?
Object.keys 方法在 Js 中用于获取一个对象自身的所有可枚举属性的键名,并以数组的形式返回
31 如何判断一个数组中元素的个数?
通过.length方法
32 说说this相关的用法,在不同的情况下this代表了什么意思?结合代码说明?
this在js中的应用非常广泛,它的返回值总是当前属性或者方法的对象。
在构造函数中的this指的是实例对象
var Obj = function (m) {
this.m = m;
};
上面代码定义了一个构造函数`Obj`。由于`this`指向实例对象,所以在构造函数内部定义`this.m,就相当于定义实例对象有一个`m`属性。
对象
如果对象的方法里面包含`this`,`this`的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变`this`的指向。
var obj ={
foo: function () {
console.log(this);
}
};
obj.foo() // obj
上面代码中,`obj.foo`方法执行时,它内部的`this`指向`obj`。
如果`this`所在的方法不在对象的第一层,这时`this`只是指向当前一层的对象,而不会继承更上面的层。
var a = {
p: 'Hello',
b: {
m: function() {
console.log(this.p);
}
}
};
a.b.m() // undefined等同于 b.m()
上面代码中,`a.b.m`方法在`a`对象的第二层,该方法内部的`this`不是指向`a`,而是指向`a.b`,因为实际执行的是下面的代码。
全局环境
全局环境使用`this`,它指的就是顶层对象`window`。
this === window // true
function f() {
console.log(this === window);
}
f() // true
函数的内部还是外部,只要是在全局环境下,`this`指的就是顶层对象`window
33 说说什么是base64?关于base64的俩个函数分别是什么?
Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符。3个字节有24个比特,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。它可用来作为电子邮件的传输编码。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。
Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据,包括MIME的电子邮件及XML的一些复杂数据。
atob()将base64转换为字符串格式,btoa()将字符串转换为base64格式。
34 说说什么是xhr断点,如何打xhr断点?
xhr断点就是在浏览器发送XHR请求时,允许暂定js代码的执行,从而获得请求的相关信息。
打xhr断点需要在浏览器界面按下F12进入开发者工具,进入source界面,找到想要打断点的js代码然后打断点就可以了
35 ‘ds23gl23s234ja’ 用什么方法可以提取出其中的数字?
parselnt函数
36 说说什么是自执行函数,一遍的表现形式是什么?
根据js的语法要求,圆括号()跟在函数名之后,表示调用该函数,有时我们在定义完函数之火,需要立刻调用函数,但是此时不能再函数的定义之后加上()。
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();
37 说说eval的作用是什么?这种东西如何对我们进行反爬,我们要如何处理?
作用:解析和执行传入的字符串形式的Js代码;
通过在Node.js环境下去重写eval函数去记录捕获传入eval函数的字符串内容。
38 如果一个函数没有形参,但是调用它的时候有确实传递了很多参数,那么如何接受这些参数并使用?
可以使用*args和**kwargs来接受这些参数。这两个特殊的参数可以接受任意数量的位置参数和关键字参数。
39 如果一个函数返回多个值,那么实际会产生什么效果?比如下面的伪代码
Function hello(){
Return hh = 12+5,gg.t = hh,gg
}
hh变量就接收了hello函数返回的第一个值,gg变量接收了第二个值
40 我们已知的作用域有哪些?举例说明
(1)局部作用域
变量在函数内声明,变量为局部变量,具有局部作用域。
局部变量:只能在函数内部访问。
// 此处不能调用 carName 变量
function myFunction() {
var carName = "Volvo";
// 函数内可调用 carName 变量
}
(2)全局作用域
变量在函数外定义,即为全局变量。
var carName = " Volvo";
// 此处可调用 carName 变量
function myFunction() {
// 函数内可调用 carName 变量
}
爬虫通用篇:
41 如果我们请求接口返回的数据是json形式但是不是标准的json,我们应该啊如何处理?
在JavaScript中使用JSON.parse(),解析失败时捕获错误
42 header头信息中有哪些常用重要的参数,分别代表什么意思?
GET:请求指定的资源。
POST:向指定的资源提交数据,以便根据所提供的数据创建/更新资源。POST请求不是幂等的,每次执行相同的POST请求可能会产生不同的结果。
PUT:用于完整地更新指定的资源。由于PUT请求是幂等的,多次执行相同的PUT请求应该产生相同的结果。
DELETE:用于删除指定的资源。
HEAD:与GET方法类似,只是服务器在响应中只返回HTTP头部,而不返回实际的数据。这用于检查资源的元数据。
OPTIONS:用于获取指定的资源所支持的通信选项。这可以用于CORS(跨源资源共享)检查。
PATCH:用于对资源进行部分更新。
Host:指定请求的主机名和/或端口号。这是必需的,因为HTTP是一个基于TCP/IP的协议,没有主机名和端口号,服务器无法知道请求来自哪里。
User-Agent:提供了关于发送请求的应用程序或用户代理的信息。这可以包括浏览器的名称和版本、操作系统等信息。
Accept:指定客户端接受哪些类型的数据。例如,可以指定接受HTML、JSON、XML等格式的数据。
Content-Type:指定在POST或PUT请求中发送的数据的类型。例如,如果发送的是JSON数据,那么此头部字段应该设置为application/json。
Content-Length:指定POST或PUT请求中发送的数据的长度。
Cookie:包含由服务器发送的cookie信息,这些信息将在后续的请求中自动包含,以便服务器识别用户或保存状态信息。
Authorization:用于向服务器提供身份验证信息,例如Bearer token或Basic authentication。
Referer:指定原始URL,即从哪个URL页面跳转到了当前页面。
43什么是线程进程协成?它们之间有什么区别?你更提倡用哪种?
进程:是系统进行资源分配和调度的基本单位,是操作系统结构的基础
线程:是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位
协程:是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程;完全是由程序所控制
区别:
进程是操作系统进行资源分配和管理的基本单位,拥有独立的内存空间和资源,线程是进程中的执行流程,共享进程的内存和资源,协程是由应用程序控制的轻量级执行单元,共享线程的上下文和资源。
线程和进程都是同步机制,需要内核调度,协程是异步机制,由程序员手动控制切换,减少上下文切换开销,提高执行效率。
协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态,适合协同性强且任务间相对独立的场景。
一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样程序中则能使用多核CPU,适合多任务并行处理,尤其在I/O密集型场景下效率更高。
进程:密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。
线程:密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。
协程:当程序中存在大量不需要CPU的操作时(IO),适用于协程;多线程请求返回是无序的,那个线程有数据返回就处理那个线程,而协程返回的数据是有序的。
44 我们抓取网页数据的步骤是什么(比如,源码没有数据怎么查看,如果还是没有有哪些可能性,怎么处理呢)
(1)发送请求(2)解析数据(3)提取数据
45 如何不考虑追溯源码,如何判断一段加密代码到底是什么加密?
- md5:输出字节总是16字节
- Base64编码只有64个字符(英文大小写、数字和+、/)以及用作后缀等号
46画出scrapy 框架的流程图并写出运行流程?
(1)首先 先从Spider发送一个请求,初始化了一个Resquest对象,并且设置了回调函数(也就是创建Scrapy项目后的def parse(self, response)),把请求传给引擎。
(2)引擎收到请求后,并不会把请求马上发送出去,而是传给了调度器。调度器接受到引擎发过来的Requser请求后,把Request对象按照一定的排序算法存储到它里面的一个队列当中。
(3) 接下来引擎会不断的从调度器当中取出已经处理好的Request
(4) 当引擎拿到Request后,再把这个请求扔给下载器。
(5)下载器拿到请求后,按照下载中间件中的设置去互联网上面下载request请求。下载好数据后再把数据传送给引擎。
(6)引擎拿到数据之后,再把这个数据(也就是response)通过爬虫中间件返回给Spider。并把response作为参数传递给第一步设置好的回调函数。
(7)爬虫拿到数据之后(这个爬虫是由我们自己写),经过分析,把不需要的数据剔除掉,把需要的数据提取出来(使用xpath、正则)。接下来再把提取出来的数据扔给引擎。
(8) 引擎拿到数据之后,把这个数据扔给实体管道(如果这时还有其他的Requset,会重复3-8这个过程,直到获取完所有的自己想要的信息)。
47 说说scrapy downloadmiddleware中包含哪些方法,都起到什么作用?
process_request(request, spider):
Request被Scrapy引擎调度给Downloader之前,process_request()方法就会被调用,也就是在Request从队列里调度出来到Downloader下载执行之前,我们都可以用process_request()方法对Request进行处理。方法的返回值必须为None、Response对象、Request对象之一,或者抛出IgnoreRequest异常。
process_request()方法的参数有如下两个:
request,是Request对象,即被处理的Request。
spider,是Spdier对象,即此Request对应的Spider。
process_response(request, response, spider):
Downloader执行Request下载之后,会得到对应的Response。Scrapy引擎便会将Response发送给Spider进行解析。在发送之前,我们都可以用process_response()方法来对Response进行处理。方法的返回值必须为Request对象、Response对象之一,或者抛出IgnoreRequest异常。
process_response()方法的参数有如下三个:
request,是Request对象,即此Response对应的Request。
response,是Response对象,即此被处理的Response。
spider,是Spider对象,即此Response对应的Spider
process_excepti on(request, excepti on, spider) :
当Downloader或process_request()方法抛出异常时,例如抛出IgnoreRequest异常,process_exception()方法就会被调用。方法的返回值必须为None、Response对象、Request对象之一。
process_exception()方法的参数有如下三个:
request,是Request对象,即产生异常的Request。
exception,是Exception对象,即抛出的异常。
spdier,是Spider对象,即Request对应的Spider
48 说说scrapy框架的去重机制是怎么样的?
Scrapy 的去重就需要利用共享的集合,使用的就是 Redis 中的集合数据结构
鉴别重复的方式使用指纹,指纹是依靠 request_fingerprint 方法来获取的。获取指纹 之后就直接向集合添加指纹,如果添加成功,说明这个指纹原本不存在于集合中,返回值 1。
代码中最后的返回结果是判定添加结果是否为 0,如果刚才的返回值为 1,那这个判定结果就是 False,也就是不重复,否则判定为重复
49 说说什么是布隆过滤器,大体的实现思路是什么?有什么优缺点?
Bloom Filter 使用位数组表示一个待检测集合,并可以快速地通过概率算法判断一个元素是否
存在于这个集合中。利用这个算法我们可以实现去重效果。
(1)首先需要k个hash函数,每个函数可以把key散列成为1个整数
(2)初始化时,需要一个长度为n比特的数组,每个比特位初始化为0
(3) 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1
(5)判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。
优点:不需要存储key,节省空间
缺点:(1)算法判断key在集合中时,有一定的概率key其实不在集合中
(2)无法删除
50 说说什么是字体加密,遇到字体加密的大体处理思路是什么?
字体加密:通过 CSS 自定义字体库的方式,使得页面源码中的数据与显示出来的数据不同的一种加密方式。
处理思路:
先找到字体文件的位置,查看源码大概就是xxx.tff/eot/woff这样的文件
重复上面那个操作,将两个字体文件保存下来
用上面的软件或者网址打开,并且通过 Python fontTools 将 tff 文件解析为 xml 文件
根据字体文件解析出来的 xml 文件与类似上面的字体界面找出相同内容的映射规律(重点)
51 说说scrapy_redis是什么东西,有什么作用?
scrapy_redis是一个基于Redis的Scrapy组件,用于scrapy项目的分布式部署和开发。
作用:
(1)爬取队列的实现,使用了 Redis 的列表或有序集合来维护;
(2)去重的实现,使用了 Redis 的集合来保存 Request 的指纹以提供重复过滤;
(3)中断后重新爬取的实现,中断后 Redis 的队列没有清空,再次启动时调度器的next_request 会从队列中取到下一个 Request,继续爬取。
52 简单说说如果一个接口存在无法解决的加密,我们应该如何进行处理?
在模拟虚拟环境下,通过逆向分析加密算法尝试解密;若是接口的返回数据被加密的情况下可以通过模拟请求的方法来解密。
53 如果在nodejs环境中使用MD5加密对一个数据加密?举例说明
MD5是一种常用的哈希算法,主要用于对一些重要数据进行“签名”,当然这些数据可以是任意的。最终得到的“签名”通常都是一个16或32位的十六进制的字符串。
var md5 =require("md5");
//设置加密字符串
var passWord="if(1==1){console.log('haha')}";
console.log(md5(passWord));
54 nodejs 环境中是否有window ,如果没有它的公共变量是什么?
没有
Node.js提供了一个名为global的全局对象,它可以用于模拟window对象中的某些属性和方法
55 什么是ob混淆,如何能够判断一段代码是否进行过ob混淆,它有什么具体特征?
ob混淆是JavaScript混淆中的一种方法,是指将JavaScript代码中的变量名、函数名、字符串等替换为无意义的字符串,从而增加代码的保护性和防止代码的逆向分析。此外,它还可以在代码中添加死代码、无用的函数等,增加代码的复杂度和难以理解性,从而增加代码的保密性。
特征:(1) 大数组 + 移位自执行函数 + 解密字符串函数(2)定义的Object,其key和value很有规律(3)while + switch 组合(4)一些干扰调试的垃圾代码
56 如果要实现带验证码的模拟登录,有哪些方法思路?
(1)将验证码下载下来自己进行识别然后手动输入
(2)第三方识别平台——如超级鹰等
57 说说代理IP池实现 的思路,分成哪几个部分,每个部分主要负责实现什么功能,整个架构如何运行的。
(1)准备工作:安装必要的库,aiohttp,requests,redis,pyquery,flask等;
(2)存储模块:(操作数据库的方法,Redis)存储获取到的代理(有序集合为redis的一种数据形式,第一可以自动去重,第二自动排序,把序作为分数,序可以重复);
获取模块:往代理ip池补充代理;
检测模块:检测代理ip能不能使用不能用pass;
接口模块:把收集到的代理展示出来(避免直接访问数据库,保护数据库安全)
(3)调用以上三个模块:
58 选择代理IP的标准有哪些?
连接速度;安全性;请求间隔;价格;稳定性等
59 如何用ddddocr 识别一段汉字,举例说明
import ddddocr
ocr = ddddocr.DdddOcr()
image = open("example.jpg", "rb").read()
result = ocr.classification(image)
print(result)
60 如果一个网站是瀑布流的形式,没有最大页数,那么我们翻到超过最大页之后有哪些可能性?如何处理?
重复加载之前的内容或者404