第二章 爬虫基础
2.1 HTTP基本原理
在本节我们会详细了解 HTTP 的基本原理,了解在浏览器中敲入一个 URL 到获取网页内容发生了一个怎样的过程,了解了这些内容,有助于去进一步了解爬虫的基本原理。
1. URI、URL
在了解 HTTP 之前我们先了解一下 URI 和 URL。我们经常会听到 URI 和 URL 两个术语,URI 全称为 Uniform Resource Identifier,即统一资源标志符,URL 全称为 Universal Resource Locator,即统一资源定位符。
举例来说,https://github.com/favicon.ico,这是 GitHub 的网站图标链接,它是一个 URL,也是一个 URI,即有这样的一个图标资源,我们用 URL/URI 来唯一指定了它的访问方式,这其中包括了访问协议 https、访问路径/即根目录,资源名称 favicon.ico,通过这样的一个链接我们便可以从互联网上找到这个资源,这就是 URL/URI。
URL 是 URI 的子集,也就是说每个 URL 都是 URI,但不是每个 URI 都是 URL。那么怎样的 URI 不是 URL 呢?URI 还包括一个子类叫做 URN,它的全称为 Universal Resource Name,即统一资源名称。URN 只命名资源而不指定如何定位资源,如 urn:isbn:0451450523,它指定了一本书的 ISBN,可以唯一标识这一本书,但是没有指定到哪里定位这本书,这就是 URN,URL、URN、URI 的关系可以用图 2-1 表示如下:
图 2-1 URL、URN、URI 关系图
但是在目前的互联网,URN 的使用非常少,所以几乎所有的 URI 都是 URL,所以一般的网页链接我们可以称之为 URL,也可以称之为 URI,我个人习惯称之为 URL。
2. 超文本
接下来我们再了解一个概念,超文本。超文本英文名称叫做 Hypertext,我们在浏览器里面看到的网页就是超文本解析而成的,其网页源代码是一系列 HTML 代码,里面包含了一系列标签,如 img 显示图片,p 指定显示段落等,浏览器解析这些标签后便形成了我们平常看到的网页,而这网页的源代码 HTML 就可以称作超文本。
例如我们在 Chrome 浏览器里面打开任意一个页面,如淘宝首页,右键点击检查,或按下快捷键 F12 即可打开浏览器的开发者工具,这时我们在 Elements 选项卡即可看到当前网页的源代码,这些源代码都是超文本,如图 2-2 所示:
图 2-2 源代码
3. HTTP、HTTPS
我们在前面了解了 URI 和 URL,例如淘宝的首页:https://www.taobao.com/,在 URL 的开头会有 http 或 https,这个就是访问资源需要的协议类型,有时我们还会看到 ftp、sftp、smb 开头的 URL,那么这里的 ftp、sftp、smb 都是指的协议类型。在爬虫中,我们抓取的页面通常就是 http 或 https 协议的,我们在这里首先来了解一下这两个协议的含义。
HTTP 的全称是 Hyper Text Transfer Protocol,中文名叫做超文本传输协议,HTTP 协议是用于从网络传输超文本数据到本地浏览器的传送协议,它能保证传送高效而准确地传送超文本文档。HTTP 由万维网协会(World Wide Web Consortium - W3C)和 Internet 工作小组IETF(Internet Engineering Task Force)共同合作制定的规范,目前广泛使用的是 HTTP 1.1 版本。
HTTPS 的全称是 Hyper Text Transfer Protocol over Secure Socket Layer,是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版,即 HTTP 下加入 SSL 层,简称为 HTTPS。
HTTPS 的安全基础是 SSL,因此通过它传输的内容都是经过 SSL 加密的,它的主要作用可以分为两种:
- 是建立一个信息安全通道,来保证数据传输的安全。
- 确认网站的真实性,凡是使用了 https 的网站,都可以通过点击浏览器地址栏的锁头标志来查看网站认证之后的真实信息,也可以通过 CA 机构颁发的安全签章来查询。
现在越来越多的网站和 APP 都已经向 HTTPS 方向发展。例如:
- 苹果公司强制所有 iOS App 在 2017 年 1 月 1 日 前全部改为使用 HTTPS 加密,否则 APP 就无法在应用商店上架。
- 谷歌从 2017 年 1 月推出的 Chrome 56 开始,对未进行 HTTPS 加密的网址链接亮出风险提示,即在地址栏的显著位置提醒用户“此网页不安全”。
- 腾讯微信小程序的官方需求文档要求后台使用 HTTPS 请求进行网络通信,不满足条件的域名和协议无法请求。
而某些网站虽然使用了 HTTPS 协议还是会被浏览器提示不安全,例如我们在 Chrome 浏览器里面打开 12306,链接为:https://www.12306.cn/,这时浏览器就会提示“您的连接不是私密连接”这样的话,如图 2-3 所示:
图 2-3 12306 页面
这是因为 12306 的 CA 证书是中国铁道部自己颁发给自己的,而这个证书是不被官方机构认可的,所以这里证书验证就不会通过而提示这样的话,但是实际上它的数据传输依然是经过 SSL 加密的。我们如果要爬取这样的站点就需要设置忽略证书的选项,否则会提示 SSL 链接错误,在后文会进行详细说明。
4. HTTP请求过程
我们在浏览器中输入一个 URL,回车之后便会在浏览器中观察到页面内容,实际上这个过程是浏览器向网站所在的服务器发送了一个 Request,即请求,网站服务器接收到这个 Request 之后进行处理和解析,然后返回对应的一个 Response,即响应,然后传回给浏览器,Response里面就包含了页面的源代码等内容,浏览器再对其进行解析便将网页呈现了出来,模型如图 2-4 所示:
图 2-4 模型图
此处客户端即代表我们自己的 PC 或手机浏览器,服务器即要访问的网站所在的服务器。
为了更直观地地说明这个的过程,我们在这里用 Chrome 浏览器的开发者模式下的 Network 监听组件来做下演示,它可以显示访问当前请求网页时发生的所有网络请求和响应。
打开 Chrome 浏览器,右键点击检查,或按下快捷键 F12 即可打开浏览器的开发者工具,我们在这里访问百度:http://www.baidu.com/,输入该 URL,敲击回车访问这个页面,观察一下在这个过程中发生了怎样的网络请求,这时我们可以看到在 Network 页面的下方出现了一个个的条目,那么这一个条目就代表一次发送 Request 和接收 Response 的过程,如图 2-5 所示:
图 2-5 Network 面板
我们观察第一个网络请求,即 www.baidu.com,如图 2-6 所示:
图 2-6 网络请求记录
这一个条目的各列分别代表:
- 第一列 Name,即 Request 的名称。一般会用URL的最后一部分内容当做名称。
- 第二列 Status,即 Response 的状态码。这里显示为 200,代表 Response 是正常的,通过状态码我们可以判断发送了 Request 之后是否得到了正常的 Response。
- 第三列 Type,即 Request 请求的文档类型。这里为 document,代表我们这次请求的是一个 HTML 文档,内容就是一些 HTML 代码。
- 第四列 Initiator,即请求源。用来标记 Request 是由哪个对象或进程发起的。
- 第五列 Size,即从服务器下载的文件和请求的资源大小。如果是从缓存中取得的资源则该列会显示 from cache。
- 第六列 Time,即发起 Request 到获取到 Response 所用的总时间。
- 第七列 Timeline,即网络请求的可视化瀑布流。
我们点击这个条目即可看到其更详细的信息,如图 2-7 所示:
图 2-7 详细信息
首先是 General 部分,Request URL 为 Request 的 URL,Request Method 为请求的方法,Status Code 为响应状态码,Remote Address 为远程服务器的地址和端口,Referrer Policy 为 Referrer 判别策略。
再继续往下看可以看到有一个 Response Headers 和一个 Request Headers,这分别代表响应头和请求头,请求头里面带有许多请求信息,例如浏览器标识、Cookies、Host 等信息,这是 Request 的一部分,服务器会根据请求头内的信息判断请求是否合法,进而作出对应的响应,返回 Response,那么在图中看到的 Response Headers 就是 Response 的一部分,例如其中包含了服务器的类型、文档类型、日期等信息,浏览器接受到 Response 后,会解析响应内容,进而呈现网页内容。
下面我们分别来介绍一下请求 Request 和响应 Response 都包含了哪些内容,在这里进行对其组成进行总结:
5. Request
Request,即请求,由客户端向服务端发出。可以将 Request 划分为四部分内容:Request Method、Request URL、Request Headers、Request Body,即请求方式、请求链接、请求头、请求体。
Request Method
请求方式,请求方式常见的有两种类型,GET 和 POST。
我们在浏览器中直接输入一个 URL 并回车,这便发起了一个 GET 请求,请求的参数会直接包含到 URL 里,例如百度搜索 Python,这就是一个 GET 请求,链接为:https://www.baidu.com/s?wd=Python,URL 中包含了请求的参数信息,这里参数 wd 就是要搜寻的关键字。POST 请求大多为表单提交发起,如一个登录表单,输入用户名密码,点击登录按钮,这通常会发起一个 POST 请求,其数据通常以 Form Data 即表单的形式传输,不会体现在 URL 中。
GET 和 POST 请求方法有如下区别:
- GET 方式请求中参数是包含在 URL 里面的,数据可以在 URL 中看到,而 POST 请求的 URL 不会包含这些数据,数据都是通过表单的形式传输,会包含在 Request Body 中。
- GET 方式请求提交的数据最多只有 1024 字节,而 POST 方式没有限制。
所以一般来说,网站登录验证的时候,需要提交用户名密码,这里包含了敏感信息,使用GET方式请求的话密码就会暴露在URL里面,造成密码泄露,所以这里最好以POST方式发送。文件的上传时,由于文件内容比较大,也会选用POST方式。
我们平常遇到的绝大部分请求都是 GET 或 POST 请求,另外还有一些请求方式,如 HEAD、PUT、DELETE、OPTIONS、CONNECT、TRACE,我们简单将其总结如下:
方法 | 描述 |
---|---|
GET | 请求指定的页面信息,并返回实体主体。 |
HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头。 |
POST | 向指定资源提交数据进行处理请求,数据被包含在请求体中。 |
PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
DELETE | 请求服务器删除指定的页面。 |
CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
OPTIONS | 允许客户端查看服务器的性能。 |
TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
本表参考:http://www.runoob.com/http/http-methods.html。
Request URL
顾名思义,就是请求的网址,即统一资源定位符,用 URL 可以唯一确定我们想请求的资源。
Request Headers
请求头,用来说明服务器要使用的附加信息,比较重要的信息有 Cookie、Referer、User-Agent 等,下面将一些常用的头信息说明如下:
- Accept,请求报头域,用于指定客户端可接受哪些类型的信息。
- Accept-Language,指定客户端可接受的语言类型。
- Accept-Encoding,指定客户端可接受的内容编码。
- Host,用于指定请求资源的主机 IP 和端口号,其内容为请求 URL 的原始服务器或网关的位置。从 HTTP 1.1 版本开始,Request 必须包含此内容。
- Cookie,也常用复数形式 Cookies,是网站为了辨别用户进行 Session 跟踪而储存在用户本地的数据。Cookies 的主要功能就是维持当前访问会话,例如我们输入用户名密码登录了某个网站,登录成功之后服务器会用 Session 保存我们的登录状态信息,后面我们每次刷新或请求该站点的其他页面时会发现都是保持着登录状态的,在这里就是 Cookies 的功劳,Cookies 里有信息标识了我们所对应的服务器的 Session 会话,每次浏览器在请求该站点的页面时都会在请求头中加上 Cookies 并将其发送给服务器,服务器通过 Cookies 识别出是我们自己,并且查出当前状态是登录的状态,所以返回的结果就是登录之后才能看到的网页内容。
- Referer,此内容用来标识这个请求是从哪个页面发过来的,服务器可以拿到这一信息并做相应的处理,如做来源统计、做防盗链处理等。
- User-Agent,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、浏览器及版本等信息。在做爬虫时加上此信息可以伪装为浏览器,如果不加很可能会被识别出为爬虫。
- Content-Type,即 Internet Media Type,互联网媒体类型,也叫做 MIME 类型,在 HTTP 协议消息头中,使用它来表示具体请求中的媒体类型信息。例如 text/html 代表 HTML 格式,image/gif 代表 GIF 图片,application/json 代表 Json 类型,更多对应关系可以查看此对照表:http://tool.oschina.net/commons。
因此,Request Headers 是 Request 等重要组成部分,在写爬虫的时候大部分情况都需要设定 Request Headers。
Request Body
即请求体,一般承载的内容是 POST 请求中的 Form Data,即表单数据,而对于 GET 请求 Request Body 则为空。
例如在这里我登录 GitHub 时捕获到的 Request 和 Response 如图 2-8 所示:
图 2-8 详细信息
在登录之前我们填写了用户名和密码信息,提交时就这些内容就会以 Form Data 的形式提交给服务器,此时注意 Request Headers 中指定了 Content-Type 为 application/x-www-form-urlencoded,只有设置 Content-Type 为 application/x-www-form-urlencoded 才会以 Form Data 形式提交,另外我们也可以将 Content-Type 设置为 application/json 来提交 Json 数据,或者设置为 multipart/form-data 来上传文件。
下面列出了 Content-Type 和 POST 提交数据方式的关系:
Content-Type | 提交数据方式 |
---|---|
application/x-www-form-urlencoded | Form 表单提交 |
multipart/form-data | 表单文件上传提交 |
application/json | 序列化 Json 数据提交 |
text/xml | XML 数据提交 |
在爬虫中如果我们要构造 POST 请求需要注意这几种 Content-Type,了解各种请求库的各个参数设置时使用的是哪种 Content-Type,不然可能会导致 POST 提交后得不到正常的 Response。
以上便是对 Request 各部分内容的解释。
6. Response
Response,即响应,由服务端返回给客户端。Response 可以划分为三部分,Response Status Code、Response Headers、Response Body。
Response Status Code
响应状态码,此状态码表示了服务器的响应状态,如 200 则代表服务器正常响应,404 则代表页面未找到,500 则代表服务器内部发生错误。在爬虫中,我们可以根据状态码来判断服务器响应状态,如判断状态码为 200,则证明成功返回数据,再进行进一步的处理,否则直接忽略。
下面用表格列出了常见的错误代码及错误原因:
状态码 | 说明 | 详情 |
---|---|---|
100 | 继续 | 请求者应当继续提出请求。服务器已收到请求的一部分,正在等待其余部分。 |
101 | 切换协议 | 请求者已要求服务器切换协议,服务器已确认并准备切换。 |
200 | 成功 | 服务器已成功处理了请求。 |
201 | 已创建 | 请求成功并且服务器创建了新的资源。 |
202 | 已接受 | 服务器已接受请求,但尚未处理。 |
203 | 非授权信息 | 服务器已成功处理了请求,但返回的信息可能来自另一来源。 |
204 | 无内容 | 服务器成功处理了请求,但没有返回任何内容。 |
205 | 重置内容 | 服务器成功处理了请求,内容被重置。 |
206 | 部分内容 | 服务器成功处理了部分请求。 |
300 | 多种选择 | 针对请求,服务器可执行多种操作。 |
301 | 永久移动 | 请求的网页已永久移动到新位置,即永久重定向。 |
302 | 临时移动 | 请求的网页暂时跳转到其他页面,即暂时重定向。 |
303 | 查看其他位置 | 如果原来的请求是 POST,重定向目标文档应该通过 GET 提取。 |
304 | 未修改 | 此次请求返回的网页未修改,继续使用上次的资源。 |
305 | 使用代理 | 请求者应该使用代理访问该网页。 |
307 | 临时重定向 | 请求的资源临时从其他位置响应。 |
400 | 错误请求 | 服务器无法解析该请求。 |
401 | 未授权 | 请求没有进行身份验证或验证未通过。 |
403 | 禁止访问 | 服务器拒绝此请求。 |
404 | 未找到 | 服务器找不到请求的网页。 |
405 | 方法禁用 | 服务器禁用了请求中指定的方法。 |
406 | 不接受 | 无法使用请求的内容响应请求的网页。 |
407 | 需要代理授权 | 请求者需要使用代理授权。 |
408 | 请求超时 | 服务器请求超时。 |
409 | 冲突 | 服务器在完成请求时发生冲突。 |
410 | 已删除 | 请求的资源已永久删除。 |
411 | 需要有效长度 | 服务器不接受不含有效内容长度标头字段的请求。 |
412 | 未满足前提条件 | 服务器未满足请求者在请求中设置的其中一个前提条件。 |
413 | 请求实体过大 | 请求实体过大,超出服务器的处理能力。 |
414 | 请求 URI 过长 | 请求网址过长,服务器无法处理。 |
415 | 不支持类型 | 请求的格式不受请求页面的支持。 |
416 | 请求范围不符 | 页面无法提供请求的范围。 |
417 | 未满足期望值 | 服务器未满足期望请求标头字段的要求。 |
500 | 服务器内部错误 | 服务器遇到错误,无法完成请求。 |
501 | 未实现 | 服务器不具备完成请求的功能。 |
502 | 错误网关 | 服务器作为网关或代理,从上游服务器收到无效响应。 |
503 | 服务不可用 | 服务器目前无法使用。 |
504 | 网关超时 | 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 |
505 | HTTP 版本不支持 | 服务器不支持请求中所用的 HTTP 协议版本。 |
Response Headers
响应头,其中包含了服务器对请求的应答信息,如 Content-Type、Server、Set-Cookie 等,下面将一些常用的头信息说明如下:
- Date,标识 Response 产生的时间。
- Last-Modified,指定资源的最后修改时间。
- Content-Encoding,指定 Response 内容的编码。
- Server,包含了服务器的信息,名称,版本号等。
- Content-Type,文档类型,指定了返回的数据类型是什么,如text/html 则代表返回 HTML 文档,application/x-javascript 则代表返回 JavaScript 文件,image/jpeg 则代表返回了图片。
- Set-Cookie,设置Cookie,Response Headers 中的 Set-Cookie即告诉浏览器需要将此内容放在 Cookies 中,下次请求携带 Cookies 请求。
- Expires,指定 Response 的过期时间,使用它可以控制代理服务器或浏览器将内容更新到缓存中,如果再次访问时,直接从缓存中加载,降低服务器负载,缩短加载时间。
Resposne Body
即响应体,最重要的当属响应体内容了,响应的正文数据都是在响应体中,如请求一个网页,它的响应体就是网页的 HTML 代码,请求一张图片,它的响应体就是图片的二进制数据。所以最主要的数据都包含在响应体中了,我们做爬虫请求网页后要解析的内容就是解析响应体,如图 2-9 所示:
图 2-9 响应体内容
我们在浏览器开发者工具中点击 Preview,就可以看到网页的源代码,这也就是响应体内容,是解析的目标。
我们在做爬虫时主要解析的内容就是 Resposne Body,通过 Resposne Body 我们可以得到网页的源代码、Json 数据等等,然后从中做相应内容的提取。
以上便是 Response 的组成部分。
7. 结语
本节我们了解了 HTTP 的基本原理,通过如上描述,我们应该对访问网页背后的请求和响应过程有了大体的认识,本节涉及到的知识点需要好好掌握,在后面分析网页请求的时候会经常用到。
2.2 Web网页基础
我们平时用浏览器访问网站的时候,一个个站点形形色色,页面也各不相同,但有没有想过它是为何才能呈现出这个样子的?
那么本节我们就来了解一下网页的基本组成、结构、节点等内容。
1. 网页的组成
网页可以分为三大部分,HTML、CSS、JavaScript,我们把网页比作一个人的话,HTML 相当于骨架,JavaScript 则相当于肌肉,CSS 则相当于皮肤,三者结合起来才能形成一个完善的网页,下面我们分别来介绍一下三部分的功能。
HTML
HTML 是用来描述网页的一种语言,其全称叫做 Hyper Text Markup Language,即超文本标记语言。网页包括文字、按钮、图片、视频等各种复杂的元素,其基础架构就是 HTML。不同类型的文字通过不同类型的标签来表示,如图片用 img 标签表示,视频用 video 标签来表示,段落用 p 标签来表示,它们之间的布局又常通过布局标签 div 嵌套组合而成,各种标签通过不同的排列和嵌套才形成了网页的框架。
我们在 Chrome 浏览器中打开百度,右键单击审查元素或按 F12 打开开发者模式,切换到 Elements 选项卡即可看到网页的源代码,如图 2-10 所示
这就是 HTML,整个网页就是由各种不同的标签嵌套组合而成的,这些不同标签定义的节点元素相互嵌套和组合形成了复杂的层次关系,就形成了网页的架构。
CSS
HTML 定义了网页的结构,但是只有 HTML 页面的布局会不美观,可能只是简单的节点元素的排列,那么为了让网页看起来更好看一点,在这里就借助于 CSS。
CSS,全称叫做 Cascading Style Sheets,即层叠样式表。“层叠”是指当在 HTML 中引用了数个样式文件,并且样式发生冲突时,浏览器能依据层叠顺序处理。“样式”指网页中文字大小、颜色、元素间距、排列等格式。
CSS是目前唯一的网页页面排版样式标准,有了它的帮助,页面才会变得更为美观。
在上图的右侧即为 CSS,例如:
#head_wrapper.s-ps-islite .s-p-top {
position: absolute;
bottom: 40px;
width: 100%;
height: 181px;
}
这就是一个 CSS 样式,在大括号前面是一个 CSS 选择器,此选择器的意思是选中 id 为 head_wrapper 且 class 为 s-ps-islite 内部的 class 为 s-p-top 的元素。大括号内部写的就是一条条样式规则,例如 position 指定了这个元素的布局方式为绝对布局,bottom 指定元素的下边距为 40 像素,width 指定了宽度为 100% 占满父元素,height 则指定了元素的高度。也就是说我们将一些位置、宽度、高度等样式配置统一写成这样的形式,大括号括起来,然后开头再加上一个 CSS 选择器,就代表这一个样式对 CSS 选择器选中的元素生效,这样元素就会根据此样式来展示了。
所以在网页中,一般会统一定义整个网页的样式规则,写入到 CSS 文件,其后缀名为 css,在 HTML 中只需要用 link 标签即可引入写好的 CSS 文件,这样整个页面就会变得美观优雅。
JavaScript
JavaScript,简称为 JS,是一种脚本语言,HTML 和 CSS 配合使用,提供给用户的只是一种静态的信息,缺少交互性。我们在网页里可能会看到一些交互和动画效果,如下载进度条、提示框、轮播图等,这通常就是 JavaScript 的功劳。它的出现使得用户与信息之间不只是一种浏览与显示的关系,而是实现了一种实时、动态、交互的页面功能。
JavaScript 通常也是以单独的文件形式加载的,后缀名为 js,在 HTML 中通过 script 标签即可引入。
例如:
<script src="jquery-2.1.0.js"></script>
因此综上所属,HTML 定义了网页的内容和结构,CSS 描述了网页的布局,JavaScript 定义了网页的行为。
这就是网页的三大基本组成。
2. 网页的结构
我们首先用一个例子来感受一下 HTML 的基本结构。新建一个文本文件,名称可以自取,后缀名为 html,内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>This is a Demo</title>
</head>
<body>
<div id="container">
<div class="wrapper">
<h2 class="title">Hello World</h2>
<p class="text">Hello, this is a paragraph.</p>
</div>
</div>
</body>
</html>
这就是一个最简单的 HTML 实例,开头是 DOCTYPE 定义了文档类型,其次最外层是 html 标签,最后还有对应的结尾代表标签闭合,其内部是 head 标签和 body 标签,分别代表网页头和网页体,它们也分别需要尾标签表示闭合。head 标签内定义了一些页面的配置和引用,如:
<meta charset="UTF-8">
它指定了网页的编码为 UTF-8。
title 标签则定义了网页的标题,会显示在网页的选项卡中,不会显示在网页的正文中。body 标签内则是在网页正文中显示的内容,div 标签定义了网页中的区块,它的 id 是 container,这是一个非常常用的属性,且 id 的内容在网页中是唯一的,我们可以通过 id 来取到这个区块。然后在此区块内又有一个 div 标签,它的 class 为 wrapper,这也是一个非常常用的属性,经常与 CSS 配合使用来设定样式。然后此区块内部又有一个 h2 标签,这代表一个二级标题,另外还有一个 p 标签,这代表一个段落,它们二者内部直接写入相应的内容即可在网页重呈现出来,它们也有各自的 class 属性。
我们将代码保存之后在浏览器中打开该文件,可以看到如下内容,如图 2-11 所示:
图 2-11 运行结果
可以看到在选项卡上显示了 This is a Demo 字样,这是我们在 head 里面的 title 里定义的文字,它显示在了网页选项卡里。而网页正文是 body 标签内部定义的各个元素生成的,图中可以看到网页中显示了二级标题和段落。
如上实例便是网页的一般结构,一个网页标准形式都是 html 标签内嵌套 head 和 body 标签,head 内定义网页的配置和引用,body 内定义网页的正文。
3. 节点及节点关系
在 HTML 中,所有标签定义的内容都是节点,它们构成了一个 HTML DOM 树。
我们先看下什么是 DOM,DOM 是 W3C(万维网联盟)的标准。
DOM,英文全称 Document Object Model,即文档对象模型。它定义了访问 HTML 和 XML 文档的标准:
W3C 文档对象模型 (DOM) 是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。
W3C DOM 标准被分为 3 个不同的部分:
- 核心 DOM - 针对任何结构化文档的标准模型
- XML DOM - 针对 XML 文档的标准模型
- HTML DOM - 针对 HTML 文档的标准模型
根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点:
- 整个文档是一个文档节点
- 每个 HTML 元素是元素节点
- HTML 元素内的文本是文本节点
- 每个 HTML 属性是属性节点
- 注释是注释节点
HTML DOM 将 HTML 文档视作树结构,这种结构被称为节点树,如图 2-12 所示:
图 2-12 节点树
通过 HTML DOM,树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 节点元素均可被修改,也可以被创建或删除。
节点树中的节点彼此拥有层级关系。我们常用 parent(父)、child(子)和 sibling(兄弟)等术语用于描述这些关系。父节点拥有子节点,同级的子节点被称为兄弟节点。
在节点树中,顶端节点被称为根(root),除了根节点之外每个节点都有父节点,同时可拥有任意数量的子节点或兄弟节点。
图 2-13 展示了节点树以及节点之间的关系:
图 2-13 节点树及节点关系
本段参考 W3SCHOOL,链接:http://www.w3school.com.cn/htmldom/dom_nodes.asp。
4. 选择器
我们知道网页由一个个节点组成,CSS 选择器会根据不同的节点设置不同的样式规则,那么我们怎样来定义是哪些节点呢?
在 CSS 中是使用了 CSS 选择器来定位节点的,例如上例中有个 div 节点的 id 为 container,那么我们就可以用 CSS 选择器表示为 #container,# 开头代表选择 id,其后紧跟 id 的名称。另外如果我们想选择 class 为 wrapper 的节点,便可以使用 .wrapper,. 开头代表选择 class,其后紧跟 class 的名称。另外还有一种选择方式是根据标签名筛选,例如我们想选择二级标题,直接用 h2 即可选择。如上是最常用的三种选择表示,分别是根据 id、class、标签名筛选,请牢记它们的写法。
另外 CSS 选择器还支持嵌套选择,各个选择器之间加上空格分隔开便可以代表嵌套关系,如 #container .wrapper p 则代表选择 id 为 container 内部的 class 为 wrapper 内部的 p 节点。另外如果不加空格则代表并列关系,如 div#container .wrapper p.text 代表选择 id 为 container 的 div 节点内部的 class 为 wrapper 节点内部的 class 为 text 的 p 节点。这就是 CSS 选择器,其筛选功能还是非常强大的。
另外 CSS 选择器还有一些其他的语法规则,在这里整理如下:
选择器 | 例子 | 例子描述 |
---|---|---|
.class | .intro | 选择 class="intro" 的所有节点。 |
#id | #firstname | 选择 id="firstname" 的所有节点。 |
* | * | 选择所有节点。 |
element | p | 选择所有 p 节点。 |
element,element | div,p | 选择所有 div 节点和所有 p 节点。 |
element element | div p | 选择 div 节点内部的所有 p 节点。 |
element>element | div>p | 选择父节点为 div 节点的所有 p 节点。 |
element+element | div+p | 选择紧接在 div 节点之后的所有 p 节点。 |
[attribute] | [target] | 选择带有 target 属性所有节点。 |
[attribute=value] | [target=blank] | 选择 target="blank" 的所有节点。 |
[attribute~=value] | [title~=flower] | 选择 title 属性包含单词 "flower" 的所有节点。 |
:link | a:link | 选择所有未被访问的链接。 |
:visited | a:visited | 选择所有已被访问的链接。 |
:active | a:active | 选择活动链接。 |
:hover | a:hover | 选择鼠标指针位于其上的链接。 |
:focus | input:focus | 选择获得焦点的 input 节点。 |
:first-letter | p:first-letter | 选择每个 p 节点的首字母。 |
:first-line | p:first-line | 选择每个 p 节点的首行。 |
:first-child | p:first-child | 选择属于父节点的第一个子节点的每个 p 节点。 |
:before | p:before | 在每个 p 节点的内容之前插入内容。 |
:after | p:after | 在每个 p 节点的内容之后插入内容。 |
:lang(language) | p:lang | 选择带有以 "it" 开头的 lang 属性值的每个 p 节点。 |
element1~element2 | p~ul | 选择前面有 p 节点的每个 ul 节点。 |
[attribute^=value] | a[src^="https"] | 选择其 src 属性值以 "https" 开头的每个 a 节点。 |
[attribute$=value] | a[src$=".pdf"] | 选择其 src 属性以 ".pdf" 结尾的所有 a 节点。 |
[attribute*=value] | a[src*="abc"] | 选择其 src 属性中包含 "abc" 子串的每个 a 节点。 |
:first-of-type | p:first-of-type | 选择属于其父节点的首个 p 节点的每个 p 节点。 |
:last-of-type | p:last-of-type | 选择属于其父节点的最后 p 节点的每个 p 节点。 |
:only-of-type | p:only-of-type | 选择属于其父节点唯一的 p 节点的每个 p 节点。 |
:only-child | p:only-child | 选择属于其父节点的唯一子节点的每个 p 节点。 |
:nth-child(n) | p:nth-child | 选择属于其父节点的第二个子节点的每个 p 节点。 |
:nth-last-child(n) | p:nth-last-child | 同上,从最后一个子节点开始计数。 |
:nth-of-type(n) | p:nth-of-type | 选择属于其父节点第二个 p 节点的每个 p 节点。 |
:nth-last-of-type(n) | p:nth-last-of-type | 同上,但是从最后一个子节点开始计数。 |
:last-child | p:last-child | 选择属于其父节点最后一个子节点每个 p 节点。 |
:root | :root | 选择文档的根节点。 |
:empty | p:empty | 选择没有子节点的每个 p 节点(包括文本节点)。 |
:target | #news:target | 选择当前活动的 #news 节点。 |
:enabled | input:enabled | 选择每个启用的 input 节点。 |
:disabled | input:disabled | 选择每个禁用的 input 节点 |
:checked | input:checked | 选择每个被选中的 input 节点。 |
:not(selector) | p:not | 选择非 p 节点的每个节点。 |
::selection | ::selection | 选择被用户选取的节点部分。 |
另外还有一种比较常用的选择器是 XPath,此种选择方式在后文会详细介绍。
5. 结语
本节介绍了网页的基本结构和节点关系,了解了这些内容我们才有更加清晰的思路去解析和提取网页内容。
2.3 爬虫基本原理
1. 爬虫概述
简单来说,爬虫就是获取网页并提取和保存信息的自动化程序,接下来对各个点进行说明:
获取网页
爬虫首先要做的工作就是获取网页,在这里获取网页即获取网页的源代码,源代码里面必然包含了网页的部分有用的信息,所以只要把源代码获取下来了,就可以从中提取我们想要的信息了。
我们向网站的服务器发送一个 Request,返回的 Response 的 Body 便是网页源代码。所以最关键的部分就是构造一个 Request 并发送给服务器,然后接收到 Response 并将其解析出来,Python 里面提供了许多库来帮助我们实现这个操作,如 Urllib、Requests 等,我们可以用这些库来帮助我们实现 HTTP 请求操作,Request 和 Response 都可以用类库提供的数据结构来表示,得到 Response 之后只需要解析数据结构中的 Body 部分即可,即得到网页的源代码,这样我们可以用程序来实现获取网页的过程了。
提取信息
我们在第一步获取了网页源代码之后,接下来的工作就是分析网页源代码,从中提取我们想要的数据,首先最通用的方法便是采用正则表达式提取,这是一个万能的方法,但是在构造正则表达式的时候比较复杂且容易出错。
另外由于网页的结构是有一定规则的,所以还有一些根据网页节点属性、CSS 选择器或 XPath 来提取网页信息的库,如 BeautifulSoup、PyQuery、LXML 等,使用这些库可以高效快速地从中提取网页信息,如节点的属性、文本值等内容。
提取信息是爬虫非常重要的部分,它可以使杂乱的数据变得清晰条理,以便于我们后续在对数据进行处理和分析。
保存数据
提取信息之后我们一般会将提取到的数据保存到某处以便后续数据处理使用。保存形式有多种多样,如可以简单保存为 TXT 文本或 Json 文本,也可以保存到数据库,如 MySQL、MongoDB 等,也可保存至远程服务器,如借助 Sftp 进行操作等。
自动化程序
说到自动化程序,意思即是说爬虫可以代替人来完成这些操作。首先我们手工当然是可以提取这些信息的,但是当量特别大或者想快速获取大量数据的话,肯定还是借助于程序。所以爬虫就是代替我们来完成这份爬取数据的工作的自动化程序,它可以在抓取过程中进行各种异常处理、错误重试等操作,确保爬取持续高效地运行。
2. 能抓怎样的数据
在网页中我们能看到各种各样的信息,最常见的便是常规网页,其都对应着 HTML 代码,而最常见的抓取便是抓取 HTML 源代码。
另外可能有些网页返回的不是 HTML 代码,而是返回一个 Json 字符串,API 接口大多采用这样的形式,方便数据的传输和解析,这种数据同样可以抓取,而且数据提取更加方便。
此外我们还可以看到各种二进制数据,如图片、视频、音频等等,我们可以利用爬虫将它们的二进制数据抓取下来,然后保存成对应的文件名即可。
另外我们还可以看到各种扩展名的文件,如 CSS、JavaScript、配置文件等等,这些其实也是最普通的文件,只要在浏览器里面访问到,我们就可以将其抓取下来。
以上的内容其实都对应着各自的URL,是基于 HTTP 或 HTTPS 协议的,只要是这种数据爬虫都可以进行抓取。
3. JavaScript渲染页面
有时候我们在用 Urllib 或 Requests 抓取网页时,得到的源代码实际和浏览器中看到的是不一样的。
这个问题是一个非常常见的问题,现在网页越来越多地采用 Ajax、前端模块化工具来构建网页,整个网页可能都是由 JavaScript 渲染出来的,意思就是说原始的 HTML 代码就是一个空壳,例如:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>This is a Demo</title>
</head>
<body>
<div id="container">
</div>
</body>
<script src="app.js"></script>
</html>
body 节点里面只有一个 id 为 container 的节点,但是注意到在 body 节点后引入了一个 app.js,这个便负责了整个网站的渲染。
在浏览器打开这个页面时,首先会加载这个 HTML 内容,接着浏览器会发现其中里面引入了一个 app.js 文件,然后浏览器便会接着去请求这个文件,获取到该文件之后便会执行其中的 JavaScript 代码,而 JavaScript 则会改变 HTML 中的节点,向内添加内容,最后得到完整的页面。
但是在用 Urllib 或 Requests 等库来请求当前页面时,我们得到的只是这个 HTML 代码,它不会帮助我们去继续加载这个 JavaScript 文件,这样也就看不到浏览器中看到的内容了。
这也解释了为什么有时我们得到的源代码和浏览器中看到的是不一样的。
所以使用基本 HTTP 请求库得到的结果源代码可能跟浏览器中的页面源代码不太一样。对于这样的情况,我们可以分析其后台 Ajax 接口,也可使用 Selenium、Splash 这样的库来实现模拟 JavaScript 渲染,这样我们便可以爬取 JavaScript 渲染的网页的内容了。
在后文我们会详细介绍对于 JavaScript 渲染的网页的采集方法。
4. 结语
本节介绍了爬虫的一些基本原理,了解了如上内容可以帮助我们在后面编写爬虫的时候更加得心应手。
2.4 Session和Cookies
在浏览网站的过程中我们经常会遇到需要登录的情况,有些页面只有登录之后我们才可以访问,而且登录之后可以连续访问很多次网站,但是有时候过一段时间就会需要重新登录。还有一些网站有时在我们打开浏览器的时候就自动登录了,而且很长的时间都不会失效,这种情况又是为什么?其实这里面涉及到 Session 和 Cookies 的相关知识,本节我们就来揭开它们的神秘面纱。
1. 静态网页和动态网页
在开始之前我们需要先了解一下静态网页和动态网页的概念。
还是前文中的示例代码,内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>This is a Demo</title>
</head>
<body>
<div id="container">
<div class="wrapper">
<h2 class="title">Hello World</h2>
<p class="text">Hello, this is a paragraph.</p>
</div>
</div>
</body>
</html>
这是最基本的 HTML 代码,我们将其保存为一个 html 文件,然后把它放在某台具有固定公网 IP 的主机上,主机上装上 Apache 或 Nginx 等服务器,这样这台主机就可以作为服务器了,其他人便可以通过访问服务器看到这个页面了,这就搭建了一个最简单的网站。
这种网页的内容是 HTML 代码编写的,文字、图片等内容均是通过写好的 HTML 代码来指定的,这种页面叫做静态网页。
这种网页加载速度快,编写简单,但是存在很大的缺陷,如可维护性差,不能根据 URL 灵活多变地显示内容等,例如我们想要给这个网页的 URL 传入一个 name 参数,让其在网页中显示出来,是无法做到的。
所以动态网页应运而生,它可以动态解析 URL 中参数的变化,关联数据库并动态地呈现不同的页面内容,非常灵活多变,我们现在遇到的大多数网站都是动态网站,它们不再是一个简单的 HTML,而是可能由 JSP、PHP、Python 等语言编写的,功能相比静态网页强大和丰富太多太多。
动态网站还可以实现用户登录注册的功能,再回到开篇提到的问题,很多页面是需要登录之后才可以查看的,按照一般的逻辑来说,我们输入用户名密码登录之后,肯定是拿到了一种类似凭证的东西,有了它我们才能保持登录状态,才能访问登录之后才能看到的页面。
这种神秘的凭证就是 Session 和 Cookies 共同产生的结果,下面我们来一探它们的究竟。
2. 无状态HTTP
在了解 Session 和 Cookies 之前,我们还需要了解 HTTP 的一个特点,叫做无状态。
HTTP 的无状态是指 HTTP 协议对事务处理是没有记忆能力的,也就是说服务器不知道客户端是什么状态。当我们向服务器发送一个 Requset 后,服务器解析此 Request,然后返回对应的 Response,服务器负责完成这个过程,而且这个过程是完全独立的,服务器不会记录前后状态的变化,也就是缺少状态记录,这意味着如果后续需要处理需要前面的信息,则它必须要重传,这也导致了需要额外传递一些前面的重复 Request 才能获取后续 Response,然而这种效果显然不是我们想要的。为了保持前后状态,我们肯定不能将前面的请求全部重传一次,这太浪费资源了,对于这种需要用户登录的页面来说,更是棘手。
所以,这时候,两个用于保持 HTTP 连接状态的技术就出现了,它们分别是 Session 和 Cookies,Session 在服务端,也就是网站的服务器,用来保存用户的会话信息,Cookies 在客户端,也可以理解为浏览器端,有了 Cookies,浏览器在下次访问网页时会自动附带上它发送给服务器,服务器通过识别 Cookies 并鉴定出是哪个用户,然后再判断用户是否是登录状态,然后返回对应的 Response。
所以我们可以理解为 Cookies 里面保存了登录的凭证,有了它我们只需要在下次请求携带 Cookies 发送 Request 而不必重新输入用户名、密码等信息重新登录了。
因此在爬虫中,有时候处理需要登录才能访问的页面时,我们一般会直接将登录成功后获取的 Cookies 放在 Request Headers 里面直接请求,而不必重新模拟登录。
好,大体了解什么是 Session 和 Cookies 之后,我们来详细剖析一下它们的原理。
2. Session
Session,即会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 Session。
而在 Web 中 Session 对象用来存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。
3. Cookies
Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 Session 跟踪而储存在用户本地终端上的数据。
会话维持
那么利用 Cookies 我们是怎样来保持状态的呢?当客户端第一次请求服务器时,服务器会返回一个 Headers 中带有 Set-Cookie 字段的 Response 给客户端,用来标记是哪一个用户,客户端浏览器会把Cookies 保存起来。当浏览器下一次再请求该网站时,浏览器会把此Cookies 放到 Request Headers 一起提交给服务器,Cookies 携带了 Session ID 信息,服务器检查该 Cookies 即可找到对应的 Session 是什么,然后再判断 Session 来以此来辨认用户状态。
所以我们在登录某个网站的时候,登录成功后服务器会告诉客户端设置哪些 Cookies 信息,在后续访问页面时客户端会把 Cookies 发送给服务器,服务器再找到对应的 Session 加以判断,如果 Session 中的某些设置登录状态的变量是有效的,那就证明用户是处于登录状态的,即可返回登录之后才可以查看的网页内容,浏览器进行解析便可以看到了。
反之,如果传给服务器的 Cookies 是无效的,或者 Session 已经过期了,我们将不能继续访问页面,可能会收到错误的 Response 或者跳转到登录页面重新登录。
所以 Cookies 和 Session 需要配合,一个处于客户端,一个处于服务端,二者共同协作,就实现了登录会话控制。
属性结构
接下来我们来看看 Cookies 都有哪些内容,在这里以知乎为例,在浏览器开发者工具中打开 Application 选项卡,然后在左侧会有一个 Storage 部分,最后一项即为 Cookies,将其点开,可以看到类似如下内容,这些就是 Cookies,如图 2-14 所示:
图 2-14 Cookies 列表
我们可以看到 Cookies 有一个个条目,每个条目我们可以称之为 Cookie,取单数形式。它有这么几个属性:
- Name,即该 Cookie 的名称。Cookie 一旦创建,名称便不可更改
- Value,即该 Cookie 的值。如果值为 Unicode 字符,需要为字符编码。如果值为二进制数据,则需要使用 BASE64 编码。
- Max Age,即该 Cookie 失效的时间,单位秒,也常和 Expires 一起使用,通过它可以计算出其有效时间。Max Age 如果为正数,则该Cookie 在 Max Age 秒之后失效。如果为负数,则关闭浏览器时Cookie 即失效,浏览器也不会以任何形式保存该 Cookie。
- Path,即该 Cookie 的使用路径。如果设置为 /path/,则只有路径为 /path/ 的页面可以访问该 Cookie。如果设置为 /,则本域名下的所有页面都可以访问该 Cookie。
- Domain,即可以访问该 Cookie 的域名。例如如果设置为 .zhihu.com,则所有以 zhihu.com,结尾的域名都可以访问该Cookie。
- Size字段,即此 Cookie 的大小。
- Http字段,即 Cookie 的 httponly 属性。若此属性为 true,则只有在 HTTP Headers 中会带有此 Cookie 的信息,而不能通过 document.cookie 来访问此 Cookie。
- Secure,即该 Cookie 是否仅被使用安全协议传输。安全协议。安全协议有 HTTPS,SSL 等,在网络上传输数据之前先将数据加密。默认为 false。
以上便是 Cookies 的基本结构。
会话Cookie、持久Cookie
表面意思来说,会话 Cookie 就是把 Cookie 放在浏览器内存里,浏览器在关闭之后该 Cookie 即失效,持久 Cookie 则会保存到客户端的硬盘中,下次还可以继续使用,用于长久保持用户登录状态。
其实严格来说没有会话 Cookie 和持久 Cookie 之分,它只是由 Cookie 的 Max Age 或 Expires 字段决定了过期的时间,通过它浏览器可以计算出其有效时间。Max Age 如果为正数,则该 Cookie 在 Max Age 秒之后失效,如果 Max Age 特别大,那就会保存非常长的时间。如果为负数,则关闭浏览器时 Cookie 即失效,浏览器也不会以任何形式保存该 Cookie。
所以一些持久化登录的网站其实就是把 Cookie 的有效时间和 Session 有效期设置得比较长,下次我们再访问页面时仍然携带之前的 Cookies 就可以直接保持登录状态。
4. 常见误区
在谈论 Session 机制的时候,常常听到这样一种误解“只要关闭浏览器,Session 就消失了”,这种理解是错误的,可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对 Session 来说也是一样的,除非程序通知服务器删除一个 Session,否则服务器会一直保留,比如程序一般都是在我们做注销操作的时候才去删除 Session。
但是当我们关闭浏览器时,浏览器不会主动在关闭之前通知服务器它将要关闭,所以服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分 Session 机制都使用会话 Cookie 来保存 Session ID 信息,而关闭浏览器后 Cookies 就消失了,再次连接服务器时也就无法找到原来的 Session。如果服务器设置的 Cookies 被保存到硬盘上,或者使用某种手段改写浏览器发出的 HTTP 请求头,把原来的 Cookies 发送给服务器,则再次打开浏览器仍然能够找到原来的 Session ID,依旧还是可以保持登录状态的。
而且恰恰是由于关闭浏览器不会导致 Session 被删除,这就需要服务器为 Seesion 设置一个失效时间,当距离客户端上一次使用 Session 的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把 Session 删除以节省存储空间。
5. 参考资料
由于涉及到一些专业名词知识,本节的部分内容参考来源如下:
- Session 百度百科:https://baike.baidu.com/item/session/479100
- Cookie 百度百科:https://baike.baidu.com/item/cookie/1119
- HTTP Cookie 维基百科:https://en.wikipedia.org/wiki/HTTP_cookie
- Session和几种状态保持方案理解:http://www.mamicode.com/info-detail-46545.html
6.结语
本节介绍了 Session 和 Cookies 的基本知识,后面我们可能会需要爬取一些需要登录才可以访问的页面,这里就需要用到 Cookies 的相关知识,因此本节的内容同样需要好好掌握。
2.5 代理基本原理
我们在做爬虫的过程中经常会遇到这样的情况,最初爬虫正常运行,正常抓取数据,一切看起来都是那么的美好,然而一杯茶的功夫可能就会出现错误,比如 403 Forbidden,这时候打开网页一看,可能会看到“您的 IP 访问频率太高”这样的提示。出现这样的现象的原因是网站采取了一些反爬虫的措施,比如服务器会检测某个 IP 在单位时间内的请求次数,如果超过了这个阈值,那么会直接拒绝服务,返回一些错误信息,这种情况可以称之为封 IP,于是乎就成功把我们的爬虫禁掉了。
既然服务器检测的是某个 IP 单位时间的请求次数,那么我们借助某种方式来伪装我们的 IP,让服务器识别不出是由我们本机发起的请求,不就可以成功防止封 IP 了吗?
那么在这里一种有效的方式就是使用代理,使用它我们可以成功伪装 IP,避免本机 IP 被封禁的情况,在后文会有详细的代理使用的说明,在这之前我们需要先了解下代理的基本原理,它是怎样实现 IP 伪装的呢?本节就让我们先了解一下代理的概念。
1. 基本原理
我们常称呼的代理实际上指的就是代理服务器,英文叫做 Proxy Server,它的功能是代理网络用户去取得网络信息。形象地说,它是网络信息的中转站。在我们正常请求一个网站时,是发送了 Request 给 Web 服务器,Web 服务器把 Response 传回给我们。如果设置了代理服务器,实际上就是在本机和服务器之间搭建了一个桥,此时本机不是直接向 Web 服务器发起请求,而是向代理服务器发出请求, Request 会发送给代理服务器,然后由代理服务器再发送给 Web 服务器,然后由代理服务器再把 Web 服务器返回的 Response 转发给本机,这样我们同样可以正常访问网页,但这个过程 Web 服务器识别出的真实的 IP 就不再是我们本机的 IP 了,就成功实现了 IP 伪装,这就是代理的基本原理。
2. 代理的作用
那么代理有什么作用呢?我们可以简单列举如下:
- 突破自身 IP 访问限制,访问一些平时不能访问的站点。
- 访问一些单位或团体内部资源,如使用教育网内地址段免费代理服务器,就可以用于对教育网开放的各类 FTP 下载上传,以及各类资料查询共享等服务。
- 提高访问速度,通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲区中,当其他用户再访问相同的信息时, 则直接由缓冲区中取出信息,传给用户,以提高访问速度。
- 隐藏真实 IP,上网者也可以通过这种方法隐藏自己的 IP,免受攻击,对于爬虫来说,我们用代理就是为了隐藏自身 IP,防止自身的 IP 被封锁。
3. 爬虫代理
对于爬虫来说,由于爬虫爬取速度过快,在爬取过程中可能遇到同一个 IP 访问过于频繁的问题,网站就会让我们输入验证码或登录或者直接封锁 IP,这样会给爬取带来极大的不便。
所以使用代理隐藏真实的 IP,让服务器误以为是代理服务器的在请求自己。这样在爬取过程中通过不断更换代理,就不会被封锁,可以达到很好的爬取效果。
4. 代理分类
代理分类时可以根据协议区分也可以根据其匿名程度区分,下面分别总结如下:
根据协议区分
根据代理的协议区分,代理可以分为如下类别:
- FTP 代理服务器,主要用于访问 FTP 服务器,一般有上传、下载以及缓存功能,端口一般为 21、2121 等。
- HTTP 代理服务器,主要用于访问网页,一般有内容过滤和缓存功能,端口一般为 80、8080、3128 等。
- SSL/TLS 代理,主要用于访问加密网站,一般有 SSL 或 TLS 加密功能(最高支持 128 位加密强度),端口一般为 443。
- RTSP 代理,主要用于 Realplayer 访问 Real 流媒体服务器,一般有缓存功能,端口一般为 554。
- Telnet代理,主要用于 telnet 远程控制(黑客入侵计算机时常用于隐藏身份),端口一般为23。
- POP3/SMTP 代理,主要用于 POP3/SMTP 方式收发邮件,一般有缓存功能,端口一般为 110/25。
- SOCKS代理,只是单纯传递数据包,不关心具体协议和用法,所以速度快很多,一般有缓存功能,端口一般为1080。SOCKS 代理协议又分为 SOCKS4 和 SOCKS5,SOCKS4 协议只支持 TCP,而 SOCKS5 协议支持 TCP 和 UDP,还支持各种身份验证机制、服务器端域名解析等。简单来说,SOCK4能做到的SOCKS5都可以做到,但SOCKS5能做到的SOCK4不一定能做到。
根据匿名程度区分
根据代理的匿名程度划分,代理可以分为如下类别:
- 高度匿名代理,高度匿名代理会将数据包原封不动的转发,在服务端看来就好像真的是一个普通客户端在访问,而记录的 IP 是代理服务器的 IP。
- 普通匿名代理,普通匿名代理会在数据包上做一些改动,服务端上有可能发现这是个代理服务器,也有一定几率追查到客户端的真实 IP。代理服务器通常会加入的 HTTP 头有 HTTP_VIA 和 HTTP_X_FORWARDED_FOR。
- 透明代理,透明代理不但改动了数据包,还会告诉服务器客户端的真实 IP。这种代理除了能用缓存技术提高浏览速度,能用内容过滤提高安全性之外,并无其他显著作用,最常见的例子是内网中的硬件防火墙。
- 间谍代理,间谍代理指组织或个人创建的,用于记录用户传输的数据,然后进行研究、监控等目的代理服务器。
4. 常见代理设置
- 使用网上的免费代理,最好使用高匿代理,使用前抓取下来筛选一下可用代理,也可以进一步维护一个代理池。
- 使用付费代理服务,互联网上存在许多代理商,可以付费使用,质量比免费代理好很多。
- ADSL拨号,拨一次号换一次 IP,稳定性高,也是一种比较有效的解决方案。
在后文我们会详细介绍这几种代理的使用方式。
5. 参考来源
由于涉及到一些专业名词知识,本节的部分内容参考来源如下:
- 代理服务器 维基百科:https://zh.wikipedia.org/wiki/代理服务器
- 代理 百度百科:https://baike.baidu.com/item/代理/3242667