赞
踩
一、爬虫的定义:程序或者脚本---》自动的爬取万维网的数据的程序或者脚本。 二、爬虫可以解决的问题: (1)解决冷启动问题。 (2)搜索引擎的根基。---通用爬虫。 (3)帮助机器学习建立知识图谱。 (4)制作各种比价软件。 三、爬虫工程师的进阶之路: 初级爬虫工程师 1.web 前端的知识: HTML、CSS、JavaSc1ipt、 DOM、 DHTML 、Ajax、jQuery、json 等; 2、正则表达式, 能提取正常一般网页中想要的信息,比如某些特殊的文字, 链接信息, 知道什么是懒惰, 什么是贪婪型的正则; 3、会使用 XPath 等获取一些DOM 结构中的节点信息; 4、知道什么是深度优先, 广度优先的抓取算法, 及实践中的使用规则; 5、能分析简单网站的结构, 会使用urllib或requests 库进行简单的数据抓取。 中级爬虫工程师: 1、了解什么事HASH,会简单地使用MD5,SHA1等算法对数据进行HASH一遍存储 2、熟悉HTTP,HTTPS协议的基础知识,了解GET,POST方法,了解HTTP头中的信息,包括返回状态码,编码,user-agent,cookie,session等 3、能设置user-agent进行数据爬取,设置代理等 4、知道什么事Request,什么事response,会使用Fiddler等工具抓取及分析简单地网络数据包;对于动态爬虫,要学会分析ajax请求,模拟制造post数据包请求,抓取客户端session等信息,对于一些简单的网站,能够通过模拟数据包进行自动登录。 5、对于一些难搞定的网站学会使用phantomjs+selenium抓取一些动态网页信息 6、并发下载,通过并行下载加速数据爬取;多线程的使用。 高级爬虫工程师: 1、能够使用Tesseract,百度AI,HOG+SVM,CNN等库进行验证码识别。 2、能使用数据挖掘技术,分类算法等避免死链。 3、会使用常用的数据库进行数据存储,查询。比如mongoDB,redis;学习如何通过缓存避免重复下载的问题。 4、能够使用机器学习的技术动态调整爬虫的爬取策略,从而避免被禁IP封禁等。 5、能使用一些开源框架scrapy,scrapy-redis等分布式爬虫,能部署掌控分布式爬虫进行大规模数据爬取。 四、搜索引擎 1、什么是搜索引擎: 搜索引擎通过特定算法,从互联网上获取网页信息,将其保存到本地,为用户提供检索服务的一种程序。 2、搜索引擎的组成:搜索引擎主要是是由通用爬虫组成的。 (1)通用爬虫:将互联网上的网页信息【整体】爬取下来的爬虫程序。 (2)搜索引擎的工作步骤: 1、抓取网页 2、数据存储 3、预处理 提取文字 中文分词 消除噪音 。。。 4、设置网站排名(访问量),为用户提供检索服务。 (3)为什么搜索引擎可以爬取所有的网页?---搜索引擎的通用是如何来爬取所有网页的。 一个网页就是一个url,这个问题其实在问,【url的获取来源】。 url的获取来源: 1、新网站会主动提交网址给搜索引擎。 2、网页中的一些外链,这些url全部都会加入到通用爬虫的爬取队列。 3、搜索引擎和dns解析服务商合作,如果有新网站注册,搜索引擎就可拿到网址。 3、通用爬虫的缺陷: (1)通用爬虫是爬取整个网页,但是网页中90%的内容基本是没用。 (2)不能满足不同行业,不同人员的不同需求。 (3)只能获取文字,不能获取音频,视频,文档等信息。 (4)只能通过关键字查询,无法通过语义查询。 4、聚焦爬虫:在实施网页抓取的过程中,【会对内容进行筛选】,尽量保证只抓取与【需求相关】的信息的爬虫程序。 五、robots协议: 定义:网络爬虫排除标准 作用:告诉搜索引擎那些可以爬那些不能爬。 六、http协议 1、什么是http协议: 是一种规范——————>约束发布和接受html的规范。 2、http和https。 http:超文本传输协议。 https:安全版的http协议。---ssl--- 对称加密---密钥 非对称---私钥+公钥 数字签证--- 3、https:443 http:80 Upgrade-Insecure-Requests: 1:可以将http升级成https请求。 4、http的特点: (1)应用层协议。 (2)无连接:http每次发送请求和响应的过程都是独立。 在http 1.0以后,有请求头:connection:keep-alive:客户端和服务建立长连接。 (3)无状态:http协议不记录状态。 cookie和session做到请求状态的记录。 cookie是在客户端保存,session是在服务器保存。 5、url:统一资源定位符。 (1)主要作用:用来定位互联网上的任意资源的位置。 (2)为什么url可以定位任意资源? 组成:https://www.baidu.com/index.html?username=123&password=abc#top scheme:协议---https netloc:网络地址:ip:port---www.baidu.com 通过ip定位电脑,通过port定位应用。 192.168.92.10: 代理ip:ip:port path:资源路径。---index.html query:请求参数:---?后面的内容username=123&password=abc fragment:锚点----top 原因:url包含netloc可以定位电脑,path定位资源,这样就可以找到任意在互联网上的信息。 (3)特殊符号: ?:后面就是请求参数 &:连接请求参数 #:锚点----如果url中有锚点,在爬虫程序中尽量去除。 6、 http的工作过程: (1)地址解析: 将url的所有组成部分分别解析出来。 (2)封装http请求数据包。 将第一步解析出来的信息进行装包。---http数据包。 (3)封装tcp数据包,通过三次握手建立tcp。 (4)客户端发送请求 (5)服务发送响应 (6)关闭tcp连接。 7、当我们在浏览器输入一个url,浏览器加载出这个页面,中间做了哪些事? (1)客户端解析url,封装数据包,建立连接,发送请求。 (2)服务器返回url对应资源文件给客户端,比如:index.html。 (3)客户端检查index.html是否有静态资源(引用外部文件),比如js,css,图片。有的话再分别发送请求,来获取这些静态资源。 (4)客户端获取所有静态资源,通过html语法,完全将index.html页面显示出来。 8、 http的请求方法: get请求:get(获取)-->获取服务器的指定资源--->涉及到筛选一些信息--->请求参数:主要拼接在url中。--->不安全(别人可以通过url获取信息)--->请求参数的大小受限。 post请求:post(邮递)--->向服务器传递数据--->请求数据是放在请求实体中的--->安全--->大小不受限。 9、客户端请求 (1)组成:请求行、请求头、空行、请求数据(实体) 请求头:请求方法;host地址,http协议版本。 (2)请求头: user-agent:客户端标识。 accept:允许传入的文件类型。 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Referer:标识产生请求的网页来自于哪个网页。 防盗链 cookie:cookie信息。---现在很多网站,必须封装cookie才给数据。 post请求重要的请求头: content-type:post请求的数据类型 content-length:post请求数据的长度。 ajax请求必须封装的头: x-requested-with:xmlhttprequest 10、服务器响应: (1)组成:状态行、响应头、空行、响应正文。 (2)响应头: Content-Type:text/html;charset=UTF-8 (3)状态码(面试常考): 1XX:表示服务器成功接收部分请求,还需要发剩余请求才能处理整个过程。(处理了一半) 2XX:表示服务器成功接收请求并处理完整个过程。(成功) 200成功 3XX:为了完成请求,客户端需要进一步细化请求。 302:重定向 304:使用缓存资源 4XX:客户端请求错误。(url写错了) 404:服务器无法找到请求内容。 403:服务器拒绝访问,权限不够。 5XX:服务器错误。 502:服务器错误 500:请求未完成,服务器遇到不可知问题。 ----------------------------------------------------------------------- 1.笔记 2. 用【递归】的方法实现斐波那契数列。 1,1,2,3,5,8,13.。。。 用【递归】的方法获取一个list的最大值和最小值,return(最大值,最小值)
1、斐波那契数列: def F(n): if n==1 or n==2: return 1 return F(n-1)+F(n-2) 2、 def max_min(L,start,end): if end-start <=1: return (max(L[start],L[end]),min(L[start],L[end])) max1,min1 = max_min(L,start,(start+end)//2) max2,min2 = max_min(L,(start+end)//2+1,end) return (max(max1,max2),min(min1,min2)) ================================================================================== 第二讲requests模块 文档:从 pythoneer 到 pythonista 的100个模... 链接:http://note.youdao.com/noteshare?id=2b95bb3651c21af80ca1936f8ecb1e0f&sub=635CA99241664308947C4F3BC1B5DDBF 1、使用步骤: #1.导包 import requests #2、确定基础url base_url = 'https://www.baidu.com' #3、发送请求,获取响应 response = requests.get(base_url) #4、处理响应内容 2、requests.get()---get请求方法参数详解 (1)requests.get( url=请求url, headers =请求头字典, params = 请求参数字典。 timeout = 超时时长, )---->response对象 (2)response对象的属性: 服务器响应包含:状态行(协议,状态码)、响应头,空行,响应正文 (1)响应正文: 字符串格式:response.text bytes类型:response.content (2)状态码:response.status_code (3)响应头:response.headers(字典) response.headers['cookie'] (4)响应正文的编码:response.encoding response.text获取到的字符串类型的响应正文,其实是通过下面的步骤获取的: response.text = response.content.decode(response.encoding) (5)乱码问题的解决办法: 产生的原因:编码和解码的编码格式不一致造成的。 str.encode('编码')---将字符串按指定编码解码成bytes类型 bytes.decode('编码')---将bytes类型按指定编码编码成字符串。 a、response.content.decode('页面正确的编码格式') <meta http-equiv="content-type" content="text/html;charset=utf-8"> b、找到正确的编码,设置到response.encoding中 response.encoding = 正确的编码 response.text--->正确的页面内容。 (3)get请求项目总结: a、没有请求参数的情况下,只需要确定url和headers字典。 b、get请求是有请求参数。 在chrome浏览器中,下面找query_string_params,将里面的参数封装到params字典中。 c、分页主要是查看每页中,请求参数页码字段的变化,找到变化规律,用for循环就可以做到分页。 3、post请求: requests.post( url=请求url, headers = 请求头字典, data=请求数据字典 timeout=超时时长 )---response对象 post请求一般返回数据都是json数据。 #解析json数据的方法: (1)response.json()--->json字符串所对应的python的list或者dict (2)用json模块。 json.loads(json_str)---->json_data(python的list或者dict) json.dumps(json_data)--->json_str post请求能否成功,关键看请求参数。 如何查找是哪个请求参数在影响数据获取?--->通过对比,找到变化的参数。 变化参数如何找到参数的生成方式,就是解决这个ajax请求数据获取的途径。 寻找的办法有以下几种: (1)写死在页面。 (2)写在js中。 (3)请求参数是在之前的一条ajax请求的数据里面提前获取好的。 4、代理使用方法 。 (1)代理基本原理: 代理形象的说,他是网络信息中转站。实际上就是在本机和服务器之间架了一座桥。 (2)代理的作用: a、突破自身ip访问限制,可以访问一些平时访问不到网站。 b、访问一些单位或者团体的资源。 c、提高访问速度。代理的服务器主要作用就是中转,所以一般代理服务里面都是用内存来进行数据存储的。 d、隐藏ip。 (3)代理的分类: 1、按照协议进行划分: FTP代理服务器---21,2121 HTTP代理服务器---80,8080 SSL/TLS代理:主要用访问加密网站。端口:443 telnet代理 :主要用telnet远程控制,端口一般为23 2、按照匿名程度: 高度匿名代理:数据包会原封不动转化,在服务段看来,就好像一个普通用户在访问,做到完全隐藏ip。 普通匿名代理:数据包会做一些改动,服务器有可能找到原ip。 透明代理:不但改动数据,还会告诉服务,是谁访问的。 间谍代理:指组织或者个人用于记录用户传输数据,然后进行研究,监控等目的的代理。 (4)在requests模块中如何设置代理? proxies = { '代理服务器的类型':'代理ip' } response = requests.get(proxies = proxies) 代理服务器的类型:http,https,ftp 代理ip:http://ip:port 作业: 1.股吧信息爬取: url:http://guba.eastmoney.com/ 要求: 1、爬取10页内容,保存到guba文件夹下 2、金山词霸:http://www.iciba.com/ 做到和有道相似想过。
一、cookie和session 1、什么是cookie? cookie是指网站为了鉴别用户身份,进行会话跟踪而存储在客户端本地的数据。 2、什么是session? 本来的含义是指有始有终的一些列动作,而在web中,session对象用来在服务器存储特定用户会话所需要的属性及信息。 3、cookie和session产生的原因: cookie和session他们不属于http协议范围,由于http协议是无法保持状态,但实际情况,我们有需压保持一些信息,作为下次请求的条件,所有就产生了cookie和session。 4、cookie的原理: 由服务器产生,当浏览器第一次登录,发送请求到服务器,服务器返回数据,同时生成一个cookie返回给客户端,客户端将这个cookie保存下来。 当浏览器再次访问,浏览器就会自动带上cookie信息,这样服务器就能通过cookie判断是哪个用户在操作。 cookie的缺陷: 1、不安全--保存在客户端。 2、cookie本身最大支持4096(4kb)---存储大小受限。 5、session的工作原理。 正是因为cookie的缺陷,所有产生了另外一种保持状态的方法---session。 服务器存储session,基于http协议的无状态特征,所以服务器就不知道这个访问者是谁。为了解决这个问题,cookie就起到了桥的作用。cookie在使用的过程中,将一个叫做sessionid的字段放到cookie中,将来服务器可以通过这个id字段来查找到底是那个用户的session。 session的生命周期:当用户第一次登陆时创建(生命开始),到session有效期结束(30min)。 6、当我们浏览器关闭,session是否就失效了? 不失效,原因,session失效使用生命周期决定的。 7、cookie组成: name:cookie名称,一旦创建,不可更改。 value:该cookie的值 domain:者cookie可以访问网站域名。 maxage:cookie的失效时间。负数是永不失效。 path:这个使用路径 http字段:cookie的httponly,若次属性为true,则只有http头中会带此cookie。 secrue:该cookie是否仅被使用安全传输协议。 size:cookie的大小 8、会话cookie和持久cookie。 持久化:将内存中的数据存储到硬盘(文件中,数据库)上的过程。 序列化:将对象保存到硬盘上。 会话cookie:保存在内存中cookie,浏览器关闭,cookie失效。 持久cookie:保存在硬盘上的cookie。 9、用requests实现登陆: (1)只需要将【登陆后的】cookie字段封装在请求头中。 (2)使用requests的session对象登陆 session对象可以记录登陆状态。 使用步骤: #session:记录登陆状态 se = requests.Session() data = { 'email':'13016031459', 'password':'12345678', } #此时se对象就保存了登陆信息 se.post(base_url,data = data,headers = headers) ------------------------------------------ index_url = 'http://www.renren.com/971682585/profile' #用se对象来进行个人首页的访问,就可以了 response = se.get(index_url,headers=headers) if '鸣人' in response.text: print('登陆成功!') else: print('登陆失败!') =================================================================================== 第三讲 正则表达式 一、数据的分类 1、结构化数据 特点:数据以行为单位,每一个数据表示一个实体。每一行数据的属性都是一样的。 举例:关系型数据库中的表就是结构化数据。 处理方法:sql 2、半结构化数据 特点:结构化数据的另一种形式。他并不符合关系型数据的特点,不能用关系型模型来描述。但是这种数据包含相关标记,有用来分割语义元素以及字段进行分层的描述。 因此也被称为自描述结构。 举例:xml,html,json,非关系型数据库存储的数据。 处理方法:正则,xpath,jsonpath,css选择器。 3、非结构化数据: 特点:没有固定结构的数据。 举例:文档、图片、音频、视频。 处理方法:常常用二进制形式来做整体保存。 二、json数据 1、json是什么语言的内容? json是js语言中用来用【字符串格式】来保存对象和数组的一种数据结构。 json数据本质上是字符串。 2、js种数组和对象: js的数组:var array = ['aaa','bb','cc']----和python列表对应 js的对象:var obj = { name:'zhangsan',age:10}---和python字典对应。 name = obj.name 3、json数据的解析方法: json模块: 对json字符串的操作“: json.loads(json_str)--->python的list或者dict json.dumps(python的list或者dict) --->json_str ------ 对json文件的操作: json.load(fp)--->从json文件中读出json数据,返回一个python的list或者dict json.dump(python的list或者dict,fp)---》python的list或者dict保存到fp所对应的的文件中。 4、json的意义: json作为数据格式进行传输,具有较高的效率 json不像xml那样具有严格的闭合标签,所以json作为数据传输的时候,他的数据有效占比(有效数据和总数据的比)比xml高很多。 在相同流量下,json比xml作为数据传输,传输的数据更多。 三、正则表达式 1、元字符 (1)匹配边界: ^ ----行首 $-----行尾 (2)重复次数 ?----0次或1次 *----->=0 +---- >=1 { n,}--->=n { n,m}--->=n,<=m { n}----n次 (3)各种字符的表示 []----匹配括号中一个字符,单字符 [abc]--匹配a或者b或者c [a-z0-9A-Z] \d---数字 \w---数字字母下划线 \s---空白字符:换行符、制表符、空格 \b---单词边界 .----除换行符以外的任意字符。 abb?? 2、re模块的使用。 python中re模块是用来做正则处理的。 (1)re模块的使用步骤: #1导包 import re #2将正则表达式编译成一个pattern对象 pattern = re.complie( r'正则表达式', '匹配模式' ) r表示元字符。 #3、用pattern对象来使用相应的方法来匹配内容。 (2)pattern对象的方法: 1.match方法:默认从头开始,只匹配一次,返回一个match对象。 pattern.match( '匹配的目标字符串', start,匹配开始的位置--缺省,start = 0 end,匹配结束的位置--缺省,end = -1 )--->match对象 match对象的属性: match.group()---获取匹配内容。 match.span()--匹配的范围 match.start()---开始位置 match.end()---结束位置 这些方法都可以带一个参数0,但是不能写1,1来表示取分组。 match.group(0)---获取匹配内容。 match.span(0)--匹配的范围 match.start(0)---开始位置 match.end(0)---结束位置 match.groups()--将所有分组的内容,按顺序放到一个元组中返回 2、search方法:从任意位置开始匹配,只匹配一次,返回一个match对象 pattern.search( '匹配的目标字符串', start,匹配开始的位置--缺省,start = 0 end,匹配结束的位置--缺省,end = -1 )--->match对象 3、findall方法:全文匹配,匹配多次,将每次匹配到的结果放到list中返回。 pattern.findall( '匹配的目标字符串', start,匹配开始的位置--缺省,start = 0 end,匹配结束的位置--缺省,end = -1 )--->list 4、finditer方法:全文匹配,匹配多次,返回一个迭代器。 pattern.finditer( '匹配的目标字符串', start,匹配开始的位置--缺省,start = 0 end,匹配结束的位置--缺省,end = -1 )--->list finditer主要用匹配内容比较多的情况下。 5、split:切分,按照正则所表示内容进行切分字符串,返回切分后的每个子串 pattern.split( '要切分的字符串', '切分字数',默认是全部分。 )--->list 6、sub方法:用指定字符串,替换正则表达所匹配到的内容。 pattern.sub( repl,#替换成什么 content,替换什么 count,替换次数,默认替换所有 )--->替换后的字符串。 repl替换内容可以使函数: 函数要求: 1.函数必须有参数,参数就是正则匹配目标字符串所得到的每个match对象。 2、这个函数必须要有返回值,返回值必须是字符串,这个字符串将来就作为替换的内容。 #zhangsan:3000,lisi:4000 #涨工资每个人涨1000 content = 'zhangsan:3000,lisi:4000' p = re.compile(r'\d+') result = p.sub(add,) 7、分组 分组在正则表达式中使用()来表示的,一个括号就是一个分组。 分组的作用: (1)筛选特定内容 (2)可以在同一个表达式中应用前面的分组: \1引用第一分组 (3)findall配合分组 import re content = '<html><h1>正则表达式</h1></html>' p = re.compile(r'<(html)><(h1)>(.*)</\2></\1>') # print(p.search(content).group()) print(p.findall(content))#[('html', 'h1', '正则表达式')] 8、贪婪非贪婪模式 (1)贪婪和非贪婪的却别在于匹配内容的多少。贪婪是尽可能多,非贪婪尽可能的少 (2)贪婪使用*来控制匹配次数的。正则默认是贪婪。默认是取数量控制符的最大值。【】 (3)非贪婪使用?来控制的。 (4)在表示数量控制元字符后面加一个?,此时就表示这个数量控制符取最小值,也就是非贪婪。 9.匹配模式: re.S ----.可以匹配换行符 re.I----忽略大小写。 10、万能正则匹配表达式:.*?(尽可能少匹配任意内容)配合re.S 作业: 1、非负整数 [99,100,-100,-1,90] 2、匹配正整数 3、非正整数 4、qq邮箱: qq号5位---14 5、匹配11位电话号码 1 3-9 6、匹配日期: 2019-12-19 7、长度为8-10的用户密码: 开头字母:必须大写,每一位可以是数字,字母,_ 猫眼其他字段做出来。 股吧: 1、字段 阅读 评论 标题 作者 更新时间 详情页 2.10页内容保存到json文件。
第四讲 xpath 一、什么xml? 1、定义:可扩展标记性语言 2、特点:xml的是具有自描述结构的半结构化数据。 3、作用:xml主要设计宗旨是用来传输数据的。他还可以作为配置文件。 二、xml和html的区别? 1、语法要求不同:xml的语法要求更严格。 (1)html不区分大小写的,xml区分。 (2)html有时可以省却尾标签。xml不能省略任何标签,严格按照嵌套首位结构。 (3)只有xml中有自闭标签(没有内容的标签,只有属性。)<a class='abc'/> (4)在html中属性名可以不带属性值。xml必须带属性值。 (5)在xml中属性必须用引号括起来,html中可以不加引号。 2、作用不同: html主要设计用来显示数据以及更好的显示数据。 xml主要设计宗旨就是用传输数据。 3、标记不同:xml没有固定标记,html的标记都是固定的,不能自定义。 三、xpath 1、什么是xpath? xpath是一种筛选html或者xml页面元素的【语法】。 2、xml和html的一些名词: 元素、标签、属性、内容 3、xml的两种解析方法: dom和sax。 4、xpath语法: (1)选取节点: nodename --- 选取此标签及其所有子标签。 /----从根节点开始选取。 // ----从任意节点开始,不考虑他们的位置。 //book---不管book位置,在xml中取出所有的book标签。 .----当前节点开始找 ..----从父节点 @ ---选取属性 text()---选取内容 (2)谓语:起限定的作用,限定他前面的内容。 []写在谁的后面,就限定谁,一般用于限定元素或者标签。 //book[@class='abc'] 常见的谓语: [@class] ----选取有class [@class='abc'] ---选取class属性为abc的节点。 [contains(@href,'baidu')] ---选取href属性包含baidu的标签 [1] ---选取第一个 [last()]---选取最后一个 [last()-1]---选取倒数第二 [position()>2]---跳过前两个。 book[price>30] (3)通配符 * ---匹配任意节点 @* ---匹配任意属性 (4)选取若干路径 | ---左边和右边的xpath选的内容都要---and 5、lxml模块----python处理xml和html的模块。 (1)解析字符串类型xml。 from lxml import etree text=''' html页面内容 ''' tree = etree.HTML(text)---返回值就是一个element对象 #element对象有xpath方法,可以通过xpath表达式来筛选内容。 #选取class属性为item-1的li下面的a标签的内容 a_contents = tree.xpath('//li[@class="item-1"]/a/text()') 将element对象变成字符串的方法 # html_str = etree.tostring(tree,pretty_print=True).decode('utf-8') # print(type(html_str)) #elment对象xpath放来筛选,返回值都是一个list。 #xpath表达式最后一个是一个元素(标签),list中都是elment元素 #xpath表达式最后一个是属性,list都是属性字符串 #xpath表达式最后一个是内容,list都是内容字符串 (2)解析xml或者html文件: from lxml import etree #parse方法是按照xml的方式来解析,如果语法出问题,就会报错。 html = etree.parse('demo.html') # print(html)#_ElementTree li_texts = html.xpath('//li/a/text()') print(li_texts) 作业: 药网数据爬取: url:https://www.111.com.cn/categories/953710-a0-b0-c31-d0-e0-f0-g0-h0-i0-j2.html 要求:抓取50页 字段:单价,描述,评论数量,详情页链接,(功能主治 ,有效期 ,用法用量) (1)用xpath爬取数据。 (2)将数据保存到excel中
1、path环境变量的作用:
为了让cmd找到各种命令exe工具,配path就找exe所在目录配置就可以了。
C:\Anaconda3\Scripts---找pip.exe
C:\Anaconda3--
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。