一. Requests模块
1. 简述爬虫的概念
爬虫就是通过编写程序模拟浏览器上网,然后让其去互联网上抓取数据的过程。
2. 爬虫有几种分类,在使用场景中
主要有两种:
在编写爬虫时先下载到本地进行测试,然后再进行正规的验证
1.通用爬虫:
通用爬虫是搜索引擎(Baidu、Google、Yahoo等)“抓取系统”的重要组成部分。主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份。
简单来讲就是尽可能的;把互联网上的所有的网页下载下来,放到本地服务器里形成备分,在对这些网页做相关处理(提取关键字、去掉广告),最后提供一个用户检索接口。
搜索引擎如何抓取互联网上的网站数据?
门户网站主动向搜索引擎公司提供其网站的url
搜索引擎公司与DNS服务商合作,获取网站的url
门户网站主动挂靠在一些知名网站的友情链接中
2.聚焦爬虫:聚焦爬虫是根据指定的需求抓取网络上指定的数据。例如:获取豆瓣上电影的名称和影评,而不是获取整张页面中所有的数据值。
3. 简述robots协议的概念也作用
- 如果自己的门户网站中的指定页面中的数据不想让爬虫程序爬取到的话,那么则可以通过编写一个robots.txt的协议文件来约束爬虫程序的数据爬取。
robots协议的编写格式可以观察淘宝网的robots(访问www.taobao.com/robots.txt即可)。但是需要注意的是,该协议只是相当于口头的协议,并
没有使用相关技术进行强制管制,所以该协议是防君子不防小人。但是我们在学习爬虫阶段编写的爬虫程序可以先忽略robots协议。
# Obey robots.txt rules
ROBOTSTXT_OBEY = True
4. 什么是反爬机制和反反爬机制
- 门户网站通过相应的策略和技术手段,防止爬虫程序进行网站数据的爬取。
反爬机制:
(1)U-A校验模式
当你在家用浏览器上网的时候,每次发送请求时请求头会自动携带浏览器参数还有系统参数给服务器。从而让服务器知道这是一个人啊!一次来作为反爬机制的一种。
(2)限制访问频率
正常人浏览网站点击速度有多少?机器访问快多了,短时间发送几百个请求。这时候服务器检测到后就可以通过设置一个速度去封IP,例如一分钟访问200次就封锁IP
(3)设置验证码
很多朋友都不明白为什么要验证码?我小时候想了这个问题很久很久,自从学了爬虫才明白,这东西真的是为了验证我是一个人在上网而生得。这种方法就让爬虫更加困难了。
(4)通过账号限制
通过登录才可以浏览网站。
反反爬虫机制:
- 爬虫程序通过相应的策略和技术手段,破解门户网站的反爬虫手段,从而爬取到相应的数据。
5. 简述使用requests模块进行数据爬取的大致流程
1、指定url
2、基于requests模块发起请求
3、获取响应对象中的数据值
4、数据解析
5、持久化存储
#指定搜索关键字 word = input('enter a word you want to search:') #自定义请求头信息 headers={ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', } #指定url url = 'https://www.sogou.com/web' #封装get请求参数 prams = { 'query':word, 'ie':'utf-8' } #发起请求 response = requests.get(url=url,params=param) #获取响应数据 page_text = response.text with open('./sougou.html','w',encoding='utf-8') as fp: fp.write(page_text)
6. 简述使用requests模块爬取ajax加载数据爬取的大致流程
流程和普通的requests相同,
不同之处是:通过抓包工具获取请求的url和请求参数
url和请求参数,get请求参数封装在params字典里面;post请求参数封装在data字典里面
基于requests模块ajax的get请求 需求:爬取豆瓣电影分类排行榜 https://movie.douban.com/中的电影详情数据 基于requests模块ajax的post请求 需求:爬取肯德基餐厅查询http://www.kfc.com.cn/kfccda/index.aspx中指定地点的餐厅数据
#指定ajax-get/post请求的url(通过抓包进行获取) url = 'https://movie.douban.com/j/chart/top_list?' #定制请求头信息,相关的头信息必须封装在字典结构中 headers = { #定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36', } #定制get请求携带的参数(从抓包工具中获取) param = { 'type':'5', 'interval_id':'100:90', 'action':'', 'start':'0', 'limit':'20' } #发起get/post请求,获取响应对象 response = requests.get(url=url,headers=headers,params=param) #获取响应内容:响应内容为json串 print(response.text)
7. 简述User-Agent参数的作用
在http协议里,头信息中有一个 User-Agent,它的作用是告诉服务器,用户客户端是什么浏览器,以及操作系统的信息的。在某些特殊的情况下,
服务器根据浏览器的不同类型,输出不 同的内容。大概在三四年前,很多网站都只显示给IE看,所以当时的 opera 浏览器还特别做了一个功能,可
以把它的 User-Agent 换成 IE 的。所以 user-agent 是非常不可靠的,原因就是它是客户端自己决定并发送给服务器。 1,使用User-Agent伪造浏览器,谎称身份欺骗服务器
8. 在requests模块中接触过哪些反爬机制
验证码、限制ip访问频率、U-A校验模式
9. 介绍下requests模块中get和post方法常用参数的作用
#封装get请求参数 prams = { 'query':word, 'ie':'utf-8' } #发起get请求,获取响应对象 #定制请求头信息,相关的头信息必须封装在字典结构中 headers = { #定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36', } response = requests.get(url=url,headers=headers,params=param) #定制post请求携带的参数(从抓包工具中获取) data = { 'cname':'', 'pid':'', 'keyword':'北京', 'pageIndex': '1', 'pageSize': '10' } #发起post请求,获取响应对象 response = requests.get(url=url,headers=headers,data=data) #获取响应内容:响应内容为json串 print(response.text)
10. 简述session的创建流程及其该对象的作用
#创建一个session对象,该对象会自动将请求中的cookie进行存储和携带 session = requests.session()
cookie由服务器创建并交给客户端浏览器保存,会话跟踪技术,再次访问时,服务器就可以区分客户端浏览器
#使用session发送请求,目的是为了将session保存该次请求中的cookie
session.post(url=post_url,data=formdata,headers=headers)
get_url = 'http://www.renren.com/960481378/profile'
#再次使用session进行请求的发送,该次请求中已经携带了cookie
response = session.get(url=get_url,headers=headers)
11. 简述如何使用requests模块进行模拟登录,并抓取登录成功后的某个二级页面数据。
requests.get请求到登陆页面
requests.post请求
/pazims/
结合session = requests.post.session() 先登陆成功后:在浏览器获取post请求的参数 response = requests.post(url=url,data=data,headers=headers) 获取个人页面url params = {} 获取get请求参数 response = requests.get(url=url,params=params,headers=headers) # 3.获取响应对象的页面数据 page_text = response.text print(page_text)
12. 简述如何使用requests模块设置代理IP
两种爬虫编写方法
1、在使用requests模块--get post ---proxy 2]、在scrapy框架下----下载中间件
设置代理ip主要有两种形式: 一种是在代码中直接写 #不同的代理IP proxy_list = [ {"http": "112.115.57.20:3128"}, {'http': '121.41.171.223:3128'} ] #随机获取UA和代理IP header = random.choice(header_list) proxy = random.choice(proxy_list) #参数3:设置代理 response = requests.get(url=url,headers=header,proxies=proxy) ------------------------------------------------------------------------------ 另外一种是在中间件中设置: 1.在下载中间件中拦截请求 2.将拦截到的请求的IP修改成某一代理IP 3.在配置文件(settings.py)中开启下载中间件
二. 数据解析
1. 简述使用在使用正则进行解析时用到的re.S和re.M的作用和区别
re.I : 忽略大小写
re.M :多行匹配
re.S :单行匹配
---------------------------
#匹配出i开头的行 string = '''fall in love with you i love you very much i love she i love her''' re.findall('^.*',string,re.M)
##################################################################### #匹配全部行 string1 = """<div>静夜思 窗前明月光 疑是地上霜 举头望明月 低头思故乡 </div>""" re.findall('.*',string1,re.S)
2. 简述如何使用xpath进行数据解析
jupyter 音标:/ˈdʒuːpɪtə(r)/
1、jupyter,python环境下
首先要下载 from lxml import etree
xpath插件:就可以直接将xpath表达式作用到浏览器的网页当中
安装:选择浏览器更多工具-扩展程序(管理)-打开开发者 打开界面直接拖进来
创建etree对象进行指定数据的解析
- 本地:tree = etree.parse(‘本地文件路径’)
tree.xpath('xpath表达式')
- 网络:tree=etree.HTML('网络请求到的页面数据')
tree.xpath('xpath表达式')
2、使用Scrapy框架开发,已经封装了xpath模块
属性定位:
div_list = response.xpath("//div[@class='data_row news_article clearfix']")
#找到class属性值为song的div标签 //div[@class="song"]
层级定位、逻辑元算、模糊匹配、取属性
取属性: //div[@class="tang"]//li[2]/a/@href
3. 简述如何使用bs4进行数据解析
beautiful soup soup 英 /suːp/ 美 /sup/
python独有的一种解析方式,只能在python中进行解析
使用起来更加简便和高效
from bs4 import BeautifulSoup - 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的节点内容 (1)转化本地文件: - soup = BeautifulSoup(open('本地文件'), 'lxml') (2)转化网络文件: #获取标题正文页数据 page_text = requests.get(url,headers=headers).text soup = BeautifulSoup('字符串类型或者字节类型', 'lxml') (3)打印soup对象显示内容为html文件中的内容 #创建soup对象 soup = BeautifulSoup(page_text,'lxml') #解析数据 1、找到符合条件的标签 2、获取标签的属性、内容 【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
4. xpath方法返回值类型是什么‘
xpath函数返回的总是一个列表----列表里面是选择器对象
可以用索引、和切片进行处理
5. 在xpath中如何/text()和//text()的区别是什么
1、/text()表示获取某个标签下的文本内容
2、//text()表示获取某个标签下的文本内容 和 所有子标签下的文本内容
6. id为su的div标签有一个子标签ul,ul下有十个li标签,每一个li标签下都有一个a标签,如何编写xpath表达式可以解析到a标签的href属性值
tree=etree.HTML(url_content)
li = tree.xpath('//div[@id="su"]/ul/li')
li = response.xpath('//div[@id="su"]/ul/li')
for i in li:
i.xpath('./a/@href').extract_first()
response.xpath('//div[@id="images"]/a/text()').extract_first() 提取到第一个匹配到的元素, 必须调用 .extract_first() 提取真实的原文数据,你需要调用 .extract() response.xpath('//title/text()').extract() 当你没有使用extract()的时候,提取出来的内容依然具有选择器属性,简而言之,你可以继续使用里面的内容进行提取下级内容,而当你使用了extract()之后,提取出来的内容就会变成字符串格式了 关于选择器 Scrapy帮我们下载完页面后,我们怎样在满是html标签的内容中找到我们所需要的元素呢,这里就需要使用到选择器了,它们是用来定位元素并且提取元素的值。先来举几个例子看看: /html/head/title: 选择<title>节点, 它位于html文档的<head>节点内 /html/head/title/text(): 选择上面的<title>节点的内容. //td: 选择页面中所有的元素 //div[@class=”mine”]: 选择所有拥有属性class="mine"的div元素 Scrapy使用css和xpath选择器来定位元素,它有四个基本方法: xpath(): 返回选择器列表,每个选择器代表使用xpath语法选择的节点 css(): 返回选择器列表,每个选择器代表使用css语法选择的节点 extract(): 返回被选择元素的unicode字符串 re(): 返回通过正则表达式提取的unicode字符串列表
在Scrapy中才有 extract_first()和extract()
# 需求:将id97电影网站中电影详情数据进行爬取(名称,类型,导演,语言,片长) div_list = response.xpath('/html/body/div[1]/div[1]/div[2]/div') # 可循环,切片、索引 print('div_list',type(div_list)) print('div_list[0],索引',type(div_list[0])) print('div_list[1:],切片',type(div_list[1:])) for div in div_list: # extract_first()第一个 name = div.xpath(".//div[@class='meta']/h1/a/text()").extract_first() name2 = div.xpath(".//div[@class='meta']/h1/a/text()") name3 = div.xpath(".//div[@class='meta']/h1/a/text()")[0] #name4 = div.xpath(".//div[@class='meta']/h1/a/text()")[0].extract_first() print('name---',name,type(name))#print('name4---',name4,type(name4)) kind = div.xpath('.//div[@class="otherinfo"]//text()').extract() kind2 = div.xpath('.//div[@class="otherinfo"]//text()') kind3 = div.xpath('.//div[@class="otherinfo"]//text()')[0] kind4 = div.xpath('.//div[@class="otherinfo"]//text()')[0].extract() # 将kind列表转化成字符串 kind = " ".join(kind) print('" ".join(kind)---',kind,type(kind))
7. class为wd的div标签有一个子标签ul,ul下有十个li标签,每一个li标签下都有一个a标签,如何编写xpath表达式可以解析到a标签中的文本内容
lis=response.xpath('//div[@class="wd"]/ul/li')
for li in lis:
a=li.xpath('./a/text()').extract_first()
prin(a)
8. 简述BeautifulSoup模块中find和findall方法的区别
(4)find:找到第一个符合要求的标签 - soup.find('a') 找到第一个符合要求的 - soup.find('a', title="xxx") - soup.find('a', alt="xxx") - soup.find('a', class_="xxx") - soup.find('a', id="xxx") (5)find_all:找到所有符合要求的标签 - soup.find_all('a') - soup.find_all(['a','b']) 找到所有的a和b标签 - soup.find_all('a', limit=2) 限制前两个
9. 简述BeautifulSoup模块中select方法的使用
(6)根据选择器选择指定的内容 select:soup.select('#feng') - 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器 - 层级选择器: div .dudu #lala .meme .xixi 下面好多级 div > p > a > .lala 只能是下面一级 【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
a_list = soup.select('.book-mulu > ul > li > a')
print(a_list,type(a_list)) <class 'list'> 列表里面全是Tag对象 可以 继续Beatiful对象的属性和方法
print(a_list[0],type(a_list[0])) <class 'bs4.element.Tag'>
10. 简述xpath插件的作用
xpath插件:就可以直接将xpath表达式作用到浏览器的网页当中 ,进行验证
快捷键: 开启和关闭xpath插件:ctrl+shitf+x
-------------------------------------
首先先来介绍下xml,它是用来存储和传输数据使用的。 和html的不同有以下的两点: (1)html用来显示数据,xml是用来传输数据 (2)html标签是固定的,xml标签是自定义的 xpath用来在xml中查找指定的元素,它是一种路径表达式 常用的路径表达式: // : 不考虑位置的查找 ./ : 从当前节点开始往下查找 @ : 选取属性
# "."表示当前节点。
# ".."表示当前节点的父节点
1)通过属性进行定位 //input[@id="kw"] //input[@class="bg s_btn"] (2) 层级定位 索引定位 //div[@id="head"]/div/div[2]/a[@class="toindex"] 【注】索引从1开始 //div[@id="head"]//a[@class="toindex"] 【注】双斜杠代表下面所有的a节点,不管位置 逻辑运算 //input[@class="s_ipt" and @name="wd"] 模糊匹配 contains //input[contains(@class, "s_i")] 所有的input,有class属性,并且属性中带有s_i的节点 //input[contains(text(), "爱")] starts-with //input[starts-with(@class, "s")] 所有的input,有class属性,并且属性以s开头 取文本 //div[@id="u1"]/a[5]/text() 获取节点内容 //div[@id="u1"]//text() 获取节点里面不带标签的所有内容 取属性 //div[@id="u1"]/a[5]/@href 在代码中进行使用方法如下 from lxml import etree 两种方式使用:将html文档变成一个对象,然后调用对象的方法去查找指定的节点 (1)本地文件 tree = etree.parse(文件名) (2)网络文件 tree = etree.HTML(网页字符串) ret = tree.xpath(路径表达式) 注意:ret是一个列表 #将所用满足条件的内容进行拼接起来形成string ret = tree.xpath('//div[@class="**"]') string = ret[0].xpath('string(.)') print(string.replace(' ', '').replace(' ', ''))
三. Scrapy框架
1. 简述scrapy框架的安装流程
直接 pip install scrapy
install 英音[ɪnˈstɔ:l]、美音[ɪnˈstɔl] 安装、安顿
2. scrapy中持久化操作有几种形式,分别如何实现?
持久化存储操作: a. 磁盘文件 a) 基于终端指令 i. 保证parse方法返回一个可迭代类型的对象(存储解析到的页面内容) ii. 使用终端指令完成数据存储到制定磁盘文件中的操作 1. scrapy crawl 爬虫文件名称 –o 磁盘文件.后缀 b) 基于管道 i. items:存储解析到的页面数据 ii. pipelines:处理持久化存储的相关操作 iii. 代码实现流程: 1. 将解析到的页面数据存储到items对象 2. 使用yield关键字将items提交给管道文件进行处理 3. 在管道文件中编写代码完成数据存储的操作 4. 在配置文件中开启管道操作 b. 数据库 a) mysql b) redis c) 编码流程: 1. 将解析到的页面数据存储到items对象 2. 使用yield关键字将items提交给管道文件进行处理 3. 在管道文件中编写代码完成数据存储的操作 4. 在配置文件中开启管道操作
3. 简述start_requests方法的作用
源码: def start_requests(self): for url in self.start_urls: yield self.make_requests_from_url(url)
首先由start_requests
对start_urls
中的每一个url发起请求(make_requests_from_url
),这个请求会被parse接收。
4. 如何进行post请求发送,在scrapy中
post请求
方式一:通过FormRequest 发送
---------------------------------------------------
在之前代码中,我们从来没有手动的对start_urls列表中存储的起始url进行过请求的发送,但是起始url的确是进行了请求的发送,那这是如何实现的呢?
- 解答:其实是因为爬虫文件中的爬虫类继承到了Spider父类中的start_requests(self)这个方法,该方法就可以对start_urls列表中的url发起请求:
def start_requests(self):
for url in self.start_urls:
yield self.make_requests_from_url(url)
【注意】该方法默认的实现,是对起始的url发起get请求,
-----------------------------------------------------
如果想发起post请求,则需要子类重写该方法。
-方法: 重写start_requests方法,让其发起post请求:
def start_requests(self):
#请求的url
post_url = 'http://fanyi.baidu.com/sug'
# post请求参数
formdata = {
'kw': 'wolf',
}
# 发送post请求
yield scrapy.FormRequest(url=post_url, formdata=formdata, callback=self.parse)
-------------------------------------------------------------------
如何发起post请求? 一定要对start_requests方法进行重写。 method 英 /'meθəd/ 美 /'mɛθəd/
1. Request()方法中给method属性赋值成post
2. FormRequest()进行post请求的发送
5. 如何手动进行一个get请求的发送,在scrapy中
get请求 方式:通过Request 发送 def parse(self, response): #登录成功后的页面数据进行存储 fp = open('main.html','w',encoding='utf-8') fp.write(response.text) #获取当前用户的个人主页 url = 'https://www.douban.com/people/185687620/' yield scrapy.Request(url=url,callback=self.parseBySecondPage)
6. 简述管道文件的作用
爬取获得的数据通过 item 类实例化,然后传递到管道,通过管道进行进一步处理。
pipelines.py: 项目管道文件,用于提取Items内容 ,进行数据的持久化处理
parse():爬虫的方法,调用时候传入从每一个URL传回的Response对象作为参数,response将会是parse方法的唯一的一个参数,
这个方法负责解析返回的数据、匹配抓取的数据(解析为item)并跟踪更多的URL。
settings.py: 项目配置文件
7. 简述Request方法中callback参数的作用
回调方法 2. ... 调用这一个接口完成后就调用参数中转入的回调实例里的函数
8. 简述Request方法中meta参数的作用
主要是传递数据使用
首先我们要知道meta是一个字典,它的主要作用是用来传递数据的,meta = {‘key1’:value1},
如果想在下一个函数中取出value1, 只需得到上一个函数的meta[‘key1’]即可, 因为meta是随
着Request产生时传递的,下一个函数得到的Response对象中就会有meta,即response.meta.
9. 简述下载中间件的作用
下载器中间件是介于Scrapy的request/response处理的钩子框架,是用于全局修改Scrapy request和response的一个轻量、底层的系统。 要激活下载器中间件组件,将其加入到 DOWNLOADER_MIDDLEWARES 设置中。 该设置是一个字典(dict),键为中间件类的路径,值为其中间件的顺序(order)。像下面这样 DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.CustomDownloaderMiddleware': 543, }
在中间件 middlewares.py
比如自定义中间件 UA池和代理池
1.在下载中间件中拦截请求
2.将拦截到的请求的请求头信息中的UA进行篡改伪装
3.在配置文件中开启下载中间件
10. 简述如何设置scrapy项目的代理IP
在下载中间件中拦截请求
11. 简述CrawlSpider中链接提取器的作用
#实例化了一个链接提取器对象 #链接提取器:用来提取指定的链接(url) #allow参数:赋值一个正则表达式 #链接提取器就可以根据正则表达式在页面中提取指定的链接 #提取到的链接会全部交给规则解析器 link = LinkExtractor(allow=r'/all/hot/recent/d+')
12. 简述CrawlSpider中规则解析器的作用
#实例化了一个规则解析器对象 #规则解析器接受了链接提取器发送的链接后,就会对这些链接发起请求,获取链接对应的页面内容,就会根据指定的规则对页面内容中指定的数据值进行解析 #callback:指定一个解析规则(方法/函数) #follow:是否将链接提取器继续作用到连接提取器提取出的链接所表示的页面数据中 Rule(link, callback='parse_item', follow=), )
13. 简述scrapy核心组件的工作原理
• 引擎(Scrapy)
用来处理整个系统的数据流处理, 触发事务(框架核心)
scheduler 英 /ˈskedʒʊələ;ˈʃedjuːlə/
• 调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列,
由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
• 下载器(Downloader)
用于下载网页内容,并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
• 爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
• 项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
-----------------------------
Scrapy运行流程 1 引擎访问spider,询问需要处理的URL链接,spider收到请求,将需要处理的URL告诉引擎,然后将URL给引擎处理。 2 引擎通知调度器,调度器得到通知将URL排序入队,并加以处理。 3 引擎通知调度器,调度器将处理好的request返回给引擎 4 引擎接收到request后告诉下载器,按照setting中配置的顺序下载这个request的请求 5 下载器收到请求,将下载好后的东西返回给引擎。如果下载失败,下载器会通知引擎,引擎再通知调度器,调度器收到消息后会记录这个下载失败的request。 6 引擎得到下载好的东西后,通知spider(这里responses默认是交给def parse()函数处理) 7 Spider收到通知后,处理接收的数据 8 Spider处理完数据后返回给引擎两个结果:一个是需要跟进的URL,另一个是获取到的item数据。 9 引擎将接收到的item数据交给管道处理,将需要跟进的URL交给调度器处理。重复循环直到获取完需要的全部信息。
14. 原生scrapy框架为何不能实现分布式
1.scrapy框架是否可以自己实现分布式? - 不可以。原因有二。 其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls列表中的url。(多台机器无法共享同一个调度器) 其二:多台机器爬取到的数据无法通过同一个管道对数据进行统一的数据持久出存储。(多台机器无法共享同一个管道)
15. 简述基于scrapy-redis分布式的流程
https://www.jianshu.com/p/abe51b92c695