今日内容概要
- 后端如何防爬虫
- 持久化
- 全站爬取cnblogs
- 爬虫中间件和下载中间件
- scrapy加代理、cookie、header
- scrapy集成selenium
- 源码去重规则
- 分布式爬虫
- linux介绍
今日内容详细
后端如何防爬虫
1.频率限制(ip,用户)
2.尽量登录后才能访问
3.爬虫可以拿到cookie,token模拟发送请求
3.1 请求头携带发送请求时间(时间戳)--->后端中间件中取出请求头中得时间戳,跟当前时间做比较,如果时间过长,就返回错误
3.2 请求头中带sign签名--->签名自己的规则生成
项目名+时间戳+项目名--->md5摘要
后端:项目名+时间戳+项目名跟传入的sign比较,如果一样
3.3 整个对请求体进行加密
有的只加密了一部分;有的直接全部把请求体加密
app端把请求体全加密---->拦截器取出请求体---->同样秘钥解密
持久化
爬回来,解析完了,想存储,有两种方案。一是使用命令保存到json文件或csv文件中;二是使用pipline存储,可以存储多个位置
1.方案一:(一般不用)parse必须有return值,必须是列表套字典形式--->使用命令,可以保存到json格式中,csv中
scrapy crawl cnblogs -o cnbogs.json #以json形式保存
scrapy crawl cnblogs -o cnbogs.csv #以csv形式保存
2.方案二: 我们用的,使用pipline存储--->可以存到多个位置
1.第一步:在item.py中写一个类
class CnblogsItem(scrapy.Item):
title = scrapy.Field()
url = scrapy.Field()
desc = scrapy.Field()
img = scrapy.Field()
author = scrapy.Field()
create_time = scrapy.Field()
content = scrapy.Field() # 文章详情,展示没有
2.第二步:在pipline.py中写代码,写一个类:open_spide,close_spider,process_item
1.open_spide:开启爬虫会触发
2.close_spider:爬完会触发
3.process_item:每次要保存一个对象会触发
class FirstscrapyFilePipeline:
def open_spider(self, spider):
print('我开了')
self.f=open('a.txt','w',encoding='utf-8')
def close_spider(self, spider):
print('我关了')
self.f.close()
# 这个很重要
def process_item(self, item, spider):
self.f.write(item['title']+'\n')
return item
3.第三步:配置文件配置
ITEM_PIPELINES = {
"myfirstscrapy.pipelines.MyCnblogsPipeline": 300, # 数字越小,优先级越高
}
4.第四步:在解析方法parse中yield item对象
方案一
import scrapy
class CnblogsSpider(scrapy.Spider):
name = "cnblogs"
allowed_domains = ["www.cnblogs.com"]
start_urls = ["https://www.cnblogs.com"]
def parse(self, response):
# print(response.text) # http响应包装成了response
# 需求:解析出所有文章(xpath选择器)
# article.post-item
arctile_list = response.xpath('//article[contains(@class,"post-item")]') # 列表中放对象
print(len(arctile_list))
l=[]
for article in arctile_list:
# a.post - item - title::text
title = article.xpath('.//a/text()').extract_first()
print(title)
# a.post-item-title::attr(href)
url = article.xpath('.//a[contains(@class,"post-item-title")]/@href').extract_first()
print(url)
# p.post-item-summary::text
desc = article.xpath('.//p[contains(@class,"post-item-summary")]/text()').extract()
# print(desc)
real_desc = desc[0].replace('\n','').replace(' ','')
if real_desc:
desc = real_desc
else:
desc = desc[1].replace('\n', '').replace(' ', '')
print(desc)
# img.avatar::attr(src)
img = article.xpath('.//p//img/@src').extract_first()
print(img)
# a.post-item-author>span::text
author = article.xpath('.//section/footer/a[1]/span/text()').extract_first()
print(author)
# span.post-meta-item>span::text
create_time = article.xpath('.//section/footer/span[1]/span/text()').extract_first()
print(create_time)
l.append({
'title':title,
'url':url,
'desc':desc,
'img':img,
'author':author,
'create_time':create_time
})
print(f"""
文章标题:{title}
文章链接:{url}
文章简介:{desc}
作者头像:{img}
作者昵称:{author}
文章创建时间:{create_time}
""")
return l
"""
ps:在终端注解输入:scrapy crawl cnblogs -o cnbogs.json 或 scrapy crawl cnblogs -o cnbogs.csv
"""
方案二
练习1
item.py
import scrapy
class CnblogsItem(scrapy.Item):
title = scrapy.Field()
url = scrapy.Field()
desc = scrapy.Field()
img = scrapy.Field()
author = scrapy.Field()
create_time = scrapy.Field()
content = scrapy.Field() # 文章详情,展示没有
pipline.py
class MyCnblogsPipeline:
def open_spider(self,spider): # 开启爬虫会触发
print('-------我开了')
# print(spider) #
self.f = open('cnblogs.txt','wt',encoding='utf-8')
def process_item(self,item,spider): #每次要保存一个对象会触发
print('-------我来了')
# item 当前被处理到的在cnblogs.py的parser中yield item
# 存文件
self.f.write('标题:%s,地址:%s\n'%(item['title'],item['url']))
return item #如果不返回,后续的pipline就拿不到了
def close_spider(self,spider): # 爬完会触发
print('我关了')
self.f.close()
Settings.py
# 管道 数字表示优先级,数字越小,优先级越高
ITEM_PIPELINES = {
"myfirstscrapy.pipelines.MyCnblogsPipeline": 300,
}
cnblogs.py
import scrapy
from myfirstscrapy.items import CnblogsItem
class CnblogsSpider(scrapy.Spider):
name = "cnblogs"
allowed_domains = ["www.cnblogs.com"]
start_urls = ["https://www.cnblogs.com"]
def parse(self, response):
item = CnblogsItem()
arctile_list = response.xpath('//article[contains(@class,"post-item")]') # 列表中放对象
print(len(arctile_list))
for article in arctile_list:
# a.post - item - title::text
title = article.xpath('.//a/text()').extract_first()
# print(title)
# a.post-item-title::attr(href)
url = article.xpath('.//a[contains(@class,"post-item-title")]/@href').extract_first()
# print(url)
# p.post-item-summary::text
desc = article.xpath('.//p[contains(@class,"post-item-summary")]/text()').extract()
# print(desc)
real_desc = desc[0].replace('\n','').replace(' ','')
if real_desc:
desc = real_desc
else:
desc = desc[1].replace('\n', '').replace(' ', '')
# print(desc)
# img.avatar::attr(src)
img = article.xpath('.//p//img/@src').extract_first()
# print(img)
# a.post-item-author>span::text
author = article.xpath('.//section/footer/a[1]/span/text()').extract_first()
# print(author)
# span.post-meta-item>span::text
create_time = article.xpath('.//section/footer/span[1]/span/text()').extract_first()
# print(create_time)
item['title'] = title
item['url'] = url
item['desc'] = desc
item['img'] = img
item['author'] = author
item['create_time'] = create_time
yield item
全站爬取cnblogs
1.需求:继续爬取下一页并且爬取文章详情
1.步骤:
1.Request创建:在parse中的for循环中,创建Request对象时,传入meta
yield Request(url=url, callback=self.detail_parse,meta={'item':item})
ps:item对象一定要在for循环中创建,否则,当前页面都用同一个item导致同一页数据都一样
2.在parser_detail中取出来
item=response.meta.get('item')
Response对象:detail_parse中,通过response取出meta取出item,把文章详情写入
def parser_detail(self,response):
# content = response.css('#cnblogs_post_body').extract_first()
item=response.meta.get('item')
content=str(response.xpath('//div[@id="cnblogs_post_body"]').extract_first())
item['content']=content
yield item
cnblogs.py
import scrapy
from myfirstscrapy.items import CnblogsItem
from scrapy.http.request import Request
class CnblogsSpider(scrapy.Spider):
name = "cnblogs"
allowed_domains = ["www.cnblogs.com"]
start_urls = ["https://www.cnblogs.com"]
def parse(self, response):
# item = CnblogsItem() # #会有问题,是个引用类型
arctile_list = response.xpath('//article[contains(@class,"post-item")]') # 列表中放对象
print(len(arctile_list))
for article in arctile_list:
item = CnblogsItem()
title = article.xpath('.//a/text()').extract_first()
url = article.xpath('.//a[contains(@class,"post-item-title")]/@href').extract_first()
desc = article.xpath('.//p[contains(@class,"post-item-summary")]/text()').extract()
real_desc = desc[0].replace('\n','').replace(' ','')
if real_desc:
desc = real_desc
else:
desc = desc[1].replace('\n', '').replace(' ', '')
img = article.xpath('.//p//img/@src').extract_first()
author = article.xpath('.//section/footer/a[1]/span/text()').extract_first()
create_time = article.xpath('.//section/footer/span[1]/span/text()').extract_first()
item['title'] = title
item['url'] = url
item['desc'] = desc
item['img'] = img
item['author'] = author
item['create_time'] = create_time
# yield item
yield Request(url = url,callback=self.parse_detail,meta={'item':item}) # # 详情地址,继续爬取,详情页面解析用parser_detail
# 解析出下一页地址,然后yield一个对象;请求对象:解析出要爬去的地址
next = "https://www.cnblogs.com" +response.css('#paging_block > div > a:last-child::attr(href)').extract_first()
print(next) # 拿到地址,继续爬取,组装成一个Request对象
yield Request(url=next, callback=self.parse) # 下一页地址,继续爬取,解析还是用parse
def parse_detail(self,response):
content = str(response.css('#cnblogs_post_body').extract_first())
# content=response.xpath('//*[@id="cnblogs_post_body"]').extract_first()
item = response.meta.get('item')
item['content']=content
yield item
items.py
import scrapy
class CnblogsItem(scrapy.Item):
title = scrapy.Field()
url = scrapy.Field()
desc = scrapy.Field()
img = scrapy.Field()
author = scrapy.Field()
create_time = scrapy.Field()
content = scrapy.Field() # 文章详情,展示没有
pipelines.py
class MyCnblogsMysqlPipline:
def open_spider(self,spider): # 开启爬虫会触发
self.count = 0
print('-------我开了')
self.conn=pymysql.connect(
user="root",
password="ln1998151125",
host='127.0.0.1',
database="cnblogs",
port=3306,
)
self.cursor=self.conn.cursor()
def process_item(self,item,spider): #每次要保存一个对象会触发
print('-------我来了========')
self.count +=1
print(self.count)
# item 当前被处理到的在cnblogs.py的parser中yield item
# 存文件
sql = 'insert into test (title,url,`desc`,img,author,create_time,content) values (%s,%s,%s,%s,%s,%s,%s)'
self.cursor.execute(sql,args=[item['title'],item['url'],item['desc'],item['img'],item['author'],item['create_time'],item['content']])
self.conn.commit()
return item
def close_spider(self,spider): # 爬完会触发
print('我关了')
self.cursor.close()
self.conn.close()
Settings.py
# 管道 数字表示优先级,数字越小,优先级越高
ITEM_PIPELINES = {
"myfirstscrapy.pipelines.MyCnblogsPipeline": 300,
"myfirstscrapy.pipelines.MyCnblogsMysqlPipline": 301,
}
爬虫中间件和下载中间件
1.爬虫中间件:爬虫和引擎之间
用的很少,了解即可
2.下载中间件:引擎和下载器之间
用的多,能干啥?
进来request对象
加代理
加cookie
加请求头
出去response对象:修改响应对象,最后进入到爬虫的parser中就是修改后的response
3.爬虫中间件 (了解) middlewares.py
class MysfirstscrapySpiderMiddleware:
@classmethod
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
def process_spider_input(self, response, spider):
return None
def process_spider_output(self, response, result, spider):
for i in result:
yield i
def process_spider_exception(self, response, exception, spider):
pass
def process_start_requests(self, start_requests, spider):
for r in start_requests:
yield r
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
# 下载中间件
class MysfirstscrapyDownloaderMiddleware:
@classmethod
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
# 请求来了执行
def process_request(self, request, spider):
print(request.url)
"""
返回值可以是如下:
return None:继续处理本次请求,执行执行下一个中间件的process_request
return Response:执行当前中间件的process_response回去,进入到引擎,被调度,进入第6步,返回到爬虫的解析方法中
return a Request:直接返回,给引擎,被调度,进入第2步,进入调度器等待下次被调度爬取
raise IgnoreRequest:执行 process_exception
"""
return None
# 请求走了
def process_response(self, request, response, spider):
"""
返回如下:
return Response :继续往后走,进入到引擎,被调度到爬虫中解析
return Request :进入到引擎,被调度进调度器
- or raise IgnoreRequest:会执行process_exception
"""
return response
def process_exception(self, request, exception, spider):
pass
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
4.在配置文件中配置
# 下载中间件
DOWNLOADER_MIDDLEWARES = {
"myfirstscrapy.middlewares.MyfirstscrapyDownloaderMiddleware": 543,
}
scrapy加代理,cookie,header
加代理
1.在下载中间件的def process_request(self, request, spider)中写代码
1.第一步:
-在下载中间件写process_request方法
def get_proxy(self):
import requests
res = requests.get('http://127.0.0.1:5010/get/').json()
if res.get('https'):
return 'https://' + res.get('proxy')
else:
return 'http://' + res.get('proxy')
def process_request(self, request, spider):
request.meta['proxy'] = self.get_proxy()
return None
2.第二步:代理可能不能用,会触发process_exception,在里面写
def process_exception(self, request, exception, spider):
print('-----',request.url) # 这个地址没有爬
return request
加cookie
def process_request(self, request, spider):
print(request.cookies)
request.cookies['name']='lqz'
return None
修改请求头
加cookie
def process_request(self, request, spider):
print(request.cookies)
request.cookies['name']='na'
return None
修改请求头
# 动态生成User-agent使用
def process_request(self, request, spider):
# fake_useragent模块
from fake_useragent import UserAgent
ua = UserAgent()
request.headers['User-Agent']=str(ua.random)
print(request.headers)
return None
scrapy集成selenium
1.使用scrapy默认下载器--->类似于requests模块发送请求,不能执行js,有的页面拿回来数据不完整
2.想在scrapy中集成selenium,获取数据更完整,获取完后,自己组装成 Response对象,就会进爬虫解析,现在解析的是使用selenium拿回来的页面,数据更完整
3.集成selenium 因为有的页面,是执行完js后才渲染完,必须使用selenium去爬取数据才完整
4.注意:
保证整个爬虫中,只有一个浏览器器
只要爬取 下一页这种地址-->使用selenium;爬取详情--->继续使用原来的
步骤
1.第一步:在爬虫类中写
from selenium import webdriver
class CnblogsSpider(scrapy.Spider):
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
bro.implicitly_wait(10)
def close(spider, reason):
spider.bro.close() #浏览器关掉
2.第二步:在中间件中
def process_request(self, request, spider):
# 爬取下一页这种地址--->用selenium,但是文章详情,就用原来的
if 'sitehome/p' in request.url:
spider.bro.get(request.url)
from scrapy.http.response.html import HtmlResponse
response = HtmlResponse(url=request.url, body=bytes(spider.bro.page_source, encoding='utf-8'))
return response
else:
return None
源码去重规则(布隆过滤器)
1.源码去重规则:如果爬取过的地址,就不会再爬了
2.调度器可以去重----->使用了集合
要爬取的Request对象,在进入到scheduler调度器排队之前,先执行enqueue_request,它如果return False,这个Request就丢弃掉,不爬了
如何判断这个Request要不要丢弃掉,执行了self.df.request_seen(request),它来决定的----->RFPDupeFilter类中的方法---->request_seen--->会返回True或False---->如果这个request在集合中,说明爬过了,就return True,如果不在集合中,就加入到集合中,然后返回False
3.调度器源码
from scrapy.core.scheduler import Scheduler
# 这个方法如果return True表示这个request要爬取,如果return False表示这个网址就不爬了(已经爬过了)
def enqueue_request(self, request: Request) -> bool:
# request当次要爬取的地址对象
if self.df.request_seen(request):
# 有的请情况,在爬虫中解析出来的网址,不想爬了,就就可以指定
# yield Request(url=url, callback=self.detail_parse, meta={'item': item},dont_filter=True)
# 如果符合这个条件,表示这个网址已经爬过了
return False
return True
# self.df 去重类 是去重类的对象 RFPDupeFilter
-在配置文件中如果配置了:DUPEFILTER_CLASS = 'scrapy.dupefilters.RFPDupeFilter'表示,使用它作为去重类,按照它的规则做去重
-RFPDupeFilter的request_seen
def request_seen(self, request: Request) -> bool:
# request_fingerprint 生成指纹
fp = self.request_fingerprint(request) #request当次要爬取的地址对象
#判断 fp 在不在集合中,如果在,return True
if fp in self.fingerprints:
return True
#如果不在,加入到集合,return False
self.fingerprints.add(fp)
return False
# 传进来是个request对象,生成的是指纹
-爬取的网址:https://www.cnblogs.com/teach/p/17238610.html?name=lqz&age=19
-和 https://www.cnblogs.com/teach/p/17238610.html?age=19&name=lqz
-它俩是一样的,返回的数据都是一样的,就应该是一条url,就只会爬取一次
-所以 request_fingerprint 就是来把它们做成一样的(核心原理是把查询条件排序,再拼接到后面)
4.生成指纹,指纹是什么? 生成的指纹放到集合中去重
www.cnblogs.com?name=lqz&age=19
www.cnblogs.com?age=19&name=lqz
上面的两种地址生成的指纹是一样的
# 测试指纹
from scrapy.utils.request import RequestFingerprinter
from scrapy import Request
fingerprinter = RequestFingerprinter()
request1 = Request(url='http://www.cnblogs.com?name=lqz&age=20')
request2 = Request(url='http://www.cnblogs.com?age=20&name=lqz')
res1 = fingerprinter.fingerprint(request1).hex()
res2 = fingerprinter.fingerprint(request2).hex()
print(res1)
print(res2)
5.集合去重,集合中放
# a一个bytes
# 假设爬了1亿条url,放在内存中,占空间非常大
a6af0a0ffa18a9b2432550e1914361b6bffcff1a
a6af0a0ffa18a9b2432550e191361b6bffc34f1a
6.想一种方式,极小内存实现去重--->布隆过滤器
总结:scrapy的去重规则
1.根据配置的去重类RFPDupeFilter的request_seen方法,如果返回True,就不爬了,如果返回False就爬
后期咱们可以使用自己定义的去重类,实现去重
2.更小内存实现去重
-如果是集合:存的数据库越多,占内存空间越大,如果数据量特别大,可以使用布隆过滤器实现去重
3.布隆过滤器:https://zhuanlan.zhihu.com/p/94668361
bloomfilter:是一个通过多哈希函数映射到一张表的数据结构,能够快速的判断一个元素在一个集合内是否存在,具有很好的空间和时间效率。(典型例子,爬虫url去重)
原理: BloomFilter 会开辟一个m位的bitArray(位数组),开始所有数据全部置 0 。当一个元素(www.baidu.com)过来时,能过多个哈希函数(h1,h2,h3....)计算不同的在哈希值,并通过哈希值找到对应的bitArray下标处,将里面的值 0 置为 1 。
4.Python中使用布隆过滤器
测试布隆过滤器,可以自动扩容指定错误率,底层数组如果大于了错误率会自动扩容
from pybloom_live import ScalableBloomFilter
bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
url = "www.cnblogs.com"
url2 = "www.liuqingzheng.top"
bloom.add(url)
bloom.add(url2)
print(url in bloom)
print(url2 in bloom)
from pybloom_live import BloomFilter
bf = BloomFilter(capacity=10)
url = 'www.baidu.com'
bf.add(url)
bf.add('aaaa')
bf.add('ggg')
bf.add('deww')
bf.add('aerqaaa')
bf.add('ae2rqaaa')
bf.add('aerweqaaa')
bf.add('aerwewqaaa')
bf.add('aerereweqaaa')
bf.add('we')
print(url in bf)
print("wa" in bf)
5.如果有去重的情况,就可以使用集合--->但是集合占的内存空间大,如果到了亿级别的数据量,想一种更小内存占用,而去重的方案----?布隆过滤器
布隆过滤器:通过不同的hash函数,加底层数组实现的极小内存去重
python中如何使用:pybloom_live
-指定错误率
-指定大小
6.使用redis实现布隆过滤器
-编译redis--->把第三方扩展布隆过滤器编译进去,才有这个功能
-https://zhuanlan.zhihu.com/p/94668736
7.重写scrapy的过滤类
分布式爬虫
1.原来scrapy的Scheduler维护的是本机的任务队列(待爬取的地址)+本机的去重队列(放在集合中)--->在本机内存中
2.如果把scrapy项目,部署到多台机器上,多台机器爬取的内容是重复的
所以实现分布式爬取的关键就是,找一台专门的主机上运行一个共享的队列比如Redis,
然后重写Scrapy的Scheduler,让新的Scheduler到共享队列存取Request,并且去除重复的Request请求,所以总结下来,实现分布式的关键就是三点:
1.多台机器共享队列
2.重写Scheduler,让其无论是去重还是任务都去访问共享队列
3.为Scheduler定制去重规则(利用redis的集合类型)
3.scrapy-redis实现分布式爬虫
公共的去重
公共的待爬取地址队列
4.使用步骤
1.把之前爬虫类,继承class CnblogsSpider(RedisSpider):
2.去掉起始爬取的地址,加入一个类属性
class CnblogsSpider(RedisSpider):
name = "cnblogs"
allowed_domains = ["www.cnblogs.com"] # 允许爬取的域
# 去掉起始爬取的地址--->起始爬取的地方,统一放到redis中了
# 3 设置一个类属性 redis_key,随便起名字
redis_key = 'myspider:start_urls' # redis列表的key,后期我们需要手动插入起始地址
3.配置文件中配置
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # scrapy redis去重类,使用redis的集合去重
# 不使用原生的调度器了,使用scrapy_redis提供的调度器,它就是使用了redis的列表
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
REDIS_HOST = 'localhost' # 主机名
REDIS_PORT = 6379 # 端口
ITEM_PIPELINES = {
# 'mysfirstscrapy.pipelines.MyCnblogsPipeline': 300,
'mysfirstscrapy.pipelines.MyCnblogsMySqlPipeline': 301,
'scrapy_redis.pipelines.RedisPipeline': 400,
}
4.再不同多台机器上运行scrapy的爬虫,就实现了分布式爬虫
Linux介绍
文档:https://zhuanlan.zhihu.com/p/429509333
1. 什么是操作系统
Operating System(操作系统) 简称OS
Windows,MacOS,Linux都是操作系统
2.什么是BootLoader
在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境
一般在计算机启动的时候,选择启动Linux还是Windows的BootLoader是GRUB
如果没有选择,GRUB会在几秒后启动默认的操作系统
双操作系统启动流程如下图
3.Linux和Windows的软件
windows系统下的软件,不能在Linux上运行,Linux上的软件不能在windows上运行
Linux上的软件,基本上都是免费的
Linux上软件,更新更频繁,且免费
Linux上软件,效率更高
4.操作系统发展
# 1984年,微软发布了第一个操作系统-->MS-DOS
-1980年,西雅图电脑产品公司(Seattle Computer Products)的一名24岁的程序员蒂姆·帕特森(Tim Paterson)花费了四个月时间编写出了86-DOS操作系统
-在1981年7月27日,比尔盖茨完全获得了一个叫做Seattle Computer Systems(西雅图计算机系统公司)的“Quick and Dirty Operating System”,简称QDOS的授权,花费5w美元。后来该操作系统被称作MS-DOS
# 同时代,1969年(1970年,计算机元年)起,也有一款比较出名的操作系统叫Unix
-1968年 Multics项目
-MIT、Bell实验室、美国通用电气有限公司走到了一起,致力于开发Multics项目。到后期由于开发进度不是很好,MIT和Bell实验室相继离开这个项目的开发,最终导致项目搁浅。
-1970年(Unix元年,时间戳) Unix诞生
-当时在开发Multics项目的时候,实验室中有一个开发成员开发了一款游戏(travel space:遨游太空),因为两个实验室相继离开项目开发,导致这名开发人员没法玩游戏,后来他提议组织人员重新在Multics项目之上重新的开发,也就出现了1970年的Unix。当时Unix操作系统是使用的汇编语言(机器语言)开发的。
-1973年 用C语言重写Unix
-因为汇编语言有一个最大的局限性:对于计算机硬件过于依赖。导致移植性不好,所以后期在1973年使用了C语言对其进行重新开发。
-1975年 Bell实验室允许大学使用Unix。
-1975年,bell实验室允许大学使用Unix操作系统用于教学作用,而不允许用于商业用途。
# 在1984年,微软发布DOS的同时,理查德·马修·斯托曼(Richard Matthew Stallman, RMS)创立了GUN项目
-GNU项目 (牛羚),GNU is not Unix的缩写,目的是创建一个类Unix的操作系统,因为unix不是免费的,商业收费,刚开始学校教学免费使用,后来不免费了,价格比较贵(MacOS是类Unix操作系统)
-理查德·马修·斯托曼:1953年出生,自由软件运动的精神领袖、GNU计划以及自由软件基金会(Free Software Foundation)的创立者、著名黑客,代表作:Emacs编辑器,GCC编译器,GDB调试器
# 自由的和免费的
-自由的意味着源代码必须公开(windows和macos是私有的)
-自由软件大部分是免费的,但是可以复制,修改,出售
# 1991年 Linux的开发作者,Linux之父,李纳斯·托瓦兹。Linux诞生时是荷兰在校大学生。
-1991年 0.0.1版本
-李纳斯当时学校使用的就是Unix操作系统,然后其对系统的底层代码进行了修改,放到了学校为学生开放的网站上,原先他把文件命名写成了Linus’s Unix,后期网络管理发现之后觉得这个名字不好,自己手动的将名字改成Linux。随后其他同学下载之后发现这个版本还是挺好用的,随后都把自己代码贡献给李纳斯。
-1992年 0.0.2版本
-1994年 1.0版本
-2003年 2.6版本
# Linux吉祥物企鹅
-李纳斯以前在澳大利亚被一只企鹅咬过
7.Linux和GNU的联系
这两个项目是互补的,Linus其实就写了一个类Unix内核
1991年GNU项目已经创建了不少操作系统外网软件,比如cp命令,rm命令,GCC,GDB等--》好比Linux就是人的骨骼,GUN就是血肉
# 后来完善Linux的工作就由Linus和广大开源社区的黑客们
# GNU项目+Linux内核=完整的操作系统,我们现在叫的Linux一般都是指GUN项目+Linux内核
8. Linux内核
上述所提及的版本号并不是分支版本,而是指Linux的内核版本。
Linux内核网站(现在最新5.x版本,3.x多一些,原来2.x多,现在docker要跑在3上)
https://www.kernel.org/
9.各个操作系统的关系
# MacOS和Linux是基于Unix的意思是复刻了Unix的运行模式,源代码是不通的
# 目前Windows的版本是基于革命性的Windows NT内核
10.Linux发行版和区别
# 因为Linux是开源自由软件,所以基于开源的代码可以定制属于自己的Linux系统,所以Linux的版本特别多
11.Linux不同发行版的区别(centos 乌班图 麒麟。。)
-安装方式不一样,有的简单,有的复杂
-安装应用程序的方式也不一样 (yum apt-get)
-预装的应用程序不一样 ()
12.不同发行版使用的Linux内核一样
-我们现在说的Linux其实都是指的是发行版(Distribution version),就是使用Linux内核加上各种GNU的库文件、应用程序,构造而成的操作系统
-可以想象成同一个人(Linux内核),穿不通的衣服裙子羽绒服(软件)
13.不同发行版有的免费,有的收费
不同发行版
-Red Hat:性能稳定,老牌的Linux发行版,RHEL收费,是red hat的企业版,源代码是开放的,收费是因为长期升级更新服务
-现在Red Hat分两个系列:一个是red hat公司提供收费技术支持的RHEL,另一个是社区开放的免费版Fedora,每半年发行一次,由于发行频繁导致性能不太稳定,企业一般不选用Fedora
-Centos:算是RHEL的克隆版,社区企业级操作系统, 改与Redhat, 完全开源,兼具社区和企业特性,融合了Fedora和redhat优点,长期支持,大规模使用稳定,企业普遍使用,特别适合做服务器
-Deepin:深度,中国发行
-Debian:迄今为止,最遵循GUN规范的Linux系统
-Ubuntu :Debian一个后继或分支,社区维护, 现在主要做手机系统和电脑桌面系统。
graph TD;
Linux--> Debian
Linux--> RedHat
Linux--> Deepin深度
RedHat--> RHEL
RedHat--> CentOS
RedHat--> Fedora