爬⾍⾯试题
scrapy框架的⼯作流程?
  a、spider解析下载器下下来的response,返回item或是links
  b、item或者link经过spidermiddleware的process_spider_out( )⽅法,交给engine
  c、engine将item交给item pipeline ,将links交给调度器
  d、在调度器中,先将requests对象利⽤scrapy内置的指纹函数⽣成⼀个指纹对象
  e、如果requests对象中的don't filter参数设置为False,并且该requests对象的指纹不在信息指纹的队列中,那么就把该request对象放到优先级队列中
  f、从优先级队列中获取request对象,交给engine
  g、engine将request对象交给下载器下载,期间会通过downloadmiddleware的process_request⽅法
  h、下载器完成下载,获得response对象,将该对象交给engine,期间会经过downloadmiddleware的            process_response()⽅法
  i、engine将获得的response对象交给spider进⾏解析,期间会经过spidermiddleware的process_spider_input()的⽅法
  j、从a开始循环
1,你了解的反爬机制,如何处理?
通过headers反爬⾍
ip的访问频率
动态加载数据,通过ajax请求数据,或者通过js代码⽣成数据
部分数据加密处理
解决⽅法: 
对于基本⽹页的抓取可以⾃定义headers,添加headers的数据
使⽤多个代理ip进⾏抓取或者设置抓取的频率降低⼀些,
动态⽹页的可以使⽤selenium + phantomjs 进⾏抓取
对部分数据进⾏加密的,可以使⽤selenium进⾏截图,使⽤python⾃带的pytesseract库进⾏识别,但是⽐较慢最直接的⽅法是到加密的⽅法进⾏逆向推理。
2,scrapy的基本架构
Scrapy Engine(引擎): 负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。
Scheduler(调度器): 它负责接受引擎发送过来的Request请求,并按照⼀定的⽅式进⾏整理排列,⼊队,当引擎需要时,交还给引擎。
Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理,Spider(爬⾍):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进⼊Scheduler(调度器),
Item Pipeline(管道):它负责处理Spider中获取到的Item,并进⾏进⾏后期处理(详细分析、过滤、存储等)的地⽅.
Downloader Middlewares(下载中间件):你可以当作是⼀个可以⾃定义扩展下载功能的组件。
Spider Middlewares(Spider中间件):你可以理解为是⼀个可以⾃定扩展和操作引擎和Spider中间通信的功能组件(⽐如进⼊Spider的Responses;和从Spider出去的Requests)
3,scrapy的去重原理
1.到Request类:需要将dont_filter设置为False开启去重,默认是True,没有开启去重;
2.对于每⼀个url的请求,调度器都会根据请求得相关信息加密得到⼀个指纹信息,并且将指纹信息和set()集合中的指纹信息进⾏⽐对,如果set()集合中已经存在这个数据,就不在将这个Request放⼊队列中。如果set()集合中没有存在这个加密后的数据,就将这个Request对象放⼊队列中,等待被调度。
4,scrapy中间件有哪⼏种类,你⽤过哪些?
下载中间件,爬⾍中间件
5,scrapy中间件在哪起的作⽤
爬⾍中间件:爬⾍发起请求request的时候调⽤,列如更换修改代理ip,修改UA,
下载器中间件:浏览器返回响应response的时候调⽤,⽆效的数据,特殊情况进⾏重试
6,为什么会⽤到代理?
如果你⼀直⽤同⼀个代理IP爬取这个⽹页,很有可能IP会被禁⽌访问⽹页,所以,基本上做爬⾍的都躲不过去IP的问题。
7,代理怎么使⽤?
1,可以使⽤urllib2中的ProxyHandler来设置代理ip
1 import urllib2
2
3 # 构建了两个代理Handler,⼀个有代理IP,⼀个没有代理IP
4 httpproxy_handler = urllib2.ProxyHandler({"http" : "124.88.67.81:80"})
5 nullproxy_handler = urllib2.ProxyHandler({})
6 #定义⼀个代理开关
7 proxySwitch = True
8 # 通过 urllib2.build_opener()⽅法使⽤这些代理Handler对象,创建⾃定义opener对象
9 # 根据代理开关是否打开,使⽤不同的代理模式
10if proxySwitch:
11    opener = urllib2.build_opener(httpproxy_handler)
12else:
13    opener = urllib2.build_opener(nullproxy_handler)
14
15 request = urllib2.Request("www.baidu/")
16
17 # 使⽤opener.open()⽅法发送请求才使⽤⾃定义的代理,⽽urlopen()则不使⽤⾃定义代理。
18 response = opener.open(request)
19
20 # 就是将opener应⽤到全局,之后所有的,不管是opener.open()还是urlopen() 发送请求,都将使⽤⾃定义代理。
21 # urllib2.install_opener(opener)
22 # response = urlopen(request)
23
24 ad()
uillib2代理ip
2,使⽤requets代理
1 import requests
2
3 # 根据协议类型,选择不同的代理
4 proxies = {
5"http": "12.34.56.79:9527",
6"https": "12.34.56.79:9527",
7 }
8
9 response = ("www.baidu", proxies = proxies)
10
requests代理
8,代理失效了怎么处理?
11、将代理IP及其协议载⼊ProxyHandler赋给⼀个opener_support变量;2、将opener_support载⼊build_opener⽅法,创建opener;3、安装opener。具体代码如下:from urllib import requestdef ProxySpider(url, proxy_ip, header):opener_support = View Code
9,爬取过程中,登录验证码如何处理?
抓取验证码图,对接打码平台进⾏处理返回
10,爬取速度过快,出现的验证码如何处理?
抓取验证码图,对接打码平台进⾏处理返回
11,如何⽤机器识别验证码?
对接打码平台
12,cookie如何处理?
requests.session
13,如何处理⽹站加密传参的情况?
简单加密可以使⽤selenium,或者执⾏js,存储cookie,⼿动修改cookie的值
也可以下载它们相关的APP
14,分布式的原理?
捅过重写scheduler和spider类,实现了调度、spider启动和redis的交互。实现新的dupefilter和queue类,达到了判重和调度容器和redis的交互,因为每个主机上的爬⾍进程都访
问同⼀个redis数据库,所以调度和判重都统⼀进⾏统⼀管理,达到了分布式爬⾍的⽬的。
15,分布式如何判断爬⾍已经停⽌了?
Status();//获取爬⾍状态
Status().equals(Spider.Status.Init);//运⾏中
View Code
16,爬取下来的数据如何选择什么存储⽅式会更好
针对数据⽽定。
18,动态加载的数据如何提取?
动态⽹页的可以使⽤selenium + phantomjs 进⾏抓取
19,json数据如何提取?
json.loads, json.dumps
20,Python⾥⾯深拷贝,浅拷贝的区别?
浅拷贝不管⼏层只会拷贝第⼀层
拷贝就是在内存中重新开辟⼀块空间,不管数据结构多么复杂,只要遇到可能发⽣改变的数据类型,就重新开辟⼀块内存空间把内容复制下来,直到最后⼀层,不再有复杂的数
据类型,就保持其原引⽤。这样,不管数据结构多么的复杂,数据之间的修改都不会相互影响
21,是否了解线程的同步和异步?
线程同步是多个线程同时访问同⼀资源,等待资源访问结束,浪费时间,效率低
线程异步:访问资源时在空闲等待时同时访问其他资源,实现多线程机制
22,链表和顺序表存储时各⾃有什么特点?
顺序表的优点是可以随机访问数据元素;
缺点是⼤⼩固定,不利于增删结点。
链表的优点是采⽤指针⽅式增减结点,⾮常⽅便(只需要改变指针指向,不移动结点);
缺点是不能进⾏随机访问,另外,每个结点上增加指针域,造成额外存储空间增⼤。
23,使⽤redis搭建分布式系统时如何处理⽹络延迟和⽹络异常?
由于⽹络异常的存在,分布式系统中请求结果存在“三态”的概念,即三种状态:“成功”、“失败”、“超时(未知)”
当出现“超时”时可以通过发起读取数据的操作以验证 RPC 是否成功(例如银⾏系统的做法)
另⼀种简单的做法是,设计分布式协议时将执⾏步骤设计为可重试的,即具有所谓的“幂等性”
24,数据仓库是什么?
数据仓库是⼀个⾯向主题的、集成的、稳定的、反映历史变化的、随着时间的流逝发⽣变化的数据集合。它主要⽀持管理⼈员的决策分析。
数据仓库收集了企业相关内部和外部各个业务系统数据源、归档⽂件等⼀系列历史数据,最后转化成企
业需要的战略决策信息。
特点:
⾯向主题:根据业务的不同⽽进⾏的内容划分;
集成特性:因为不同的业务源数据具有不同的数据特点,当业务源数据进⼊到数据仓库时,需要采⽤统⼀的编码格式进⾏数据加载,从⽽保证数据仓库中数据的唯⼀性;
⾮易失性:数据仓库通过保存数据不同历史的各种状态,并不对数据进⾏任何更新操作。
历史特性:数据保留时间戳字段,记录每个数据在不同时间内的各种状态。
25,假设有⼀个爬⾍程序,从⽹络上的获取的频率快,本地写⼊数据的频率慢,使⽤什么数据结构⽐较好?
26,你是否了解⾕歌的⽆头浏览器?
⽆头浏览器即headless browser,是⼀种没有界⾯的浏览器。既然是浏览器那么浏览器该有的东西它都应该有,只是看不到界⾯⽽已。
Python中selenium模块中的PhantomJS即为⽆界⾯浏览器(⽆头浏览器):是基于QtWebkit的⽆头浏览器,
27,你是否了解mysql数据库的⼏种引擎?
InnoDB:
InnoDB是⼀个健壮的事务型存储引擎,这种存储引擎已经被很多互联⽹公司使⽤,为⽤户操作⾮常⼤的数据存储提供了⼀个强⼤的解决⽅案。
在以下场合下,使⽤InnoDB是最理想的选择:
1.更新密集的表。InnoDB存储引擎特别适合处理多重并发的更新请求。
2.事务。InnoDB存储引擎是⽀持事务的标准MySQL存储引擎。
3.⾃动灾难恢复。与其它存储引擎不同,InnoDB表能够⾃动从灾难中恢复。
4.外键约束。MySQL⽀持外键的存储引擎只有InnoDB。
5.⽀持⾃动增加列AUTO_INCREMENT属性。
⼀般来说,如果需要事务⽀持,并且有较⾼的并发读取频率,InnoDB是不错的选择。
MEMORY:
使⽤MySQL Memory存储引擎的出发点是速度。为得到最快的响应时间,采⽤的逻辑存储介质是系统内存。
虽然在内存中存储表数据确实会提供很⾼的性能,但当mysqld守护进程崩溃时,所有的Memory数据都会丢失。
获得速度的同时也带来了⼀些缺陷。
⼀般在以下⼏种情况下使⽤Memory存储引擎:
1.⽬标数据较⼩,⽽且被⾮常频繁地访问。在内存中存放数据,所以会造成内存的使⽤,可以通过参数max_heap_table_size控制Memory表的⼤⼩,设置此参数,就可以限制Memory表的最⼤⼤⼩。
2.如果数据是临时的,⽽且要求必须⽴即可⽤,那么就可以存放在内存表中。
3.存储在Memory表中的数据如果突然丢失,不会对应⽤服务产⽣实质的负⾯影响。
28,redis数据库有哪⼏种数据结构?
5种数据结构
string
使⽤string时,redis**⼤多数情况下**并不会理解或者解析其含义,⽆论使⽤json、xml还是纯⽂本在redis看来都是⼀样的,只是⼀个字符串,只能进⾏strlen、append等对字符串通⽤的操作,⽆法针对其内容进⼀步操作。其基本操作命令有set、get、strlen、getrange、append:
1 2 3 4 5SET key value
GET key
STRLEN key GETRANGE key start end APPEND key value
在⼤多数情况之外,就是string中存储的为纯数字的情况,redis可以将字符串当做数字进⾏进⼀步操作,这些操作包括decr、decrby、incr、incrby和incrbyfloat。
hash
使⽤hash时,在我看来,就是value本⾝就是⼀组key-value对,不过redis将这⾥的key称为field(但是hkeys命令为什么不叫hfields命令呢哈哈),也就是value是⼀组field-value 对。其基本操作命令有hset、hget、hmset、hmget、hgetall、hkeys和hdel:
1 2 3 4 5 6 7HSET key field value
HGET key field
HMSET key field value [field value ...] HMGET key field [field ...] HGETALL key
HKEYS key
HDEL key field [field ...]
list
使⽤list时,value就是⼀个string数组,操作这组string时,可以像对待栈⼀样使⽤pop和push操作,但是这个栈两端都能进⾏操作;也可以像对待数组⼀样使⽤⼀个index参数来操作。list的操作命令略杂,主要分为两类:L开头的和R开头的,L代表LEFT或者LIST,进⾏⼀些从列表左端进⾏的操作,或者⼀些与端⽆关的操作;R代表RIGHT,进⾏⼀些从列表右端进⾏的操作。
set
set⽤于存储⼀组不重复的值,也可以进⾏⼀些集合的操作,就像数学上的集合,它是⽆序的。基本操作有sadd和sismember:
1 2SADD key member [member ...] SISMEMBER key member
集合操作有:求交sinter、求并sunion和求差sdiff:
1 2 3SINTER key [key ...] SUNION key [key ...] SDIFF key [key ...]
sorted set
sorted set类似set,但是sorted set⾥每个元素都有⼀个score,这个score可⽤于排序和排名。基本操作有zadd、zcount、zrank:
1 2 3ZADD key score member [score member ...] ZCOUNT key min max
ZRANK key member
29,对 __name__=='main'的理解
__name__是当前模块名,当模块被直接运⾏时模块名为_main_,也就是当前的模块,当模块被导⼊时,模块名就不是__main__,即代码将不会执⾏。
30,python是如何进⾏内存管理的?
a、对象的引⽤计数机制
python内部使⽤引⽤计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引⽤,即引⽤计数,当对象被创建时就创建了⼀个引⽤计数,当对象不再需要时,这个对象的引⽤计数为0时,它被垃圾回收。
b、垃圾回收
1>当⼀个对象的引⽤计数归零时,它将被垃圾收集机制处理掉。
2>当两个对象a和b相互引⽤时,del语句可以减少a和b的引⽤计数,并销毁⽤于引⽤底层对象的名称。然⽽由于每个对象都包含⼀个对其他对象的应⽤,因此引⽤计数不会归零,对象也不会销毁。(从⽽导致内存泄露)。为解决这⼀问题,解释器会定期执⾏⼀个循环检测器,搜索不可访问对象的循环并删除它们。
c、内存池机制
Python提供了对内存的垃圾收集机制,但是它将不⽤的内存放到内存池⽽不是返回给操作系统。
1>Pymalloc机制。为了加速Python的执⾏效率,Python引⼊了⼀个内存池机制,⽤于管理对⼩块内存的申请和释放。
2>Python中所有⼩于256个字节的对象都使⽤pymalloc实现的分配器,⽽⼤的对象则使⽤系统的malloc。
3>对于Python对象,如整数,浮点数和List,都有其独⽴的私有内存池,对象间不共享他们的内存池。也就是说如果你分配⼜释放了⼤量的整数,⽤于缓存这些整数的内存就不能再分配给浮点数。
31,__new)_和__init__的区别
__new__:它是创建对象时调⽤,会返回当前对象的⼀个实例,可以⽤_new_来实现单例
__init__:它是创建对象后调⽤,对当前对象的⼀些实例初始化,⽆返回值
32,常⽤的⽹络爬取⽅法
正则表达式
Beautiful Soup
Lxml
33,urllib 和urllib2的区别?
urllib 和urllib2都是接受URL请求的相关模块,但是urllib2可以接受⼀个Request类的实例来设置URL请求的headers,urllib仅可以接受URL。urllib不可以伪装你的User-Agent字符串。
urllib提供urlencode()⽅法⽤来GET查询字符串的产⽣,⽽urllib2没有。这是为何urllib常和urllib2⼀起使⽤的原因。
34,列举爬⾍⽤到的⽹络数据包,解析包?
⽹络数据包 urllib、urllib2、requests
解析包 re、xpath、beautiful soup、lxml
35,⼿写⼀个单列模式?
1 lass Singleton(object):
2    _instance = None
3    def __new__(cls, *args, **kw):
4if not cls._instance:
5            cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
6return cls._instance
7class MyClass(Singleton):
8    a = 1
9 one = MyClass()
10 two = MyClass()
11 id(one) = id(two)
12 >>> True
单列模式
36,常⽤的⽹络爬取⽅法?
urllib2 , Beautiful Soup  正则 xpath
37,代码实现删除list⾥⾯的重复元素
set
38,⼿写代码⼆分查
39,python2和python3的区别
40,python的装饰器,⼿写
41,python常⽤的内置库,
42,如何做增量式爬取?
去爬帖⼦列表页,每次都把当前页的 url 和数据库中的对⽐,如果没有重复的说明,这⼀夜都是新的 URL,然后下⼀页,直到出现部分重复或完全重复的页⾯
43,简述beautifulsoup模块的作⽤及基本使⽤?
BeautifulSoup是⼀个模块,该模块⽤于接收⼀个HTML或XML字符串,然后将其进⾏格式化,之后遍可以使⽤他提供的⽅法进⾏快速查指定元素,从⽽使得在HTML或XML中查指定元素变得简单。
44,seleniun模块的作⽤及基本使⽤?
操作浏览器
45,scrapy如何实现⼤⽂件的下载?
当使⽤requests的get下载⼤⽂件/数据时,建议使⽤使⽤stream模式。
当把get函数的stream参数设置成False时,它会⽴即开始下载⽂件并放到内存中,如果⽂件过⼤,有可能导致内存不⾜。
当把get函数的stream参数设置成True时,它不会⽴即开始下载,当你使⽤iter_content或iter_lines遍历内容或访问内容属性时才开始下载。需要注意⼀点:⽂件没有下载之前,它也需要保持连接。
iter_content:⼀块⼀块的遍历要下载的内容
iter_lines:⼀⾏⼀⾏的遍历要下载的内容
使⽤上⾯两个函数下载⼤⽂件可以防⽌占⽤过多的内存,因为每次只下载⼩部分数据。
⽰例代码:
1 r = (url_file, stream=True)
2 f = open("file_path", "wb")
3 # chunk是指定每次写⼊的⼤⼩,每次只写了512byte
4for chunk in r.iter_content(chunk_size=512):
5if chunk:
6        f.write(chunk)
View Code
46,scrapy如何实现限速?
在settings⾥⾯,
DOWNLOAD_DELAY = 0.25 # 250 ms of delay
47,scrapy如何实现暂停爬⾍程序?
实现暂停与重启记录状态
⽅法⼀:
1、⾸先cd进⼊到scrapy项⽬⾥(当然你也可以通过编写脚本Python⽂件直接在pycharm中运⾏)
2、在scrapy项⽬⾥创建保存记录信息的⽂件夹
3、执⾏命令:
  scrapy crawl 爬⾍名称 -s JOBDIR=保存记录信息的路径
  如:scrapy crawl cnblogs -s JOBDIR=zant/001
  执⾏命令会启动指定爬⾍,并且记录状态到指定⽬录
爬⾍已经启动,我们可以按键盘上的ctrl+c停⽌爬⾍,停⽌后我们看⼀下记录⽂件夹,会多出3个⽂件,其中的requests.queue⽂件夹⾥的p0⽂件就是URL记录⽂件,这个⽂件存在就说明还有未完成的URL,当所有URL完成后会⾃动删除此⽂件
当我们重新执⾏命令:scrapy crawl cnblogs -s JOBDIR=zant/001  时爬⾍会根据p0⽂件从停⽌的地⽅开始继续爬取。
⽅法⼆:
在settings.py⽂件⾥加⼊下⾯的代码:
JOBDIR='sharejs'
使⽤命令scrapy crawl somespider,就会⾃动⽣成⼀个sharejs的⽬录,然后将⼯作列表放到这个⽂件夹⾥
48,scrapy如何进⾏⾃定制指令?
单爬⾍运⾏
代码:
1 import sys
dline import execute
3
4if __name__ == '__main__':
5    execute(["scrapy","crawl","chouti","--nolog"])
View Code
同时运⾏多个爬⾍
步骤如下:
- 在spiders同级创建任意⽬录,如:commands
- 在其中创建 crawlall.py ⽂件(此处⽂件名就是⾃定义的命令)
- 在settings.py 中添加配置 COMMANDS_MODULE = '项⽬名称.⽬录名称'
-
在项⽬⽬录执⾏命令:scrapy crawlall
代码:
1from scrapymands import ScrapyCommand
2from scrapy.utils.project import get_project_settings
3
4class Command(ScrapyCommand):
5
6        requires_project = True
7
8        def syntax(self):
9return'[options]'
10
11        def short_desc(self):
selenium获取cookie12return'Runs all of the spiders'
13
14        def run(self, args, opts):
15            spider_list = awler_process.spiders.list()
16for name in spider_list:
17                awl(name, **opts.__dict__)
18            awler_process.start()
19
20 crawlall.py
View Code
49,scrapy如何实现记录爬⾍的深度?
通过scrapy.CrawlSpider
50,scrapy中的pipelines⼯作原理?
当Item在Spider中被收集之后,它将会被传递到Item Pipeline,这些Item Pipeline组件按定义的顺序处理Item。
每个Item Pipeline都是实现了简单⽅法的Python类,⽐如决定此Item是丢弃⽽存储。以下是item pipeline的⼀些典型应⽤:
验证爬取的数据(检查item包含某些字段,⽐如说name字段)
查重(并丢弃)
将爬取结果保存到⽂件或者数据库中
51,简述scrapy爬⾍中间件和下载中间件的作⽤?
52,scrapy-redis组件的作⽤?
scheduler - 调度器
dupefilter - URL去重规则(被调度器使⽤)
pipeline  - 数据持久化
53,scrapy-redis组件中如何实现任务的去重?
dupefilter - URL去重规则(被调度器使⽤)
54,scrapy-redis的调度器如何实现任务的深度优先和⼴度优先?
55,⼿写正则匹配邮箱,⼿机号,⼀段html内容
56,robots协议是什么?
Robots协议(也称为爬⾍协议、机器⼈协议等)的全称是“⽹络爬⾍排除标准”(Robots Exclusion Protocol),⽹站通过Robots协议告诉搜索引擎哪些页⾯可以抓取,哪些页⾯不能抓取。
57,常见的http状态码?
1⼀些常见的状态码为:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。