优秀的编程知识分享平台

网站首页 > 技术文章 正文

面试妥了!2020 爬虫面试题目合集(爬虫面试经历)

nanyue 2024-10-24 11:44:54 技术文章 3 ℃

作者 | 不愿透露姓名の网友

出品 | CSDN博客

介绍爬虫

1.爬虫:自动的抓取互联网上信息的脚本文件。

2.爬虫可以解决的问题:

(1)解决冷启动问题

(2)搜索引擎的根基:做搜索引擎少不了爬虫

(3)建立知识图谱,帮助建立机器学习知识图谱

(4)可以制作各种商品的比价软件,趋势分析。

3.爬虫分类

(1)通用爬虫:搜索引擎的主要组成,作用就是将互联网的上页面整体的爬取下来之后,保存到本地。

(2)聚焦爬虫:聚焦爬虫在实施网页抓取时会对内容进行处理筛选,尽量保证只抓取与需求相关的网页信息。

通用爬虫和聚焦爬虫的区别:聚焦爬虫在实施网页抓取时会对内容进行处理筛选,尽量保证只抓取与需求相关的网页信息。

4.爬虫遵循的协议:robot协议

定义:网络爬虫排除标准。

作用:告诉搜索引擎哪里可以爬,哪里不可以爬。

5.通用爬虫工作流程

(1)抓取网页:通过搜索引擎将待爬取的url加入到通用爬虫的url队列中,进行网页内容的爬取

(2)数据存储:将爬取下来的网页保存到本地,这个过程会有一定的去重操作,如果某个网页的内 容大部分内容都会重复,搜索引擎可能不会保存。

(3)预处理:提取文字,中文分词,消除噪音(比如版权声明文字,导航条,广告等)。

(4)设置网站排名,为用户提供服务。

6.一些反爬及其应对措施

( 1)通过user-agent来判断是否是爬虫。

解决方案:可以通过伪装请求头中的user-agent来解决。若user-agent被检测到,可以找大量的user-agent,放入列表,然后进行更换

(2)将IP进行封杀。

解决方案:可以通过代理来伪装IP。

(3)通过访问频率来判断是否是一个爬虫。

解决方案:可以通过设置请求间隔,和爬取间隔。

(4)当一定时间内的总请求数超过上限,弹出验证码。

解决方案:对于简单的验证码图片可以使用tesseract来处理,对于复杂的可以去打码平台。

(5)通过JS来获取页面数据。

解决方案:可以使用selenium+phantomjs来加载JS获取数据。

介绍搜索引擎

1.搜索引擎的主要组成

通用爬虫:就是将互联网的上页面整体的爬取下来之后,保存到本地。

通用爬虫要想爬取网页,需要网站的url.但是搜索引擎是可以搜索所有网页的。那么通用爬虫url就要涉及到所有网页,这个‘所有’是如何做到的:

  • 新网站向搜索引擎主动提交网址;

  • 在其他网站上设置新网站外链;

  • 搜索引擎和DNS解析服务商(如DNSPod等)合作,新网站域名将被迅速抓取。

2.搜索引擎的工作流程(通用爬虫的工作流程)

(1)抓取网页:通过搜索引擎将待爬取的URL加入到通用爬虫的URL队列中,进行网页内容的爬取。

(2)数据存储:将爬取下来的网页保存到本地,这个过程会有一定的去重操作,如果某个网页的内 容大部分内容都会重复,搜索引擎可能不会保存。

(3)预处理:提取文字,中文分词,消除噪音(比如版权声明文字,导航条,广告等)。

(4)设置网站排名,为用户提供服务。

3.搜索引擎的局限性

(1)搜索引擎只能爬取原网页,但是页面90%内容都是无用的。

(2)搜索引擎不能满足不同行业,不同人的特定需求。

(3)通用搜索引擎只能爬取文字信息,不能对音频、图片等进行爬取。

(4)只能基于关键字查询,无法基于语义查询。

介绍HTTP

网络七层协议:

1.HTTP协议特点

  • HTTP协议是超文本传输协议;

  • HTTP协议是一个应用层协议;

  • 无连接:每次请求都是独立的;

  • 无状态,表示客户端每次请求都不能记录请求状态,就是两条请求直接不可通信。

2.HTTP工作过程

  • 地址进行DNS解析,将URL解析出对应的内容

  • 封装HTTP请求数据包

  • 封装成TCP包,建立TCP连接(TCP的三次握手)

  • 客户端发送请求

  • 服务器接收请求,发送响应

  • 客户端接收到响应,进行页面渲染

  • 服务器关闭TCP连接(TCP的四次挥手)

3.HTTP协议和HTTPS协议的区别

  • HTTP协议是使用明文数据传输的网络协议,明文传输会让用户存在一个非常大的安全隐患。端口80

  • HTTPS协议可以理解为HTTP协议的安全升级版,就是在HTTP的基础上增加了数据加密。端口443

  • HTTPS协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议要比HTTP协议安全。

4.HTTP通信

HTTP通信由两部分组成:客户端请求消息与服务器响应消息。

5.关于响应常见的响应码

6.客户端请求(Get和Post区别)

(1)组成:请求行、请求头部、空行、请求数据四个部分组成

(2)请求方法Get/Post

(3)Get和Post的区别

GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

(4)常见的请求头

User-Agent:客户端请求标识。

Accept:传输文件类型。

Referer :请求来源。

cookie (cookie):在做登录的时候需要封装这个头。

Content-Type (POST数据类型)

7.服务器响应

(1)组成:状态行,响应头,空行,响应正文。

(2)常见的响应头

Content-Type:text/html;资源文件的类型,还有字符编码

Content-Length:响应长度

Content-Size响应大小

Content-Encoding告诉客户端,服务端发送的资源是采用什么编码的。

Connection:keep-alive这个字段作为回应客户端的Connection:keep-alive,告诉客户端服务器的tcp连接也是一个长连接,客户端可以继续使用这个TCP连接发送HTTP请求

url

统一资源定位符:

基本格式:scheme://host[:port#]/path/…/?query-string

协议://服务器ip地址:端口号/资源路径/?key1=参数1&key2=参数2

scheme:协议(例如:HTTP、HTTPS、FTP)

host/IP:服务器的IP地址或者域名

port:服务器的端口(如果是走协议默认端口,缺省端口80),用来从互联网进入电脑

path:访问资源的路径,就是为了在电脑中找到对应的资源路径

query-string:参数,发送给http服务器的数据

anchor:锚(跳转到网页的指定锚点位置)

Q:当我们在浏览器输入一个URL,为什么可以加载出一个页面?为什么抓包的过程中请求一个URL,出现很多的资源请求?

当我们在浏览器输入一个URL,客户端会发送这个URL对应的一个请求到服务器获取内容。服务器收到这个请求,解析出对应内容,之后将内容封装到响应里发送到客户端

当客户端拿到这个HTML页面,会查看这个页面中是否有CSS、JS、image等URL,如果有,在分别进行请求,获取到这些资源。

客户端会通过HTML的语法,将获取到的所有内容完美的显示出来。

Cookie和Session

产生原因:由于HTTP是一个无状态的协议,每次请求如果需要之前的一些信息,无法记录,因此为了解决这个问题,产生了一种记录状态技术,Cookie和Session。

Cookie指某些网站为了辨别用户身份,进行会话跟踪而存储在用户本地终端上的数据,种类有会话Cookie和持久Cookie。

(1)会话Cookie指存在浏览器内存的Cookie,当浏览器关闭,会话Cookie会失效;

(2)持久Cookie是保存在硬盘上的Cookie。

Session用来存储特定的用户会话所需的属性及其配置信息。

Cookie是在客户端记录状态,Session是在服务端记录状态。

联系:当客户端发送一个Cookie,服务器会从这个Cookie中找到sessionID,再查找出相应的Session信息返回给客户端,来进行用户页面的流转。如果通过sessionID来查找Session的时候,发现没有Session(一般第一次登陆或者清空了浏览器),那么就会创建一个Session。

hashlib密码加密

def get_hex(value):

md5_ = hashlib.md5

md5_.update(value.encode('utf-8'))

return md5_.hexdigest

关于response.text乱码问题

response的常用属性:

1.获取字符串类型的响应正文:response.text

2.获取bytes类型的响应正文:response.content

3.响应正文字符串编码:response.encoding

4.状态码:response.status_code

5.响应头:response.headers

response.text乱码问题:

#方法一:转换成utf-8格式

response.encoding='utf-8'

print(response.text)

#方法二:解码为utf-8 :

with open('index.html','w',encoding='utf-8') as fp: fp.write(response.content.decode('utf-8'))


代理

代理的作用:

1、突破自身IP 访问限制, 访问一些平时不能访问的站点。

2、访问一些单位或团体内部资源:比如使用教育网内地址段免费代理服务器, 就可以用于对教育网开放的各类FTP 下载上传, 以及各类资料查询共享等服务。

3、提高访问速度:通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时, 同时也将其保存到缓冲区中,当其他用户再访问相同的信息时,则直接由缓冲区中取屮信息传给用户,以提高访问速度。

4、隐藏真实IP :上网者也可以通过这种方法隐藏自己的IP , 免受攻击。对于爬虫来说, 我们用代理就是为了隐藏自身IP , 防止自身的被封锁。

代理根据匿名程度的分类:

JSON数据

数据的分类:

JSON的本质:是一个字符串,JSON是对JS对象的字符串表达式,它使用文本形式表示一个JS对象的信息。

JSON使用:

(1)json.dumps(Python的list或者dict),将Python的list或者dict返回为一个JSON字符串;

(2)json.loads(json字符串),将JSON字符串返回为Python的list或者dict;

(3)json.dump(list/dict,fp),将Python的list或者dict转为一个JSON字符串,保存到文件中;

(4)json.load(fp) ,从JSON文件中读出JSON数据,并转换为Python的list或者dict。

正则表达式

1、贪婪和非贪婪

(1)正则默认是贪婪模式,所以数量控制符默认是取最大值,也是贪婪。例如*

(2)非贪婪是用?来控制,尽量匹配最少的次数,0次或一次。

2、Python使用格式:

pattern=re.compile('正则表达式') 

print(pattern.match(字符串,start,end))#默认从头开始匹配,只匹配一次,返回一个match对象

print(pattern.search(字符串,start,end))#从任意位置开始匹配,只匹配一次,返回一个match对象

print(pattern.findall(字符串,start,end))#全文多次匹配,将匹配到的结果放到一个list返回给我们

print(pattern.finditer(字符串,start,end))#全文多次匹配,将匹配到的结果放到一个match对象的迭代器返回

3、常见的正则题目

XML

XML的基本知识:

1.定义:XML称为可拓展性标记语言,类似 HTML;

2.特点:XML具有自描述特性,是一种半结构化数据;

3.作用:XML的设计宗旨是传输数据,而非显示数据。可以作为配置文件使用来记录一些重要信息;

4.XML的标签需要我们自行定义。

HTML和XML 区别:

语法方面:

  • 在HTML中不区分大小写,在XML中严格区分大小写

  • 在HTML中,在某些情况可以省略闭合标签。在XML中,绝对不能省略任何标记。

  • 在XML中,单标签结尾处需要加’/’。

  • XML文档中,空白部分不会被解析器自动删除,但是HTML是过滤掉空格的

  • 在XML中,属性值必须封装在引号中。在HTML中,引号可用可不用。

  • 在HTML中属性名可以不带属性值,XML必须带属性值而且不能为空。

标记不同:

HTML使用固有的标记,XML没有固有标记。

作用不同:

XML主要用来传输数据,HTML主要用来显示数据。

Selenium+PhantomJS

Selenium:Web自动测试工具。

PantomJS:是一个无界面的浏览器,所以他可以运行JS代码,帮我们拿到页面数据。

特点:功能强大,几乎可以对付任何网页,但会导致代码效率低。

Selenium 是一个 Web 的自动化测试工具,可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。Selenium 自己不带浏览器,不支持浏览器的功能,它需要与第三方浏览器结合在一起才能使用。但是我们有时候需要让它内嵌在代码中运行, 所以我们可以用一个PhantomJS 的工具代替真实的浏览器。Selenium 库里有个叫 WebDriver 的 API。WebDriver 有点儿像可以加载网站的浏览器,但是它也可以查找页面元素,与页面上的元 素进行交互 (发送文本、点击等),以及执行其他动作来运行网络爬虫。

PhantomJS 是一个基于 Webkit 的“无界面”浏览器,它会把网站加载到内存并执行页面上的 JavaScript,因为不会展示图形界面,所以运行起来比完整的浏览器要高效。相比传统的 Chrome或 Firefox 浏览器等,资源消耗会更少。如果我们把 Selenium 和 PhantomJS 结合在一起,就可以运行一个非常强大的网络爬虫了,这个爬虫可以处理 JavaScript、Cookie、headers,以及任 何我们真实用户需要做的事情。主程序退出后,selenium 不保证 phantomJS 也成功退出,最好手动关闭 phantomJS 进程。(有可能会导致多个 phantomJS 进程运行,占用内存)。WebDriverWait 虽然可能会减少延时,但是目前存在 bug(各种报错),这种 情况可以采用 sleep。phantomJS 爬数据比较慢,可以选择多线程。如果运行的时候发现有的可 以运行,有的不能,可以尝试将 phantomJS 改成 Chrome。

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

1.直接使用已知的Cookie访问

先用浏览器登录,获取浏览器里的cookie字符串,然后封装至请求头。

2.模拟登录后用session保持登录状态

使用session模拟登陆后,就会自动存储一个cookie次从而保持住登录状态。

3.使用Selenium+PhantomJS访问

Selenium库提供了find_element(s)_by_xxx的方法来找到网页中的输入框、按钮等元素。其中xxx可以是id、name、tag_name(标签名)、class_name(class),也可以是xpath(xpath表达式)等等。当然还是要具体分析网页源代码。

线程+进程+多线程

关系:一个程序至少有一个进程,一个进程至少有一个线程。

线程的顺序:

新建:线程创建(t=threading.Thread(target=方法名)或者线程类)

就绪:当启动线程后,线程就进入就绪状态,就绪状态的线程会被放到一个CPU调度队列里面,cpu会负责让其中的线程运行,变为运行状态。

运行状态:CPU调度一个就绪状态的线程,该线程就变为运行状态。

阻塞状态:当运行状态的线程被阻塞变为阻塞状态,阻塞状态的线程就会重新变为就绪状态才能继续运行。

死亡状态:线程执行完毕。

多线程和多进程优缺点

多线程的优点:

  • 程序逻辑和控制方式复杂;

  • 所有线程可以直接共享内存和变量;

  • 线程方式消耗的总资源比进程方式好。

多线程缺点:

  • 每个线程与主程序共用地址空间,受限于2GB地址空间;

  • 线程之间的同步和加锁控制比较麻烦;

  • 一个线程的崩溃可能影响到整个程序的稳定性;

多进程优点:

  • 每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系;

  • 通过增加CPU,就可以容易扩充性能;

  • 每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大 。

多进程缺点:

  • 逻辑控制复杂,需要和主程序交互;

  • 需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算 多进程调度开销比较大。

在实际开发中,选择多线程和多进程应该从具体实际开发来进行选择。最好是多进程和多线程结合。

互斥锁和死锁和GLF锁

1.互斥锁

定义:当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。线程同步能够保证多个线程安全访问“竞争资源”,最简单的同步机制就是引用互斥锁。

由于线程共享进程的内存空间和数据,因此在某个线程要共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进入写入操作,从而保证了多线程情况下数据的正确性。互斥锁体现的就是一个同步的机制,即该线程释放资源就是一个条件,条件完成,才能执行下一步操作。是微观的操作。

锁的好处:

(1)确定了某段代码只能由一个线程从头到尾完整地执行。

(2)全局变量的安全

锁的坏处:

(1)阻止了多线程的并发执行,包含锁的某段代码实际上只能以单线程模块执行,效率降低

(2)由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁的时,可能会造成“死锁”。

2.死锁

(1)同一个线程先后两次调用lock,在第二次调用时,由于锁已经被自己占用,该线程会挂起等待自己释放锁,由于该线程已被挂起而没有机会释放锁,因此 它将一直处于挂起等待状态,变为死锁;

(2)线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都在等待对方释放自己才释放,从而造成两个都永远处于挂起状态,造成死锁。

3.有GLF锁为什么还要有互斥锁

(1)为什么设计GLF锁?

Python 在设计之初就考虑到要在解释器的主循环中,同时只有一个线程在执行,即在任意时刻,只有一个线程在解释器中运行。对Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是GIL锁能保证同一时刻只有一个线程在运行。

(2)宏观微观考虑GLF锁?

GIL是宏观的操作。比如在一个4核的环境下,只有一个核是运行着线程,而其他三个核是空的。GIL是线程锁,针对线程,而不是进程。

MongoDB

MongoDB和传统的关系型数据库区别:

1.传统数据库特点是存储结构化数据,数据以行为单位,每行的数据结构和类型相同。

2.MongoDB存储的是文档,每个文档得结构可以不相同,能够更便捷的获取数据。

3.MongoDB的集合不需要提前创建,可隐式创建,而关系型数据库的表需要提前定义。

4.MongoDB第三方支持丰富。(这是与其他的NoSQL相比,MongoDB也具有的优势)。

5.MongoDB不支持事务操作。

6.MongoDB支持大容量的存储,占用空间过大。

MongoDB和SQL的对比:

游标:

定义:由于MongoDB底层是JS编写,因此可以使用一些JS代码,通俗的说游标不是查询结果,而是查询的返回资源或者接口,就像Python中的生成器,通过这个生成器,可以一次一次的获取每一个资源。

持久化:日志和快照对比

快照和日志两者如何选择?

推荐两种共同使用:

1.如果Redis仅仅是用来做为缓存服务器的话,我们可以不使用任何的持久化。

2.一般情况下我们会将两种持久化的方式都开启。redis优先加载AOF文件来回复数据。RDB的好处是快速。

3.在主从节点中,RDB作为我们的备份数据,只在salve(从节点)上启动,同步时间可以设置的长一点,只留(save 900 1)这条规则就可以了。

4.开启AOF的情况下,主从同步是时候必然会带来IO的性能影响,此时我们可以调大auto-aof-rewrite-min-size的值,比如5GB。来减少IO的频率

5.不开启AOF的情况下,可以节省IO的性能影响,这是主从间通过RDB持久化同步,但如果主从都挂掉,影响较大。

在dump rdb过程中,aof如果停止同步,会不会丢失?

答: 不会,所有的操作缓存在内存的队列里,dump完成后,统一操作。

aof重写是指什么?

答:由于日志保存的是所有操作命令,导致存的日志会过大,而且数据库中有可能数据进行过删除,因此日志中的一些命令就相当于无效,因此日志先会删除,然后内存中的数据会逆化成命令,再重新写入到日志文件中,以解决 aof日志过大的问。

如果rdb文件和aof文件都存在,优先用谁来恢复数据?

答: 在这种情况下,当Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件完整。

恢复时rdb和aof哪个恢复的快?

答: rdb快,因为其是数据的内存映射,直接载入到内存,而aof是命令,需要逐条执行。

Scrapy和Scrapy-Redis

什么是Scrapy?

Scrapy 是一个快速、高层次的基于 Python 的 Web 爬虫构架,它用于抓取Web 站点并从页面中提取结构化的数据。Scrapy 使用了 Twisted异步网络库来处理网络通讯。

Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

Scrapy优缺点

优点:

  • Scrapy 是异步的(Scrapy 框架的异步机制是基于 twisted 异步网络框架处理的,在 settings.py 文件里可以设置具体的并发量数值(默认是并发量 16))

  • 采取可读性更强的xpath代替正则

  • 强大的统计和log系统

  • 同时在不同的url上爬行

  • 支持shell方式,方便独立调试

  • 写middleware,方便写一些统一的过滤器

  • 通过管道的方式存入数据库

缺点:

  • 基于Python的爬虫框架,扩展性比较差

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

Scrapy的组件:

Scrapy和scrapy-redis的区别:

Scrapy是一个Python爬虫框架,爬取效率极高,具有高度定制性,但是不支持分布式。而scrapy-redis一套基于redis数据库、运行在Scrapy框架之上的组件,可以让scrapy支持分布式策略,Slaver端共享Master端Redis数据库里的item队列、请求队列和请求指纹集合

为什么选择Redis数据库,因为Redis支持主从同步,而且数据都是缓存在内存中的,所以基于Redis的分布式爬虫,对请求和数据的高频读取效率非常高。

Redis的优点:

1.数据读取快,因为数据都放在内存上

2.支持事务watch

3.数据持久化,支持快照和日志,方便恢复数据

4.拥有丰富的数据类型:list,string,set,qset,hash

5.支持主从复制,可以进行数据备份

6.丰富的特性:可以作为缓存,消息队列,设置过期时间,到期自动删除

分布式爬虫主要解决什么问题?

IP、带宽、CPU、io

Scrapy如何实现分布式抓取?

可以借助scrapy_redis类库来实现。

在分布式爬取时,会有master机器和slave机器,其中,master为核心服务器,slave为具体的爬虫服务器。

我们在master服务器上搭建一个Redis数据库,并将要抓取的url存放到redis数据库中,所有的slave爬虫服务器在抓取的时候从redis数据库中去链接,由于scrapy_redis自身的队列机制,slave获取的url不会相互冲突,然后抓取的结果最后都存储到数据库中。master的redis数据库中还会将抓取过的url的指纹存储起来,用来去重。相关代码在dupefilter.py文件中的request_seen方法中可以找到。

定时任务:

1.可以设计一个定时的类,使用时间模块(time,datetime),比如爬取时候获取当前时间,再当前时间多久后再爬取

2.使用linux的crontab的计划任务

crontab -l 列出所有的定时任务 看不到配置文件中写的定时任务

crontab -e 新增计划任务 跟上面的区别在于 没有用户名

crontab -r 清空计划任务

分 时 日 月 周 命令

举例使用:

* * * * * 命令 每分每时每天每月每周 执行这个命令

0-59 0-23 1-31 1-12 0-6 0是 周天 1-6 周一到周六

0 2 * * * mysqldump 每天的2点备份数据库

0 2 * * 2 sync 每个周二的2点做数据同步

0 8 15 * * /home/jsgz.py 每个月15号的八点给大家算工资

0 */2 * * * /home/camera.py 每隔2个小时执行一次查看摄像头

0 8,12,18 * * 1-5 kq.py 每周1-5 的 8点 12点 18点 执行打卡

0 8 * * * * spider.sh 每天 8点爬虫


爬虫框架、模块

Python自带:urllib,urllib2。

urllib和urllib2模块都做与请求URL相关的操作,但他们提供不同的功能。

urllib2.:urllib2.urlopen可以接受一个Request对象或者url,(在接受Request对象时候,并以此可以来设置一个URL 的headers),urllib.urlopen只接收一个url。

urllib 有urlencode,urllib2没有,因此总是urllib,urllib2常会一起使用的原因

第 三 方:Requests

Requests是一个HTTP库, 它只是用来,进行请求,对于HTTP请求,他是一个强大的库,下载,解析全部自己处理,灵活性更高,高并发与分布式部署也非常灵活,对于功能可以更好实现.

框 架:Scrapy

Scrapy是封装起来的框架,他包含了下载器,解析器,日志及异常处理,基于多线程, twisted的方式处理,对于固定单个网站的爬取开发,有优势,但是对于多网站爬取 100个网站,并发及分布式处理方面,不够灵活,不便调整与括展。

MyISAM 与 InnoDB

MyISAM 与 InnoDB 两个引擎有什么区别?

InnoDB 支持事务,MyISAM 不支持,这一点是非常之重要。事务是一种高级的处理方式,如果在一些列增删改中只要哪个出错还可以回滚还原,而 MyISAM就不可以了。

MyISAM 适合查询以及插入为主的应用,InnoDB 适合频繁修改以及涉及到安全性较高的应用。

InnoDB 支持外键,MyISAM 不支持。

MyISAM 是默认引擎,InnoDB 需要指定。

InnoDB 不支持 FULLTEXT 类型的索引。

InnoDB 中不保存表的行数,如 select count( * ) from table 时,InnoDB;需要扫描一遍整个表来计算有多少行,但是 MyISAM 只要简单的读出保存好的行数即 可。注意的是,当 count( * )语句包含 where 条件时 MyISAM 也需要扫描整个表。

对于自增长的字段,InnoDB 中必须包含只有该字段的索引,但是在 MyISAM表中可以和其他字段一起建立联合索引。

清空整个表时,InnoDB 是一行一行的删除,效率非常慢。MyISAM 则会重建表。

写爬虫是用多进程好?还是多线程好? 为什么?

IO密集型情况使用多线程;

计算密集型情况下使用多进程;

IO 密集型代码(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有 IO 操作会进行 IO 等待,造成不必要的时间浪费,而开启多线程能在线程 A 等待时,自动切换到线程 B,可以不浪费 CPU 的资源,从而能提升程序执行效率)。在实际的数据采集过程中,既考虑网速和响应的问题,也需要考虑自身 机器的硬件情况,来设置多进程或多线程。

数据库的优化

为什么要优化?

一个应用吞吐量瓶颈往往出现在数据库的处理速度上,随着应用程序的使用和业务的拓展,数据库数据量逐渐增多,数据库处理压力逐渐增大,关系型数据库数据存放在磁盘上的,读写速度较慢(与内存中的数据相比)。

优化方式:

  • 设计表的时候严格根据数据库的设计范式来设计数据库;

  • select 后尽量不使用*;

  • 尽量不使用嵌套查询,使用连接查询或者where查询;

  • sql关键词尽量使用大写;

  • 尽量使用逻辑外交不使用物理外键;

  • 给查询频繁的字段添加索引,并且遵循最左原则(尽量将首选关键字段放在最前边);

  • 垂直分库分表:把一些不经常读的数据或者结果复杂的表拆分成多张表,较少磁盘I/O操作;

  • 水平分库分表:于数据量庞大的表,使用水平分库分表;

  • 使用缓存,把经常访问到的数据而且不需要经常变化的数据放在缓存中,能节约磁盘IO;

  • 优化硬件;

  • 主从分离读写;采用主从复制把数据库的读操作和写入操作分离开来。

使用过的解析数据包:Xpath、正则

怎么监控爬虫的状态

(1)使用 Python 的 STMP 包将爬虫的状态信心发送到指定的邮箱。

(2)Scrapyd、pyspider

(3)引入日志

panads读取数据

import pandas as pd

# 加载detail

detail = pd.read_excel("./meal_order_detail.xlsx")

# 1、直接查看方式--先后索引

# 先列 后行的方式

# 获取dishes_id counts amounts dishes_name 多列数据

res = detail[["dishes_id","counts","amounts","dishes_name"]]

# 获取多列的前n行

res1 = detail[["dishes_id", "counts", "amounts", "dishes_name"]][:20]

res2 = detail[["dishes_id", "counts", "amounts", "dishes_name"]].head(20)

# res = detail[["dishes_id", "counts", "amounts", "dishes_name"]][[0,1,2,3,4]] # # 不可以的

# res = detail["dishes_id"][[0,1,2,3,4]] # 可以的

# print("获取多列的结果:\n", res)

# 2、同时索引

# loc 只能使用名称 ---注意:可以使用名称切片---包含收尾

# iloc 只能使用下标

res3 = detail.loc[:,"dishes_id":"counts"]

# 3、ix ---同时索引-----混合索引

# 既可以使用名称,也可以使用下标

# res = detail.ix[0:4, "dishes_id":"counts"]

# res = detail.ix[0:4, 0:2] # 前面行使用名称,列使用下标

#

# res = detail.ix[0:4, 0:"amounts"] # 错误的,不能混搭

# print("res:\n", res)



# 直接索引方式 # 速度最快

# loc iloc #速度适中 # 某些大的平台上只封装loc iloc方式 --推荐

# ix # 速度最慢 --在最新的版本中提示,将要被删除掉

Nginx的正向代理和反向代理

Web 开发中,部署方式大致类似。简单来说,使用 Nginx 主要是为了实现分流、转发、负载均衡, 以及分担服务器的压力。Nginx 部署简单,内存消耗少,成本低。Nginx 既可以做正向代理,也可以做反向代理。

正向代理:请求经过代理服务器从局域网发出,然后到达互联网上的服务器。特点:服务端并不知道真正的客户端是谁。

反向代理:请求从互联网发出,先进入代理服务器,再转发给局域网内的服务器。特点:客户端并不知道真正的服务端是谁。

区别:正向代理的对象是客户端。反向代理的对象是服务端。

缓存穿透、缓存击穿、缓存雪崩

Redis技术就是NoSQL技术中的一种,但是引入Redis又有可能出现缓存穿透,缓存击穿,缓存雪崩等问题。

1.缓存穿透:

(1)产生原因:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

(2)解决方案:

①最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

②另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

2.缓存击穿:

(1)产生原因:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

(2)解决方案:业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

3.缓存雪崩:

(1)产生原因:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。

(2)解决方案:

①缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。

②简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

Python垃圾回收机制

引用计数是一种垃圾收集机制,而且也是一种最直观,最简单的垃圾收集技术。当 Python 的某个对象的引用计数降为 0 时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为 1。如果引用被删除,对象的引用计数为 0,那么该对象就可以被垃圾回收。不过如果出现循环引用的话,引用计数机制就不再起有效的作用了。

一般是类中的 __del方法进行的。

版权声明:本文为CSDN博主「不愿透露姓名の网友」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_40558166/article/details/103044923

金三银四招聘季,你还有哪些面试干货文章呢?分享或者查看干货文章可戳:

Tags:

最近发表
标签列表