python爬虫从入门到实战笔记——第一章爬虫原理和数据爬取

2022年05月11日 阅读数:2
这篇文章主要向大家介绍python爬虫从入门到实战笔记——第一章爬虫原理和数据爬取,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

爬虫原理和数据抓取

1.1 通用爬虫和聚焦爬虫

根据使用场景,网络爬虫可分为 通用爬虫聚焦爬虫 两种.html

通用爬虫

通用网络爬虫 是 捜索引擎抓取系统(Baidu、Google、Yahoo等)的重要组成部分。主要目的是将互联网上的网页下载到本地,造成一个互联网内容的镜像备份。python

通用搜索引擎(Search Engine)工做原理:git

通用网络爬虫 从互联网中搜集网页,采集信息,这些网页信息用于为搜索引擎创建索引从而提供支持,它决定着整个引擎系统的内容是否丰富,信息是否即时,所以其性能的优劣直接影响着搜索引擎的效果。程序员

第一步:抓取网页github

搜索引擎网络爬虫的基本工做流程以下:web

  1. 首先选取一部分的种子URL,将这些URL放入待抓取URL队列;算法

  2. 取出待抓取URL,解析DNS获得主机的IP,并将URL对应的网页下载下来,存储进已下载网页库中,而且将这些URL放进已抓取URL队列。数据库

  3. 分析已抓取URL队列中的URL,分析其中的其余URL,而且将URL放入待抓取URL队列,从而进入下一个循环…编程

    image-20200615093838849

搜索引擎如何获取一个新网站的URL:json

  1. 新网站向搜索引擎主动提交网址:(如百度http://zhanzhang.baidu.com/linksubmit/url

  2. 在其余网站上设置新网站外链(尽量处于搜索引擎爬虫爬取范围)

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

可是搜索引擎蜘蛛的爬行是被输入了必定的规则的,它须要听从一些命令或文件的内容,如标注为nofollow的连接,或者是Robots协议。

Robots协议(也叫爬虫协议、机器人协议等),全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站经过Robots协议告诉搜索引擎哪些页面能够抓取,哪些页面不能抓取,例如:

淘宝网:https://www.taobao.com/robots.txt

腾讯网: http://www.qq.com/robots.txt

第二步:数据存储

搜索引擎经过爬虫爬取到的网页,将数据存入原始页面数据库。其中的页面数据与用户浏览器获得的HTML是彻底同样的。

搜索引擎蜘蛛在抓取页面时,也作必定的重复内容检测,一旦遇到访问权重很低的网站上有大量抄袭、采集或者复制的内容,极可能就再也不爬行。

第三步:预处理

搜索引擎将爬虫抓取回来的页面,进行各类步骤的预处理。

  • 提取文字
  • 中文分词
  • 消除噪音(好比版权声明文字、导航条、广告等……)
  • 索引处理
  • 连接关系计算
  • 特殊文件处理

除了HTML文件外,搜索引擎一般还能抓取和索引以文字为基础的多种文件类型,如 PDF、Word、WPS、XLS、PPT、TXT 文件等。咱们在搜索结果中也常常会看到这些文件类型。

但搜索引擎还不能处理图片、视频、Flash 这类非文字内容,也不能执行脚本和程序。

第四步:提供检索服务,网站排名

搜索引擎在对信息进行组织和处理后,为用户提供关键字检索服务,将用户检索相关的信息展现给用户。

同时会根据页面的PageRank值(连接的访问量排名)来进行网站排名,这样Rank值高的网站在搜索结果中会排名较前,固然也能够直接使用 Money 购买搜索引擎网站排名,简单粗暴。

image-20200425214953842

课外阅读:Google搜索引擎的工做原理

可是,这些通用性搜索引擎也存在着必定的局限性:

  1. 通用搜索引擎所返回的结果都是网页,而大多状况下,网页里90%的内容对用户来讲都是无用的。
  2. 不一样领域、不一样背景的用户每每具备不一样的检索目的和需求,搜索引擎没法提供针对具体某个用户的搜索结果。
  3. 万维网数据形式的丰富和网络技术的不断发展,图片、数据库、音频、视频多媒体等不一样数据大量出现,通用搜索引擎对这些文件无能为力,不能很好地发现和获取。
  4. 通用搜索引擎大多提供基于关键字的检索,难以支持根据语义信息提出的查询,没法准确理解用户的具体需求。

针对这些状况,聚焦爬虫技术得以普遍使用。

聚焦爬虫

聚焦爬虫,是"面向特定主题需求"的一种网络爬虫程序,它与通用搜索引擎爬虫的区别在于: 聚焦爬虫在实施网页抓取时会对内容进行处理筛选,尽可能保证只抓取与需求相关的网页信息。

而下面的要学的网络爬虫,就是聚焦爬虫。

1.2 HTTP和HTTPS

HTTP协议(HyperText Transfer Protocol,超文本传输协议):是一种发布和接收 HTML页面的方法。

HTTPS(Hypertext Transfer Protocol over Secure Socket Layer)简单讲是HTTP的安全版,在HTTP下加入SSL层。

SSL(Secure Sockets Layer 安全套接层)主要用于Web的安全传输协议,在传输层对网络链接进行加密,保障在Internet上数据传输的安全。

  • HTTP的端口号为80
  • HTTPS的端口号为443

HTTP的请求与响应

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

image-20200425215801160

浏览器发送HTTP请求的过程:

  1. 当用户在浏览器的地址栏中输入一个URL并按回车键以后,浏览器会向HTTP服务器发送HTTP请求。HTTP请求主要分为“Get”和“Post”两种方法。
  2. 当咱们在浏览器输入URL http://www.baidu.com 的时候,浏览器发送一个Request请求去获取 http://www.baidu.com 的html文件,服务器把Response文件对象发送回给浏览器。
  3. 浏览器分析Response中的 HTML,发现其中引用了不少其余文件,好比Images文件,CSS文件,JS文件。 浏览器会自动再次发送Request去获取图片,CSS文件,或者JS文件。
  4. 当全部的文件都下载成功后,网页会根据HTML语法结构,完整的显示出来了。

URL(Uniform / Universal Resource Locator的缩写):统一资源定位符,是用于完整地描述Internet上网页和其余资源的地址的一种标识方法。
image-20200425220139851
基本格式:scheme://host[:port#]/path/…/[?query-string][#anchor]

  • scheme:协议(例如:http, https, ftp)
  • host:服务器的IP地址或者域名
  • port#:服务器的端口(若是是走协议默认端口,缺省端口80)
  • path:访问资源的路径
  • query-string:参数,发送给http服务器的数据
  • anchor:锚(跳转到网页的指定锚点位置)

例如:

客户端HTTP请求

URL只是标识资源的位置,而HTTP是用来提交和获取资源。客户端发送一个HTTP请求到服务器的请求消息,包括如下格式:

请求行`、`请求头部`、`空行`、`请求数据

四个部分组成,下图给出了请求报文的通常格式。

image-20200425220230631
一个典型的HTTP请求示例

GET https://www.baidu.com/ HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://www.baidu.com/
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: BAIDUID=04E4001F34EA74AD4601512DD3C41A7B:FG=1; BIDUPSID=04E4001F34EA74AD4601512DD3C41A7B; PSTM=1470329258; MCITY=-343%3A340%3A; BDUSS=nF0MVFiMTVLcUh-Q2MxQ0M3STZGQUZ4N2hBa1FFRkIzUDI3QlBCZjg5cFdOd1pZQVFBQUFBJCQAAAAAAAAAAAEAAADpLvgG0KGyvLrcyfrG-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFaq3ldWqt5XN; H_PS_PSSID=1447_18240_21105_21386_21454_21409_21554; BD_UPN=12314753; sug=3; sugstore=0; ORIGIN=0; bdime=0; H_PS_645EC=7e2ad3QHl181NSPbFbd7PRUCE1LlufzxrcFmwYin0E6b%2BW8bbTMKHZbDP0g; BDSVRTM=0
请求方法
GET https://www.baidu.com/ HTTP/1.1

根据HTTP标准,HTTP请求能够使用多种请求方法。

HTTP 0.9:只有基本的文本 GET 功能。

HTTP 1.0:完善的请求/响应模型,并将协议补充完整,定义了三种请求方法: GET, POST 和 HEAD方法。

HTTP 1.1:在 1.0 基础上进行更新,新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。

HTTP 2.0(未普及):请求/响应首部的定义基本没有改变,只是全部首部键必须所有小写,并且请求行要独立为 :method、:scheme、:host、:path这些键值对。

序号 方法 描述
1 GET 请求指定的页面信息,并返回实体主体。
2 HEAD 相似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
3 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件),数据被包含在请求体中。POST请求可能会致使新的资源的创建和/或已有资源的修改。
4 PUT 从客户端向服务器传送的数据取代指定的文档的内容。
5 DELETE 请求服务器删除指定的页面。
6 CONNECT HTTP/1.1协议中预留给可以将链接改成管道方式的代理服务器。
7 OPTIONS 容许客户端查看服务器的性能。
8 TRACE 回显服务器收到的请求,主要用于测试或诊断。

HTTP请求主要分为GetPost两种方法

  • GET是从服务器上获取数据,POST是向服务器传送数据
  • GET请求参数显示,都显示在浏览器网址上,HTTP服务器根据该请求所包含URL中的参数来产生响应内容,即“Get”请求的参数是URL的一部分。 例如: http://www.baidu.com/s?wd=Chinese
  • POST请求参数在请求体当中,消息长度没有限制并且以隐式的方式进行发送,一般用来向HTTP服务器提交量比较大的数据(好比请求中包含许多参数或者文件上传操做等),请求的参数包含在“Content-Type”消息头里,指明该消息体的媒体类型和编码,

注意:避免使用Get方式提交表单,由于有可能会致使安全问题。 好比说在登录表单中用Get方式,用户输入的用户名和密码将在地址栏中暴露无遗。

经常使用的请求报头
  1. Host (主机和端口号)

Host:对应网址URL中的Web名称和端口号,用于指定被请求资源的Internet主机和端口号,一般属于URL的一部分。

  1. Connection (连接类型)

Connection:表示客户端与服务链接类型

  1. Client 发起一个包含 Connection:keep-alive 的请求,HTTP/1.1使用 keep-alive 为默认值。
  2. Server收到请求后:
    • 若是 Server 支持 keep-alive,回复一个包含 Connection:keep-alive 的响应,不关闭链接;
    • 若是 Server 不支持 keep-alive,回复一个包含 Connection:close 的响应,关闭链接。
    • 若是client收到包含 Connection:keep-alive 的响应,向同一个链接发送下一个请求,直到一方主动关闭链接

keep-alive在不少状况下可以重用链接,减小资源消耗,缩短响应时间,好比当浏览器须要多个文件时(好比一个HTML文件和相关的图形文件),不须要每次都去请求创建链接。

  1. Upgrade-Insecure-Requests (升级为HTTPS请求)

Upgrade-Insecure-Requests:升级不安全的请求,意思是会在加载 http 资源时自动替换成 https 请求,让浏览器再也不显示https页面中的http请求警报。

*HTTPS 是以安全为目标的 HTTP 通道,因此在 HTTPS 承载的页面上不容许出现 HTTP 请求,一旦出现就是提示或报错。*

  1. User-Agent (浏览器名称)

User-Agent:是客户浏览器的名称,之后会详细讲。

  1. Accept (传输文件类型)

Accept:指浏览器或其余客户端能够接受的MIME(Multipurpose Internet Mail Extensions(多用途互联网邮件扩展))文件类型,服务器能够根据它判断并返回适当的文件格式。

举例:

Accept: */*:表示什么均可以接收。

Accept:image/gif:代表客户端但愿接受GIF图像格式的资源;

Accept:text/html:代表客户端但愿接受html文本。

Accept: text/html, application/xhtml+xml;q=0.9, image/*;q=0.8:表示浏览器支持的 MIME 类型分别是 html文本、xhtml和xml文档、全部的图像格式资源。

*q是权重系数,范围 0 =< q <= 1,q 值越大,请求越倾向于得到其“;”以前的类型表示的内容。若没有指定q值,则默认为1,按从左到右排序顺序;若被赋值为0,则用于表示浏览器不接受此内容类型。*

*Text:用于标准化地表示的文本信息,文本消息能够是多种字符集和或者多种格式的;Application:用于传输应用程序数据或者二进制数据。详细请点击*

  1. Referer (页面跳转处)

Referer:代表产生请求的网页来自于哪一个U

RL,用户是从该 Referer页面访问到当前请求的页面。这个属性能够用来跟踪Web请求来自哪一个页面,是从什么网站来的等。

有时候遇到下载某网站图片,须要对应的referer,不然没法下载图片,那是由于人家作了防盗链,原理就是根据referer去判断是不是本网站的地址,若是不是,则拒绝,若是是,就能够下载;

  1. Accept-Encoding(文件编解码格式)

Accept-Encoding:指出浏览器能够接受的编码方式。编码方式不一样于文件格式,它是为了压缩文件并加速文件传递速度。浏览器在接收到Web响应以后先解码,而后再检查文件格式,许多情形下这能够减小大量的下载时间。

举例:Accept-Encoding:gzip;q=1.0, identity; q=0.5, *;q=0

若是有多个Encoding同时匹配, 按照q值顺序排列,本例中按顺序支持 gzip, identity压缩编码,支持gzip的浏览器会返回通过gzip编码的HTML页面。 若是请求消息中没有设置这个域服务器假定客户端对各类内容编码均可以接受。

  1. Accept-Language(语言种类)

Accept-Langeuage:指出浏览器能够接受的语言种类,如en或en-us指英语,zh或者zh-cn指中文,当服务器可以提供一种以上的语言版本时要用到。

  1. Accept-Charset(字符编码)

Accept-Charset:指出浏览器能够接受的字符编码。

举例:Accept-Charset:iso-8859-1,gb2312,utf-8

  • ISO8859-1:一般叫作Latin-1。Latin-1包括了书写全部西方欧洲语言不可缺乏的附加字符,英文浏览器的默认值是ISO-8859-1.
  • gb2312:标准简体中文字符集;
  • utf-8:UNICODE 的一种变长字符编码,能够解决多种语言文本显示问题,从而实现应用国际化和本地化。

若是在请求消息中没有设置这个域,缺省是任何字符集均可以接受。

  1. Cookie (Cookie)

Cookie:浏览器用这个属性向服务器发送Cookie。Cookie是在浏览器中寄存的小型数据体,它能够记载和服务器相关的用户信息,也能够用来实现会话功能,之后会详细讲。

  1. Content-Type (POST数据类型)

Content-Type:POST请求里用来表示的内容类型。

举例:Content-Type = Text/XML; charset=gb2312:

指明该请求的消息体中包含的是纯文本的XML类型的数据,字符编码采用“gb2312”。

服务端HTTP响应

HTTP响应也由四个部分组成,分别是: 状态行消息报头空行响应正文
image-20200425231408722

HTTP/1.1 200 OK
Server: Tengine
Connection: keep-alive
Date: Wed, 30 Nov 2016 07:58:21 GMT
Cache-Control: no-cache
Content-Type: text/html;charset=UTF-8
Keep-Alive: timeout=20
Vary: Accept-Encoding
Pragma: no-cache
X-NWS-LOG-UUID: bd27210a-24e5-4740-8f6c-25dbafa9c395
Content-Length: 180945

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ....

经常使用的响应报头(了解)

理论上全部的响应头信息都应该是回应请求头的。可是服务端为了效率,安全,还有其余方面的考虑,会添加相对应的响应头信息,从上图能够看到:

  1. Cache-Control:must-revalidate, no-cache, private。

这个值告诉客户端,服务端不但愿客户端缓存资源,在下次请求资源时,必需要重新请求服务器,不能从缓存副本中获取资源。

  • Cache-Control是响应头中很重要的信息,当客户端请求头中包含Cache-Control:max-age=0请求,明确表示不会缓存服务器资源时,Cache-Control做为做为回应信息,一般会返回no-cache,意思就是说,“那就不缓存呗”。
  • 当客户端在请求头中没有包含Cache-Control时,服务端每每会定,不一样的资源不一样的缓存策略,好比说oschina在缓存图片资源的策略就是Cache-Control:max-age=86400,这个意思是,从当前时间开始,在86400秒的时间内,客户端能够直接从缓存副本中读取资源,而不须要向服务器请求。
  1. Connection:keep-alive

这个字段做为回应客户端的Connection:keep-alive,告诉客户端服务器的tcp链接也是一个长链接,客户端能够继续使用这个tcp链接发送http请求。

  1. Content-Encoding:gzip

告诉客户端,服务端发送的资源是采用gzip编码的,客户端看到这个信息后,应该采用gzip对资源进行解码。

  1. Content-Type:text/html;charset=UTF-8

告诉客户端,资源文件的类型,还有字符编码,客户端经过utf-8对资源进行解码,而后对资源进行html解析。一般咱们会看到有些网站是乱码的,每每就是服务器端没有返回正确的编码。

  1. Date:Sun, 21 Sep 2016 06:18:21 GMT

这个是服务端发送资源时的服务器时间,GMT是格林尼治所在地的标准时间。http协议中发送的时间都是GMT的,这主要是解决在互联网上,不一样时区在相互请求资源的时候,时间混乱问题。

  1. Expires:Sun, 1 Jan 2000 01:00:00 GMT

这个响应头也是跟缓存有关的,告诉客户端在这个时间前,能够直接访问缓存副本,很显然这个值会存在问题,由于客户端和服务器的时间不必定会都是相同的,若是时间不一样就会致使问题。因此这个响应头是没有Cache-Control:max-age=*这个响应头准确的,由于max-age=date中的date是个相对时间,不只更好理解,也更准确。

  1. Pragma:no-cache

这个含义与Cache-Control等同。

8.Server:Tengine/1.4.6

这个是服务器和相对应的版本,只是告诉客户端服务器的信息。

  1. Transfer-Encoding:chunked

这个响应头告诉客户端,服务器发送的资源的方式是分块发送的。通常分块发送的资源都是服务器动态生成的,在发送时还不知道发送资源的大小,因此采用分块发送,每一块都是独立的,独立的块都能标示本身的长度,最后一块是0长度的,当客户端读到这个0长度的块时,就能够肯定资源已经传输完了。

  1. Vary: Accept-Encoding

告诉缓存服务器,缓存压缩文件和非压缩文件两个版本,如今这个字段用处并不大,由于如今的浏览器都是支持压缩的。

Cookie 和 Session:

服务器和客户端的交互仅限于请求/响应过程,结束以后便断开,在下一次请求时,服务器会认为新的客户端。

为了维护他们之间的连接,让服务器知道这是前一个用户发送的请求,必须在一个地方保存客户端的信息。

Cookie:经过在 客户端 记录的信息肯定用户的身份。

Session:经过在 服务器端 记录的信息肯定用户的身份。

响应状态码

响应状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。

常见状态码:
  • 100~199:表示服务器成功接收部分请求,要求客户端继续提交其他请求才能完成整个处理过程。
  • 200~299:表示服务器成功接收请求并已完成整个处理过程。经常使用200(OK 请求成功)。
  • 300~399:为完成请求,客户需进一步细化请求。例如:请求的资源已经移动一个新地址、经常使用302(所请求的页面已经临时转移至新的url)、307和304(使用缓存资源)。
  • 400~499:客户端的请求有错误,经常使用404(服务器没法找到被请求的页面)、403(服务器拒绝访问,权限不够)。
  • 500~599:服务器端出现错误,经常使用500(请求未完成。服务器遇到不可预知的状况)。
HTTP响应状态码参考:
1xx:信息

100 Continue
服务器仅接收到部分请求,可是一旦服务器并无拒绝该请求,客户端应该继续发送其他的请求。
101 Switching Protocols
服务器转换协议:服务器将听从客户的请求转换到另一种协议。



2xx:成功

200 OK
请求成功(其后是对GET和POST请求的应答文档)
201 Created
请求被建立完成,同时新的资源被建立。
202 Accepted
供处理的请求已被接受,可是处理未完成。
203 Non-authoritative Information
文档已经正常地返回,但一些应答头可能不正确,由于使用的是文档的拷贝。
204 No Content
没有新文档。浏览器应该继续显示原来的文档。若是用户按期地刷新页面,而Servlet能够肯定用户文档足够新,这个状态代码是颇有用的。
205 Reset Content
没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。
206 Partial Content
客户发送了一个带有Range头的GET请求,服务器完成了它。



3xx:重定向

300 Multiple Choices
多重选择。连接列表。用户能够选择某连接到达目的地。最多容许五个地址。
301 Moved Permanently
所请求的页面已经转移至新的url。
302 Moved Temporarily
所请求的页面已经临时转移至新的url。
303 See Other
所请求的页面可在别的url下被找到。
304 Not Modified
未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(通常是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还能够继续使用。
305 Use Proxy
客户请求的文档应该经过Location头所指明的代理服务器提取。
306 Unused
此代码被用于前一版本。目前已再也不使用,可是代码依然被保留。
307 Temporary Redirect
被请求的页面已经临时移至新的url。



4xx:客户端错误

400 Bad Request
服务器未能理解请求。
401 Unauthorized
被请求的页面须要用户名和密码。
401.1
登陆失败。
401.2
服务器配置致使登陆失败。
401.3
因为 ACL 对资源的限制而未得到受权。
401.4
筛选器受权失败。
401.5
ISAPI/CGI 应用程序受权失败。
401.7
访问被 Web 服务器上的 URL 受权策略拒绝。这个错误代码为 IIS 6.0 所专用。
402 Payment Required
此代码尚没法使用。
403 Forbidden
对被请求页面的访问被禁止。
403.1
执行访问被禁止。
403.2
读访问被禁止。
403.3
写访问被禁止。
403.4
要求 SSL。
403.5
要求 SSL 128403.6
IP 地址被拒绝。
403.7
要求客户端证书。
403.8
站点访问被拒绝。
403.9
用户数过多。
403.10
配置无效。
403.11
密码更改。
403.12
拒绝访问映射表。
403.13
客户端证书被吊销。
403.14
拒绝目录列表。
403.15
超出客户端访问许可。
403.16
客户端证书不受信任或无效。
403.17
客户端证书已过时或还没有生效。
403.18
在当前的应用程序池中不能执行所请求的 URL。这个错误代码为 IIS 6.0 所专用。
403.19
不能为这个应用程序池中的客户端执行 CGI。这个错误代码为 IIS 6.0 所专用。
403.20
Passport 登陆失败。这个错误代码为 IIS 6.0 所专用。
404 Not Found
服务器没法找到被请求的页面。
404.0
没有找到文件或目录。
404.1
没法在所请求的端口上访问 Web 站点。
404.2
Web 服务扩展锁定策略阻止本请求。
404.3
MIME 映射策略阻止本请求。
405 Method Not Allowed
请求中指定的方法不被容许。
406 Not Acceptable
服务器生成的响应没法被客户端所接受。
407 Proxy Authentication Required
用户必须首先使用代理服务器进行验证,这样请求才会被处理。
408 Request Timeout
请求超出了服务器的等待时间。
409 Conflict
因为冲突,请求没法被完成。
410 Gone
被请求的页面不可用。
411 Length Required
"Content-Length" 未被定义。若是无此内容,服务器不会接受请求。
412 Precondition Failed
请求中的前提条件被服务器评估为失败。
413 Request Entity Too Large
因为所请求的实体的太大,服务器不会接受请求。
414 Request-url Too Long
因为url太长,服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时,就会发生这种状况。
415 Unsupported Media Type
因为媒介类型不被支持,服务器不会接受请求。
416 Requested Range Not Satisfiable
服务器不能知足客户在请求中指定的Range头。
417 Expectation Failed
执行失败。
423
锁定的错误。



5xx:服务器错误

500 Internal Server Error
请求未完成。服务器遇到不可预知的状况。
500.12
应用程序正忙于在 Web 服务器上从新启动。
500.13
Web 服务器太忙。
500.15
不容许直接请求 Global.asa。
500.16
UNC 受权凭据不正确。这个错误代码为 IIS 6.0 所专用。
500.18
URL 受权存储不能打开。这个错误代码为 IIS 6.0 所专用。
500.100
内部 ASP 错误。
501 Not Implemented
请求未完成。服务器不支持所请求的功能。
502 Bad Gateway
请求未完成。服务器从上游服务器收到一个无效的响应。
502.1
CGI 应用程序超时。 ·
502.2
CGI 应用程序出错。
503 Service Unavailable
请求未完成。服务器临时过载或当机。
504 Gateway Timeout
网关超时。
505 HTTP Version Not Supported
服务器不支持请求中指明的HTTP协议版本

1.3 str和bytes的区别

bytes

bytes对象只负责以二进制字节序列的形式记录所需记录的对象,至于该对象到底表示什么(好比究竟是什么字符)则由相应的编码格式解码所决定

Python2 中

>>> type(b'xxxxx')
<type 'str'>
>>> type('xxxxx')
<type 'str'>

Python3 中

>>> type(b'xxxxx')
<class 'bytes'>
>>> type('xxxxx')
<class 'str'>

bytes是Python 3中特有的,Python 2 里不区分bytes和str。

python3中:
str 使用encode方法转化为 bytes
bytes经过decode转化为str

In [9]: str1='人生苦短,我用Python!'

In [10]: type(str1)
Out[10]: str

In [11]: b=str1.encode()#编码

In [12]: b
Out[12]: b'\xe4\xba\xba\xe7\x94\x9f\xe8\x8b\xa6\xe7\x9f\xad\
xef\xbc\x8c\xe6\x88\x91\xe7\x94\xa8Python!'

In [13]: type(str1.encode())
Out[13]: bytes

bytes转换成str:

In [22]: b
Out[22]: b'\xe4\xba\xba\xe7\x94\x9f\xe8\x8b\xa6\xe7\x9f\xad\
xef\xbc\x8c\xe6\x88\x91\xe7\x94\xa8Python!'

In [23]: type(b)
Out[23]: bytes

In [24]: b.decode()
Out[24]: '人生苦短,我用Python!'

In [25]: type(b.decode())
Out[25]: str

在Python 2中因为不区分str和bytes因此能够直接经过encode()和decode()方法进行编码解码。

而在Python 3中把二者给分开了这个在使用中须要注意。实际应用中在互联网上是经过二进制进行传输,因此就须要将str转换成bytes进行传输,而在接收中经过decode()解码成咱们须要的编码进行处理数据这样无论对方是什么编码而本地是咱们使用的编码这样就不会乱码。

bytearray

bytearray和bytes不同的地方在于,bytearray是可变的。

In [26]: str1
Out[26]: '人生苦短,我用Python!'

In [28]: b1=bytearray(str1.encode())

In [29]: b1
Out[29]: bytearray(b'\xe4\xba\xba\xe7\x94\x9f\xe8\x8b\xa6\xe7\x9f\xad\xef\xbc\x8c\xe6\x88\x91\xe7\x94\xa8Python!')

In [30]: b1.decode()
Out[30]: '人生苦短,我用Python!'

In [31]: b1[:6]=bytearray('生命'.encode())

In [32]: b1
Out[32]: bytearray(b'\xe7\x94\x9f\xe5\x91\xbd\xe8\x8b\xa6\xe7\x9f\xad\xef\xbc\x8c\xe6\x88\x91\xe7\x94\xa8Python!')

In [33]: b1.decode()
Out[33]: '生命苦短,我用Python!'

1.4 Requests的简单应用

Requests: 让 HTTP 服务人类

虽然Python的标准库中 urllib 模块已经包含了日常咱们使用的大多数功能,可是它的 API 使用起来让人感受不太好,而 Requests 自称 “HTTP for Humans”,说明使用更简洁方便。

Requests 惟一的一个非转基因的 Python HTTP 库,人类能够安全享用:)

Requests 继承了urllib的全部特性。Requests支持HTTP链接保持和链接池,支持使用cookie保持会话,支持文件上传,支持自动肯定响应内容的编码,支持国际化的 URL 和 POST 数据自动编码。

requests 的底层实现其实就是 urllib

Requests的文档很是完备,中文文档也至关不错。Requests能彻底知足当前网络的需求,支持Python 2.6–3.5,并且能在PyPy下完美运行。

开源地址:https://github.com/kennethreitz/requests

中文文档 API: http://docs.python-requests.org/zh_CN/latest/index.html

安装方式

利用 pip 安装 或者利用 easy_install 均可以完成安装:

$ pip install requests

$ easy_install requests

基本GET请求(headers参数 和 parmas参数)

1. 最基本的GET请求能够直接用get方法
response = requests.get("http://www.baidu.com/")

# 也能够这么写
# response = requests.request("get", "http://www.baidu.com/")
2. 添加 headers 和 查询参数

若是想添加 headers,能够传入headers参数来增长请求头中的headers信息。若是要将参数放在url中传递,能够利用 params 参数。

import requests

kw = {
   
   'wd':'长城'}

headers = {
   
   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不须要urlencode()
response = requests.get("http://www.baidu.com/s?", params = kw, headers = headers)

# 查看响应内容,response.text 返回的是Unicode格式的数据
print (response.text)

# 查看响应内容,response.content返回的字节流数据
print (respones.content)

# 查看完整url地址
print (response.url)

# 查看响应头部字符编码
print (response.encoding)

# 查看响应码
print (response.status_code)

运行结果

......

......

'http://www.baidu.com/s?wd=%E9%95%BF%E5%9F%8E'

'utf-8'

200
  • 使用response.text 时,Requests 会基于 HTTP 响应的文本编码自动解码响应内容,大多数 Unicode 字符集都能被无缝地解码。
  • 使用response.content 时,返回的是服务器响应数据的原始二进制字节流,能够用来保存图片等二进制文件。

小栗子

3. 经过requests获取新浪首页
#coding=utf-8
import  requests
response = requests.get("http://www.sina.com")
print(response.request.headers)
print(response.content.decode())

结果

{
   
   'User-Agent': 'python-requests/2.12.4', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
<!DOCTYPE html>
<!-- [ published at 2017-06-09 15:15:23 ] -->
<html>
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>新浪首页</title>
    <meta name="keywords" content="新浪,新浪网,SINA,sina,sina.com.cn,新浪首页,门户,资讯" />
  ...

#coding=utf-8
import  requests
response = requests.get("http://www.sina.com")
print(response.request.headers)
print(response.text)

结果

{
   
   'User-Agent': 'python-requests/2.12.4', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
<!DOCTYPE html>
<!-- [ published at 2017-06-09 15:18:10 ] -->
<html>
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>新浪首页</title>
    <meta name="keywords" content="新浪,新浪网,SINA,sina,sina.com.cn,新浪首页,门户,资讯" />
    <meta name="description" content="新浪网为全球用户24小时提供全面及时的中文资讯,内容覆盖国内外突发新闻事件、体坛赛事、娱乐时尚、产业资讯、实用信息等,设有新闻、体育、娱乐、财经、科技、房产、汽车等30多个内容频道,同时开设博客、视频、论坛等自由互动交流空间。" />
    <link rel="mask-icon" sizes="any" href="//www.sina.com.cn/favicon.svg" color="red">
`

产生问题的缘由分析

  1. requests默认自带的Accept-Encoding致使或者新浪默认发送的就是压缩以后的网页
  2. 可是为何content.read()没有问题,由于requests,自带解压压缩网页的功能
  3. 当收到一个响应时,Requests 会猜想响应的编码方式,用于在你调用response.text 方法时对响应进行解码。Requests 首先在 HTTP 头部检测是否存在指定的编码方式,若是不存在,则会使用 chardet.detect来尝试猜想编码方式(存在偏差)
  4. 更推荐使用response.content.deocde()
4. 经过requests获取网络上图片的大小
from io import BytesIO,StringIO
import requests
from PIL import Image
img_url = "http://imglf1.ph.126.net/pWRxzh6FRrG2qVL3JBvrDg==/6630172763234505196.png"
response = requests.get(img_url)
f = BytesIO(response.content)
img = Image.open(f)
print(img.size)

输出结果:

(500, 262)

理解一下 BytesIO 和StringIO

不少时候,数据读写不必定是文件,也能够在内存中读写。
StringIO顾名思义就是在内存中读写str。
BytesIO 就是在内存中读写bytes类型的二进制数据

例子中若是使用StringIO 即f = StringIO(response.text)会产生"cannot identify image file"的错误
固然上述例子也能够把图片存到本地以后再使用Image打开来获取图片大小

1.5 requests深刻

基本POST请求(data参数)

1. 最基本post方法
response = requests.post("http://www.baidu.com/", data = data)
2. 传入data数据

对于 POST 请求来讲,咱们通常须要为它增长一些参数。那么最基本的传参方法能够利用 data 这个参数。

import requests

formdata = {
   
   
    "type":"AUTO",
    "i":"i love python",
    "doctype":"json",
    "xmlVersion":"1.8",
    "keyfrom":"fanyi.web",
    "ue":"UTF-8",
    "action":"FY_BY_ENTER",
    "typoResult":"true"
}

url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null"

headers={
   
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"}

response = requests.post(url, data = formdata, headers = headers)

print (response.text)

# 若是是json文件能够直接显示
print (response.json())

运行结果

{"type":"EN2ZH_CN","errorCode":0,"elapsedTime":3,"translateResult":[[{"src":"i love python","tgt":"我喜欢python"}]],"smartResult":{"type":1,"entries":["","肆文","","","高德纳","",""]}}

{'type': 'EN2ZH_CN', 'errorCode': 0, 'elapsedTime': 3, 'translateResult': [[{'src': 'i love python', 'tgt': '我喜欢python'}]], 'smartResult': {'type': 1, 'entries': ['', '肆文', '', '', '高德纳', '', '']}}

代理(proxies参数)

若是须要使用代理,你能够经过为任意请求方法提供 proxies 参数来配置单个请求:

import requests

# 根据协议类型,选择不一样的代理
proxies = {
   
   
  "http": "http://12.34.56.79:9527",
  "https": "http://12.34.56.79:9527",
}

response = requests.get("http://www.baidu.com", proxies = proxies)
print (response.text)

也能够经过本地环境变量 HTTP_PROXYHTTPS_PROXY 来配置代理:

export HTTP_PROXY="http://12.34.56.79:9527"
export HTTPS_PROXY="https://12.34.56.79:9527"
私密代理验证(特定格式) 和 Web客户端验证(auth 参数)

私密代理

import requests

# 若是代理须要使用HTTP Basic Auth,能够使用下面这种格式:
proxy = {
   
    "http": "mr_mao_hacker:sffqry9r@61.158.163.130:16816" }

response = requests.get("http://www.baidu.com", proxies = proxy)

print (response.text)

web客户端验证

若是是Web客户端验证,须要添加 auth = (帐户名, 密码)

import requests

auth=('test', '123456')

response = requests.get('http://192.168.199.107', auth = auth)

print (response.text)

Cookies 和 Sission

Cookies

若是一个响应中包含了cookie,那么咱们能够利用 cookies参数拿到:

import requests

response = requests.get("http://www.baidu.com/")

# 7\. 返回CookieJar对象:
cookiejar = response.cookies

# 8\. 将CookieJar转为字典:
cookiedict = requests.utils.dict_from_cookiejar(cookiejar)

print (cookiejar)

print (cookiedict)

运行结果:

<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>

{
   
   'BDORZ': '27315'}
session

在 requests 里,session对象是一个很是经常使用的对象,这个对象表明一次用户会话:从客户端浏览器链接服务器开始,到客户端浏览器与服务器断开。

会话能让咱们在跨请求时候保持某些参数,好比在同一个 Session 实例发出的全部请求之间保持 cookie 。

实现人人网登陆

import requests

# 1\. 建立session对象,能够保存Cookie值
ssion = requests.session()

# 2\. 处理 headers
headers = {
   
   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

# 3\. 须要登陆的用户名和密码
data = {
   
   "email":"mr_mao_hacker@163.com", "password":"alarmchime"}

# 4\. 发送附带用户名和密码的请求,并获取登陆后的Cookie值,保存在ssion里
ssion.post("http://www.renren.com/PLogin.do", data = data)

# 5\. ssion包含用户登陆后的Cookie值,能够直接访问那些登陆后才能够访问的页面
response = ssion.get("http://www.renren.com/410043129/profile")

# 6\. 打印响应内容
print (response.text)

img

处理HTTPS请求 SSL证书验证

Requests也能够为HTTPS请求验证SSL证书:

  • 要想检查某个主机的SSL证书,你能够使用 verify 参数(也能够不写)
import requests
response = requests.get("https://www.baidu.com/", verify=True)

# 也能够省略不写
# response = requests.get("https://www.baidu.com/")
print (r.text)

运行结果:

<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type
content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible
content=IE=Edge>百度一下,你就知道 ....
  • 若是SSL证书验证不经过,或者不信任服务器的安全证书,则会报出SSLError,听说 12306 证书是本身作的:

img

来测试一下:

import requests
response = requests.get("https://www.12306.cn/mormhweb/")
print (response.text)

果真:

SSLError: ("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",)

若是咱们想跳过 12306 的证书验证,把 verify 设置为 False 就能够正常请求了。

r = requests.get("https://www.12306.cn/mormhweb/", verify = False)

1.6 HTTP代理神器Fiddler

Fiddler是一款强大Web调试工具,它能记录全部客户端和服务器的HTTP请求。 Fiddler启动的时候,默认IE的代理设为了127.0.0.1:8888,而其余浏览器是须要手动设置。

工做原理

Fiddler 是以代理web服务器的形式工做的,它使用代理地址:127.0.0.1,端口:8888

img

Fiddler抓取HTTPS设置

  1. 启动Fiddler,打开菜单栏中的 Tools > Telerik Fiddler Options,打开“Fiddler Options”对话框。

    img

  2. 对Fiddler进行设置:

    • 打开工具栏->Tools->Fiddler Options->HTTPS,

    • 选中Capture HTTPS CONNECTs (捕捉HTTPS链接),

    • 选中Decrypt HTTPS traffic(解密HTTPS通讯)

    • 另外咱们要用Fiddler获取本机全部进程的HTTPS请求,因此中间的下拉菜单中选中…from all processes (从全部进程)

    • 选中下方Ignore server certificate errors(忽略服务器证书错误)

      img

  3. 为 Fiddler 配置Windows信任这个根证书解决安全警告:Trust Root Certificate(受信任的根证书)。

    img

  4. Fiddler 主菜单 Tools -> Fiddler Options…-> Connections

    • 选中Allow remote computers to connect(容许远程链接)

    • Act as system proxy on startup(做为系统启动代理)

      img

  5. 重启Fiddler,使配置生效(这一步很重要,必须作)。

Fiddler 如何捕获Chrome的会话

  1. 安装SwitchyOmega 代理管理 Chrome 浏览器插件

    img

  2. 如图所示,设置代理服务器为127.0.0.1:8888

    img

  3. 经过浏览器插件切换为设置好的代理。

    img

Fiddler界面

设置好后,本机HTTP通讯都会通过127.0.0.1:8888代理,也就会被Fiddler拦截到。

img

请求 (Request) 部分详解

  1. Headers —— 显示客户端发送到服务器的 HTTP 请求的 header,显示为一个分级视图,包含了 Web 客户端信息、Cookie、传输状态等。
  2. Textview —— 显示 POST 请求的 body 部分为文本。
  3. WebForms —— 显示请求的 GET 参数 和 POST body 内容。
  4. HexView —— 用十六进制数据显示请求。
  5. Auth —— 显示响应 header 中的 Proxy-Authorization(代理身份验证) 和 Authorization(受权) 信息.
  6. Raw —— 将整个请求显示为纯文本。
  7. JSON - 显示JSON格式文件。
  8. XML —— 若是请求的 body 是 XML 格式,就是用分级的 XML 树来显示它。

响应 (Response) 部分详解

  1. Transformer —— 显示响应的编码信息。
  2. Headers —— 用分级视图显示响应的 header。
  3. TextView —— 使用文本显示相应的 body。
  4. ImageVies —— 若是请求是图片资源,显示响应的图片。
  5. HexView —— 用十六进制数据显示响应。
  6. WebView —— 响应在 Web 浏览器中的预览效果。
  7. Auth —— 显示响应 header 中的 Proxy-Authorization(代理身份验证) 和 Authorization(受权) 信息。
  8. Caching —— 显示此请求的缓存信息。
  9. Privacy —— 显示此请求的私密 (P3P) 信息。
  10. Raw —— 将整个响应显示为纯文本。
  11. JSON - 显示JSON格式文件。
  12. XML —— 若是响应的 body 是 XML 格式,就是用分级的 XML 树来显示它 。

1.7 urllib库的基本使用

所谓网页抓取,就是把URL地址中指定的网络资源从网络流中读取出来,保存到本地。 在Python中有不少库能够用来抓取网页,咱们先学习urllib

*在 python2 中,urllib 被分为urllib,urllib2等*

urlopen

咱们先来段代码:

# urllib_request.py

# 导入urllib.request 库
import urllib.request

# 向指定的url发送请求,并返回服务器响应的类文件对象
response = urllib.request.urlopen("https://www.baidu.com")

# 类文件对象支持文件对象的操做方法,如read()方法读取文件所有内容,返回字符串
html = response.read()

# 打印字符串
print (html)

执行写的python代码,将打印结果

Power@PowerMac ~$: python urllib_request.py

实际上,若是咱们在浏览器上打开百度主页, 右键选择“查看源代码”,你会发现,跟咱们刚才打印出来的是如出一辙。也就是说,上面的4行代码就已经帮咱们把百度的首页的所有代码爬了下来。

一个基本的url请求对应的python代码真的很是简单。

Request

在咱们第一个例子里,urlopen()的参数就是一个url地址;

可是若是须要执行更复杂的操做,好比增长HTTP报头,必须建立一个 Request 实例来做为urlopen()的参数;而须要访问的url地址则做为 Request 实例的参数。

咱们编辑urllib_request.py

# urllib_request.py

import urllib.request

# url 做为Request()方法的参数,构造并返回一个Request对象
request = urllib.request.Request("http://www.baidu.com")

# Request对象做为urlopen()方法的参数,发送给服务器并接收响应
response = urllib.request.urlopen(request)

html = response.read().decode()

print (html)

运行结果是彻底同样的:

新建Request实例,除了必需要有 url 参数以外,还能够设置另外两个参数:

  1. data(默认空):是伴随 url 提交的数据(好比要post的数据),同时 HTTP 请求将从 "GET"方式 改成 "POST"方式。
  2. headers(默认空):是一个字典,包含了须要发送的HTTP报头的键值对。

这两个参数下面会说到。

User-Agent

可是这样直接用urllib给一个网站发送请求的话,确实略有些唐突了,就比如,人家每家都有门,你以一个路人的身份直接闯进去显然不是很礼貌。并且有一些站点不喜欢被程序(非人为访问)访问,有可能会拒绝你的访问请求。

可是若是咱们用一个合法的身份去请求别人网站,显然人家就是欢迎的,因此咱们就应该给咱们的这个代码加上一个身份,就是所谓的User-Agent头。

  • 浏览器 就是互联网世界上公认被容许的身份,若是咱们但愿咱们的爬虫程序更像一个真实用户,那咱们第一步,就是须要假装成一个被公认的浏览器。用不一样的浏览器在发送请求的时候,会有不一样的User-Agent头。 urllib默认的User-Agent头为:Python-urllib/x.y(x和y是Python主版本和次版本号,例如 Python-urllib/2.7)
#urllib_request.py

import urllib.request

url = "http://www.itcast.cn"

#IE 9.0 的 User-Agent,包含在 ua_header里
ua_header = {
   
   "User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"}

#  url 连同 headers,一块儿构造Request请求,这个请求将附带 IE9.0 浏览器的User-Agent
request = urllib.request.Request(url, headers = ua_header)

# 向服务器发送这个请求
response = urllib.request.urlopen(request)

html = response.read()
print (html)

添加更多的Header信息

在 HTTP Request 中加入特定的 Header,来构造一个完整的HTTP请求消息。

能够经过调用Request.add_header() 添加/修改一个特定的header 也能够经过调用Request.get_header()来查看已有的header。

  • 添加一个特定的header
# urllib_headers.py

import urllib.request

url = "http://www.itcast.cn"

#IE 9.0 的 User-Agent
header = {
   
   "User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"}
request = urllib.request.Request(url, headers = header)

#也能够经过调用Request.add_header() 添加/修改一个特定的header
request.add_header("Connection", "keep-alive")

# 也能够经过调用Request.get_header()来查看header信息
# request.get_header(header_name="Connection")

response = urllib.request.urlopen(request)

print (response.code) #能够查看响应状态码
html = response.read().decode()

print (html)
  • 随机添加/修改User-Agent
# urllib_add_headers.py

import urllib
import random

url = "http://www.itcast.cn"

ua_list = [
    "Mozilla/5.0 (Windows NT 6.1; ) Apple.... ",
    "Mozilla/5.0 (X11; CrOS i686 2268.111.0)... ",
    "Mozilla/5.0 (Macintosh; U; PPC Mac OS X.... ",
    "Mozilla/5.0 (Macintosh; Intel Mac OS... "
]

user_agent = random.choice(ua_list)

request = urllib.request.Request(url)

#也能够经过调用Request.add_header() 添加/修改一个特定的header
request.add_header("User-Agent", user_agent)

# get_header()的字符串参数,第一个字母大写,后面的所有小写
request.get_header("User-agent")

response = urllib.request.urlopen(requestr)

html = response.read()
print (html)

1.8 urllib默认只支持HTTP/HTTPS的GETPOST方法

urllib.parse.urlencode()

  • 编码工做使用urllib.parse的urlencode()函数,帮咱们将key:value这样的键值对转换成"key=value"这样的字符串,解码工做能够使用urllib.parse的unquote()函数。
# IPython3 中的测试结果
In [1]: import urllib.parse

In [2]: word = {
   
   "wd" : "传智播客"}

# 经过urllib.urlencode()方法,将字典键值对按URL编码转换,从而能被web服务器接受。
In [3]: urllib.parse.urlencode(word)
Out[3]: "wd=%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2"

# 经过urllib.unquote()方法,把 URL编码字符串,转换回原先字符串。
In [4]: print urllib.parse.unquote("wd=%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2")
wd=传智播客

通常HTTP请求提交数据,须要编码成 URL编码格式,而后作为url的一部分,或者做为参数传到Request对象中。

Get方式

GET请求通常用于咱们向服务器获取数据,好比说,咱们用百度搜索传智播客https://www.baidu.com/s?wd=传智播客

浏览器的url会跳转成如图所示:

img

在其中咱们能够看到在请求部分里,http://www.baidu.com/s? 以后出现一个长长的字符串,其中就包含咱们要查询的关键词传智播客,因而咱们能够尝试用默认的Get方式来发送请求。

# urllib_get.py

url = "http://www.baidu.com/s"
word = {
   
   "wd":"传智播客"}
word = urllib.parse.urlencode(word) #转换成url编码格式(字符串)
newurl = url + "?" + word    # url首个分隔符就是 ?

headers={
   
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"}

request = urllib.request.Request(newurl, headers=headers)

response = urllib.request.urlopen(request)

print (response.read())

批量爬取贴吧页面数据

首先咱们建立一个python文件, tiebaSpider.py,咱们要完成的是,输入一个百度贴吧的地址,好比:

百度贴吧LOL吧第一页:http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=0

第二页: http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=50

第三页: http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=100

发现规律了吧,贴吧中每一个页面不一样之处,就是url最后的pn的值,其他的都是同样的,咱们能够抓住这个规律。

简单写一个小爬虫程序,来爬取百度LOL吧的全部网页。

  • 先写一个main,提示用户输入要爬取的贴吧名,并用urllib.urlencode()进行转码,而后组合url,假设是lol吧,那么组合后的url就是http://tieba.baidu.com/f?kw=lol
# 模拟 main 函数
if __name__ == "__main__":

    kw = raw_input("请输入须要爬取的贴吧:")
    # 输入起始页和终止页,str转成int类型
    beginPage = int(raw_input("请输入起始页:"))
    endPage = int(raw_input("请输入终止页:"))

    url = "http://tieba.baidu.com/f?"
    key = urllib.parse.urlencode({
   
   "kw" : kw})

    # 组合后的url示例:http://tieba.baidu.com/f?kw=lol
    url = url + key
    tiebaSpider(url, beginPage, endPage)
  • 接下来,咱们写一个百度贴吧爬虫接口,咱们须要传递3个参数给这个接口, 一个是main里组合的url地址,以及起始页码和终止页码,表示要爬取页码的范围。
def tiebaSpider(url, beginPage, endPage):
    """
        做用:负责处理url,分配每一个url去发送请求
        url:须要处理的第一个url
        beginPage: 爬虫执行的起始页面
        endPage: 爬虫执行的截止页面
    """

    for page in range(beginPage, endPage + 1):
        pn = (page - 1) * 50

        filename = "第" + str(page) + "页.html"
        # 组合为完整的 url,而且pn值每次增长50
        fullurl = url + "&pn=" + str(pn)
        #print fullurl

        # 调用loadPage()发送请求获取HTML页面
        html = loadPage(fullurl, filename)
        # 将获取到的HTML页面写入本地磁盘文件
        writeFile(html, filename)
  • 咱们已经以前写出一个爬取一个网页的代码。如今,咱们能够将它封装成一个小函数loadPage,供咱们使用。
def loadPage(url, filename):
    '''
        做用:根据url发送请求,获取服务器响应文件
        url:须要爬取的url地址
        filename: 文件名
    '''
    print ("正在下载" + filename)

    headers = {
   
   "User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;"}

    request = urllib.request.Request(url, headers = headers)
    response = urllib.request.urlopen(request)
    return response.read()
  • 最后若是咱们但愿将爬取到了每页的信息存储在本地磁盘上,咱们能够简单写一个存储文件的接口。
def writeFile(html, filename):
    """
        做用:保存服务器响应文件到本地磁盘文件里
        html: 服务器响应文件
        filename: 本地磁盘文件名
    """
    print ("正在存储" + filename)
    with open(filename, 'w') as f:
        f.write(html)
    print "-" * 20

其实不少网站都是这样的,同类网站下的html页面编号,分别对应网址后的网页序号,只要发现规律就能够批量爬取页面了。


POST方式:

上面咱们说了Request请求对象的里有data参数,它就是用在POST里的,咱们要传送的数据就是这个参数data,data是一个字典,里面要匹配键值对。

有道词典翻译网站:

输入测试数据,再经过使用Fiddler观察,其中有一条是POST请求,而向服务器发送的请求数据并非在url里,那么咱们能够试着模拟这个POST请求。

img

因而,咱们能够尝试用POST方式发送请求。

import urllib

# POST请求的目标URL
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null"

headers={
   
   "User-Agent": "Mozilla...."}

formdata = {
   
   
    "type":"AUTO",
    "i":"i love python",
    "doctype":"json",
    "xmlVersion":"1.8",
    "keyfrom":"fanyi.web",
    "ue":"UTF-8",
    "action":"FY_BY_ENTER",
    "typoResult":"true"
}

data = urllib.parse.urlencode(formdata)

request = urllib.request.Request(url, data = data, headers = headers)
response = urllib.request.urlopen(request)
print (response.read())

发送POST请求时,须要特别注意headers的一些属性:

Content-Length: 144: 是指发送的表单数据长度为144,也就是字符个数是144个。

Content-Type: application/x-www-form-urlencoded : 表示浏览器提交 Web 表单时使用,表单数据会按照 name1=value1&name2=value2 键值对形式进行编码。

X-Requested-With: XMLHttpRequest :表示Ajax异步请求。

获取AJAX加载的内容

有些网页内容使用AJAX加载,这种数据没法直接对网页url进行获取。只要记得,AJAX通常返回的是JSON,只要对AJAX地址进行post或get,就能返回JSON数据了。

若是非要从HTML页面里获取展示出来的数据,也不是不能够。可是要记住,做为一名爬虫工程师,你更须要关注的是数据的来源。

import urllib

# demo1

url = "https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action"

headers={
   
   "User-Agent": "Mozilla...."}

# 变更的是这两个参数,从start开始日后显示limit个
formdata = {
   
   
    'start':'0',
    'limit':'10'
}
data = urllib.parse.urlencode(formdata)

request = urllib.request.Request(url, data = data, headers = headers)
response = urllib.request.urlopen(request)

print (response.read())


# demo2

url = "https://movie.douban.com/j/chart/top_list?"
headers={
   
   "User-Agent": "Mozilla...."}

# 处理全部参数
formdata = {
   
   
    'type':'11',
    'interval_id':'100:90',
    'action':'',
    'start':'0',
    'limit':'10'
}
data = urllib.parse.urlencode(formdata)

request = urllib.request.Request(url, data = data, headers = headers)
response = urllib.request.urlopen(request)

print (response.read())
  • GET方式是直接以连接形式访问,连接中包含了全部的参数,服务器端用Request.QueryString获取变量的值。若是包含了密码的话是一种不安全的选择,不过你能够直观地看到本身提交了什么内容。
  • POST则不会在网址上显示全部的参数,服务器端用Request.Form获取提交的数据,在Form提交的时候。可是HTML代码里若是不指定 method 属性,则默认为GET请求,Form中提交的数据将会附加在url以后,以?分开与url分开。
  • 表单数据能够做为 URL 字段(method=“get”)或者 HTTP POST (method=“post”)的方式来发送。好比在下面的HTML代码中,表单数据将由于 (method=“get”) 而附加到 URL 上:
<form action="form_action.asp" method="get">
    <p>First name: <input type="text" name="fname" /></p>
    <p>Last name: <input type="text" name="lname" /></p>
    <input type="submit" value="Submit" />
</form>

img

处理HTTPS请求 SSL证书验证

如今随处可见 https 开头的网站,urllib能够为 HTTPS 请求验证SSL证书,就像web浏览器同样,若是网站的SSL证书是通过CA认证的,则可以正常访问,如:https://www.baidu.com/等…

若是SSL证书验证不经过,或者操做系统不信任服务器的安全证书,好比浏览器在访问12306网站如:https://www.12306.cn/mormhweb/的时候,会警告用户证书不受信任。(听说 12306 网站证书是本身作的,没有经过CA认证)

img

urllib在访问的时候则会报出SSLError:

import urllib

url = "https://www.12306.cn/mormhweb/"

headers = {
   
   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

request = urllib.request.Request(url, headers = headers)

response = urllib.request.urlopen(request)

print (response.read())

运行结果:

URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)>

因此,若是之后遇到这种网站,咱们须要单独处理SSL证书,让程序忽略SSL证书验证错误,便可正常访问。

import urllib
# 1. 导入Python SSL处理模块
import ssl

# 2. 表示忽略未经核实的SSL证书认证
context = ssl._create_unverified_context()

url = "https://www.12306.cn/mormhweb/"

headers = {
   
   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

request = urllib.request.Request(url, headers = headers)

# 3. 在urlopen()方法里 指明添加 context 参数
response = urllib.request.urlopen(request, context = context)

print (response.read().decode())

关于CA

CA(Certificate Authority)是数字证书认证中心的简称,是指发放、管理、废除数字证书的受信任的第三方机构

CA的做用是检查证书持有者身份的合法性,并签发证书,以防证书被伪造或篡改,以及对证书和密钥进行管理。

现实生活中能够用身份证来证实身份, 那么在网络世界里,数字证书就是身份证。和现实生活不一样的是,并非每一个上网的用户都有数字证书的,每每只有当一我的须要证实本身的身份的时候才须要用到数字证书。

普通用户通常是不须要,由于网站并不关心是谁访问了网站,如今的网站只关心流量。可是反过来,网站就须要证实本身的身份了。

好比说如今钓鱼网站不少的,好比你想访问的是www.baidu.com,但其实你访问的是www.daibu.com”,因此在提交本身的隐私信息以前须要验证一下网站的身份,要求网站出示数字证书。

通常正常的网站都会主动出示本身的数字证书,来确保客户端和网站服务器之间的通讯数据是加密安全的。

1.9 urllib:Handler处理器 和 自定义Opener

  • opener是 urllib.request.OpenerDirector 的实例,咱们以前一直都在使用的urlopen,它是一个特殊的opener(也就是模块帮咱们构建好的)。
  • 可是基本的urlopen()方法不支持代理、cookie等其余的HTTP/HTTPS高级功能。因此要支持这些功能:
    1. 使用相关的 Handler处理器 来建立特定功能的处理器对象;
    2. 而后经过 urllib.request.build_opener()方法使用这些处理器对象,建立自定义opener对象;
    3. 使用自定义的opener对象,调用open()方法发送请求。
  • 若是程序里全部的请求都使用自定义的opener,能够使用urllib.request.install_opener() 将自定义的 opener 对象 定义为 全局opener,表示若是以后凡是调用urlopen,都将使用这个opener(根据本身的需求来选择)

简单的自定义opener()

import urllib.request

# 构建一个HTTPHandler 处理器对象,支持处理HTTP请求
http_handler = urllib.request.HTTPHandler()

# 构建一个HTTPHandler 处理器对象,支持处理HTTPS请求
# http_handler = urllib.request.HTTPSHandler()

# 调用urllib.request.build_opener()方法,建立支持处理HTTP请求的opener对象
opener = urllib.request.build_opener(http_handler)

# 构建 Request请求
request = urllib.request.Request("http://www.baidu.com/")

# 调用自定义opener对象的open()方法,发送request请求
response = opener.open(request)

# 获取服务器响应内容
print (response.read().decode())

这种方式发送请求获得的结果,和使用urllib.request.urlopen()发送HTTP/HTTPS请求获得的结果是同样的。

若是在 HTTPHandler()增长 debuglevel=1参数,还会将 Debug Log 打开,这样程序在执行的时候,会把收包和发包的报头在屏幕上自动打印出来,方便调试,有时能够省去抓包的工做。

# 仅须要修改的代码部分:

# 构建一个HTTPHandler 处理器对象,支持处理HTTP请求,同时开启Debug Log,debuglevel 值默认 0
http_handler = urllib.request.HTTPHandler(debuglevel=1)

# 构建一个HTTPHSandler 处理器对象,支持处理HTTPS请求,同时开启Debug Log,debuglevel 值默认 0
https_handler = urllib.request.HTTPSHandler(debuglevel=1)

ProxyHandler处理器(代理设置)

使用代理IP,这是爬虫/反爬虫的第二大招,一般也是最好用的。

不少网站会检测某一段时间某个IP的访问次数(经过流量统计,系统日志等),若是访问次数多的不像正常人,它会禁止这个IP的访问。

因此咱们能够设置一些代理服务器,每隔一段时间换一个代理,就算IP被禁止,依然能够换个IP继续爬取。

urllib.request中经过ProxyHandler来设置使用代理服务器,下面代码说明如何使用自定义opener来使用代理:

#urllib_proxy1.py

import urllib.request

# 构建了两个代理Handler,一个有代理IP,一个没有代理IP
httpproxy_handler = urllib.request.ProxyHandler({
   
   "http" : "124.88.67.81:80"})
nullproxy_handler = urllib.request.ProxyHandler({
   
   })

proxySwitch = True #定义一个代理开关

# 经过 urllib.request.build_opener()方法使用这些代理Handler对象,建立自定义opener对象
# 根据代理开关是否打开,使用不一样的代理模式
if proxySwitch:
    opener = urllib.request.build_opener(httpproxy_handler)
else:
    opener = urllib.request.build_opener(nullproxy_handler)

request = urllib.request.Request("http://www.baidu.com/")

# 1. 若是这么写,只有使用opener.open()方法发送请求才使用自定义的代理,而urlopen()则不使用自定义代理。
response = opener.open(request)

# 2. 若是这么写,就是将opener应用到全局,以后全部的,无论是opener.open()仍是urlopen() 发送请求,都将使用自定义代理。
# urllib.request.install_opener(opener)
# response = urlopen(request)

print (response.read().decode())

免费的开放代理获取基本没有成本,咱们能够在一些代理网站上收集这些免费代理,测试后若是能够用,就把它收集起来用在爬虫上面。

免费短时间代理网站举例:

若是代理IP足够多,就能够像随机获取User-Agent同样,随机选择一个代理去访问网站。

import urllib.request
import random

proxy_list = [
    {
   
   "http" : "124.88.67.81:80"},
    {
   
   "http" : "124.88.67.81:80"},
    {
   
   "http" : "124.88.67.81:80"},
    {
   
   "http" : "124.88.67.81:80"},
    {
   
   "http" : "124.88.67.81:80"}
]

# 随机选择一个代理
proxy = random.choice(proxy_list)
# 使用选择的代理构建代理处理器对象
httpproxy_handler = urllib.request.ProxyHandler(proxy)

opener = urllib.request.build_opener(httpproxy_handler)

request = urllib.request.Request("http://www.baidu.com/")
response = opener.open(request)
print (response.read())

可是,这些免费开放代理通常会有不少人都在使用,并且代理有寿命短,速度慢,匿名度不高,HTTP/HTTPS支持不稳定等缺点(免费没好货)。

因此,专业爬虫工程师或爬虫公司会使用高品质的私密代理,这些代理一般须要找专门的代理供应商购买,再经过用户名/密码受权使用(舍不得孩子套不到狼)。

Cookie

Cookie 是指某些网站服务器为了辨别用户身份和进行Session跟踪,而储存在用户浏览器上的文本文件,Cookie能够保持登陆信息到用户下次与服务器的会话。

Cookie原理

HTTP是无状态的面向链接的协议, 为了保持链接状态, 引入了Cookie机制 Cookie是http消息头中的一种属性,包括:

Cookie名字(Name)
Cookie的值(Value)
Cookie的过时时间(Expires/Max-Age)
Cookie做用路径(Path)
Cookie所在域名(Domain),
使用Cookie进行安全链接(Secure)。

前两个参数是Cookie应用的必要条件,另外,还包括Cookie大小(Size,不一样浏览器对Cookie个数及大小限制是有差别的)。

Cookie由变量名和值组成,根据 Netscape公司的规定,Cookie格式以下:

Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
Cookie应用

Cookies在爬虫方面最典型的应用是断定注册用户是否已经登陆网站,用户可能会获得提示,是否在下一次进入此网站时保留用户信息以便简化登陆手续。

# 获取一个有登陆信息的Cookie模拟登录

import urllib

# 1. 构建一个已经登陆过的用户的headers信息
headers = {
   
   
    "Host":"www.renren.com",
    "Connection":"keep-alive",
    "Upgrade-Insecure-Requests":"1",
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
    "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language":"zh-CN,zh;q=0.8,en;q=0.6",
    "Referer":"http://www.renren.com/SysHome.do",
    # 便于终端阅读,表示不支持压缩文件
    # Accept-Encoding: gzip, deflate, sdch,

    # 重点:这个Cookie是保存了密码无需重复登陆的用户的Cookie,这个Cookie里记录了用户名,密码(一般通过RAS加密)
    "Cookie": "anonymid=j3jxk555-nrn0wh; depovince=BJ; _r01_=1; JSESSIONID=abcnLjz9MSvBa-3lJK3Xv; ick=3babfba4-e0ed-4e9f-9312-8e833e4cb826; jebecookies=764bacbd-0e4a-4534-b8e8-37c10560770c|||||; ick_login=84f70f68-7ebd-4c5c-9c0f-d1d9aac778e0; _de=7A7A02E9254501DA6278B9C75EAEEB7A; p=91063de8b39ac5e0d2a57500de7e34077; first_login_flag=1; ln_uact=13146128763; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; t=39fca09219c06df42604435129960e1f7; societyguester=39fca09219c06df42604435129960e1f7; id=941954027; xnsid=8868df75; ver=7.0; loginfrom=null; XNESSESSIONID=a6da759fe858; WebOnLineNotice_941954027=1; wp_fold=0"
}

# 2. 经过headers里的报头信息(主要是Cookie信息),构建Request对象
urllib.request.Request("http://www.renren.com/941954027#", headers = headers)

# 3. 直接访问renren主页,服务器会根据headers报头信息(主要是Cookie信息),判断这是一个已经登陆的用户,并返回相应的页面
response = urllib.request.urlopen(request)

# 4. 打印响应内容
print (response.read().decode())

img

可是这样作太过复杂,咱们先须要在浏览器登陆帐户,而且设置保存密码,而且经过抓包才能获取这个Cookie,那有么有更简单方便的方法呢?

cookiejar库 和 HTTPCookieProcessor处理器

在Python处理Cookie,通常是经过cookiejar模块和 urllib模块的HTTPCookieProcessor处理器类一块儿使用。

cookiejar模块:主要做用是提供用于存储cookie的对象

HTTPCookieProcessor处理器:主要做用是处理这些cookie对象,并构建handler对象。

cookiejar 库

该模块主要的对象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。

  • CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。

咱们来作几个案例:

1)获取Cookie,并保存到CookieJar()对象中

# urllib_cookiejar_test1.py

import urllib
from http import cookiejar

# 构建一个CookieJar对象实例来保存cookie
cookiejar = cookiejar.CookieJar()

# 使用HTTPCookieProcessor()来建立cookie处理器对象,参数为CookieJar()对象
handler=urllib.request.HTTPCookieProcessor(cookiejar)

# 经过 build_opener() 来构建opener
opener = urllib.request.build_opener(handler)

# 4. 以get方法访问页面,访问以后会自动保存cookie到cookiejar中
opener.open("http://www.baidu.com")

## 能够按标准格式将保存的Cookie打印出来
cookieStr = ""
for item in cookiejar:
    cookieStr = cookieStr + item.name + "=" + item.value + ";"

## 舍去最后一位的分号
print (cookieStr[:-1])

咱们使用以上方法将Cookie保存到cookiejar对象中,而后打印出了cookie中的值,也就是访问百度首页的Cookie值。

运行结果以下:

BAIDUID=4327A58E63A92B73FF7A297FB3B2B4D0:FG=1;BIDUPSID=4327A58E63A92B73FF7A297FB3B2B4D0;H_PS_PSSID=1429_21115_17001_21454_21409_21554_21398;PSTM=1480815736;BDSVRTM=0;BD_HOME=0

利用cookiejar和post登陆人人网

import urllib
from http import cookiejar

# 1. 构建一个CookieJar对象实例来保存cookie
cookie = cookiejar.CookieJar()

# 2. 使用HTTPCookieProcessor()来建立cookie处理器对象,参数为CookieJar()对象
cookie_handler = urllib.request.HTTPCookieProcessor(cookie)

# 3. 经过 build_opener() 来构建opener
opener = urllib.request.build_opener(cookie_handler)

# 4. addheaders 接受一个列表,里面每一个元素都是一个headers信息的元祖, opener将附带headers信息
opener.addheaders = [("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36")]

# 5. 须要登陆的帐户和密码
data = {
   
   "email":"13****46**8763", "password":"****"}

# 6. 经过urlencode()转码
postdata = urllib.parse.urlencode(data).encode()

# 7. 构建Request请求对象,包含须要发送的用户名和密码
request = urllib.request.Request("http://www.renren.com/PLogin.do", data = postdata)

# 8. 经过opener发送这个请求,并获取登陆后的Cookie值,
opener.open(request)

# 9. opener包含用户登陆后的Cookie值,能够直接访问那些登陆后才能够访问的页面
response = opener.open("http://www.renren.com/410043129/profile")

# 10. 打印响应内容
print (response.read().decode())

模拟登陆要注意几点:

  1. 登陆通常都会先有一个HTTP GET,用于拉取一些信息及得到Cookie,而后再HTTP POST登陆。
  2. HTTP POST登陆的连接有多是动态的,从GET返回的信息中获取。
  3. password 有些是明文发送,有些是加密后发送。有些网站甚至采用动态加密的,同时包括了不少其余数据的加密信息,只能经过查看JS源码得到加密算法,再去破解加密,很是困难。
  4. 大多数网站的登陆总体流程是相似的,可能有些细节不同,因此不能保证其余网站登陆成功。

这个测试案例中,为了想让你们快速理解知识点,咱们使用的人人网登陆接口是人人网改版前的隐藏接口(嘘…),登陆比较方便。

固然,咱们也能够直接发送帐号密码到登陆界面模拟登陆,可是当网页采用JavaScript动态技术之后,想封锁基于 HttpClient 的模拟登陆就太容易了,甚至能够根据你的鼠标活动的特征准确地判断出是否是真人在操做。

因此,想作通用的模拟登陆还得选别的技术,好比用内置浏览器引擎的爬虫(关键词:Selenium ,PhantomJS),这个咱们将在之后会学习到。

1.10 编码故事

好久好久之前,有一群人,他们决定用8个能够开合的晶体管来组合成不一样的状态,以表示世界上的万物。他们看到8个开关状态是好的,因而他们把这称为"字节"。

再后来,他们又作了一些能够处理这些字节的机器,机器开动了,能够用字节来组合出不少状态,状态开始变来变去。他们看到这样是好的,因而它们就这机器称为"计算机"。

开始计算机只在美国用。八位的字节一共能够组合出256(2的8次方)种不一样的状态。

他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机赶上约定好的这些字节被传过来时,就要作一些约定的动做。赶上00x10, 终端就换行,赶上0x07, 终端就向人们嘟嘟叫,例好赶上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。他们看到这样很好,因而就把这些0x20如下的字节状态称为"控制码"。

他们又把全部的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就能够用不一样字节来存储英语的文字了。你们看到这样,都感受很好,因而你们都把这个方案叫作 ANSI 的"Ascii"编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上全部的计算机都用一样的ASCII方案来保存英文文字。

后来,就像建造巴比伦塔同样,世界各地的都开始使用计算机,可是不少国家用的不是英文,他们的字母里有许可能是ASCII里没有的,为了能够在计算机保存他们的文字,他们决定采用127号以后的空位来表示这些新的字母、符号,还加入了不少画表格时须要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128到255这一页的字符集被称"扩展字符集"。今后以后,贪婪的人类再没有新的状态能够用了,美帝国主义可能没有想到还有第三世界国家的人们也但愿能够用到计算机吧!

等中国人们获得计算机时,已经没有能够利用的字节状态来表示汉字,何况有6000多个经常使用汉字须要保存呢。可是这难不倒智慧的中国人民,咱们不客气地把那些127号以后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一块儿时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样咱们就能够组合出大约7000多个简体汉字了。在这些编码里,咱们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里原本就有的数字、标点、字母都通通从新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号如下的那些就叫"半角"字符了。

中国人民看到这样很不错,因而就把这种汉字方案叫作 “GB2312”。GB2312 是对 ASCII 的中文扩展。

可是中国的汉字太多了,咱们很快就就发现有许多人的人名没有办法在这里打出来,特别是某些很会麻烦别人的国家领导人。因而咱们不得不继续把 GB2312 没有用到的码位找出来老实不客气地用上。

后来仍是不够用,因而干脆再也不要求低字节必定是127号以后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,无论后面跟的是否是扩展字符集里的内容。结果扩展以后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的全部内容,同时又增长了近20000个新的汉字(包括繁体字)和符号。

后来少数民族也要用电脑了,因而咱们再扩展,又加了几千个新的少数民族的字,GBK 扩成了 GB18030。今后以后,中华民族的文化就能够在计算机时代中传承了。

中国的程序员们看到这一系列汉字编码的标准是好的,因而通称他们叫作 “DBCS”(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特色是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,所以他们写的程序为了支持中文处理,必需要注意字串里的每个字节的值,若是这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣们都要天天念下面这个咒语数百遍:

“一个汉字算两个英文字符!一个汉字算两个英文字符……”

由于当时各个国家都像中国这样搞出一套本身的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也分别采用了不一样的 DBCS 编码方案——当时的中国人想让电脑显示汉字,就必须装上一个"汉字系统",专门用来处理汉字的显示、输入的问题,可是那个台湾的愚昧封建人士写的算命程序就必须加装另外一套支持 BIG5 编码的什么"倚天汉字系统"才能够用,装错了字符系统,显示就会乱了套!这怎么办?并且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎么办?

真是计算机的巴比伦塔命题啊!

正在这时,大天使加百列及时出现了——一个叫 ISO (国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了全部的地区性编码方案,从新搞一个包括了地球上全部文化、全部字母和符号的编码!他们打算叫它"Universal Multiple-Octet Coded Character Set",简称 UCS, 俗称 “UNICODE”。

UNICODE 开始制订时,计算机的存储器容量极大地发展了,空间不再成为问题了。因而 ISO 就直接规定必须用两个字节,也就是16位来统一表示全部的字符,对于ascii里的那些“半角”字符,UNICODE 包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其余文化和语言的字符则所有从新统一编码。因为"半角"英文符号只须要用到低8位,因此其高8位永远是0,所以这种大气的方案在保存英文文本时会多浪费一倍的空间。

这时候,从旧社会里走过来的程序员开始发现一个奇怪的现象:他们的strlen函数靠不住了,一个汉字再也不是至关于两个字符了,而是一个!是的,从 UNICODE 开始,不管是半角的英文字母,仍是全角的汉字,它们都是统一的"一个字符"!同时,也都是统一的"两个字节",请注意"字符"和"字节"两个术语的不一样,“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。在UNICODE 中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。

从前多种字符集存在时,那些作多语言软件的公司赶上过很大麻烦,他们为了在不一样的国家销售同一套软件,就不得不在区域化软件时也加持那个双字节字符集咒语,不只要到处当心不要搞错,还要把软件中的文字在不一样的字符集中转来转去。UNICODE 对于他们来讲是一个很好的一揽子解决方案,因而从 Windows NT 开始,MS 趁机把它们的操做系统改了一遍,把全部的核心代码都改为了用 UNICODE 方式工做的版本,从这时开始,WINDOWS 系统终于无须要加装各类本土语言系统,就能够显示全世界上全部文化的字符了。

可是,UNICODE 在制订时没有考虑与任何一种现有的编码方案保持兼容,这使得 GBK 与UNICODE 在汉字的内码编排上彻底是不同的,没有一种简单的算术方法能够把文本内容从UNICODE编码和另外一种编码进行转换,这种转换必须经过查表来进行。

如前所述,UNICODE 是用两个字节来表示为一个字符,他总共能够组合出65535不一样的字符,这大概已经能够覆盖世界上全部文化的符号。若是还不够也没有关系,ISO已经准备了UCS-4方案,说简单了就是四个字节来表示一个字符,这样咱们就能够组合出21亿个不一样的字符出来(最高位有其余用途),这大概能够用到银河联邦成立那一天吧!

UNICODE 来到时,一块儿到来的还有计算机网络的兴起,UNICODE 如何在网络上传输也是一个必须考虑的问题,因而面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位,只不过为了传输时的可靠性,从UNICODE到UTF时并非直接的对应,而是要过一些算法和规则来转换。

总结:

  • 字符(Character)是各类文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。字符集(Character set)是多个字符的集合
  • 字符集包括:ASCII字符集、GB2312字符集、GB18030字符集、Unicode字符集等
  • ASCII编码是1个字节,而Unicode编码一般是2个字节。
  • UTF-8是Unicode的实现方式之一,UTF-8是它是一种变长的编码方式,能够是1,2,3个字节