7  爬虫基础

7.1 HTTP基本原理

7.1.1 URI 和 URL

URI 的全称为 Uniform Resource Identifier,即统一资源标志符,URL 的全称为 Universal Resource Locator,即统一资源定位符。

举例来说,https://github.com/favicon.ico,它是一个 URL,也是一个 URI。即有这样的一个图标资源,我们用 URL/URI 来唯一指定了它的访问方式

这其中包括了

  1. 访问协议https
  2. 访问路径(即根目录)
  3. 资源名称 favicon.ico

通过这样一个链接,我们便可以从互联网上找到这个资源,这就是 URL/URI。

URL 是 URI 的子集,也就是说每个 URL 都是 URI,但不是每个 URI 都是 URL。那么,怎样的 URI 不是 URL 呢?URI 还包括一个子类叫作 URN,它的全称为 Universal Resource Name,即统一资源名称。URN 只命名资源而不指定如何定位资源,比如 urn:isbn:0451450523 指定了一本书的 ISBN,可以唯一标识这本书,但是没有指定到哪里定位这本书,这就是 URN。

URI格式:

scheme://[username:password@]hostname[:port][/path][;parameters][?query][#fragment]
  • scheme:协议。比如常用的协议有 http、https、ftp 等等,另外 scheme 也被常称作 protocol,都代表协议的意思。

  • username:password:用户名和密码。在某些情况下 URL 需要提供用户名和密码才能访问,这时候可以把用户名密码放在 host 前面。比如 https://ssr3.scrape.center 这个 URL 需要用户名密码才能访问,那么可以直接写为 https://admin:admin@ssr3.scrape.center 则可以直接访问。

  • hostname:主机地址。可以是域名或 IP 地址,比如 https://www.baidu.com 这个 URL 中的 hostname 就是 www.baidu.com,这就是百度的二级域名。比如 https://8.8.8.8 这个 URL 中 hostname 就是 8.8.8.8,它是一个 IP 地址。

  • port:端口。这是服务器设定的服务端口,比如 https://8.8.8.8:12345 这个 URL 中的端口就是 12345。但是有些 URL 中没有端口信息,这是使用了默认的端口,http 协议的默认端口是 80,https 协议的默认端口是 443。所以 <https://www.baidu.com 其实相当于 https://www.baidu.com:443,而 http://www.baidu.com 其实相当于 http://www.baidu.com:80

  • path:路径。指的是网络资源在服务器中的指定地址,比如 https://github.com/favicon.ico 这里 path 就是 favicon.ico,指的就是访问 GitHub 上的根目录下的 favicon.ico 这个资源。

  • parameters:参数。用来制定访问某个资源的时候的附加信息,比如 https://8.8.8.8:12345/hello;user 这里的 user 就是 parameters。但是 parameters 现在用得很少,所以目前很多人会把该参数后面的 query 部分称为参数,甚至把 parameters 和 query 混用。严格意义上来说,parameters 是分号;后面的内容。

  • query:查询。用来查询某类资源,如果有多个查询,则用 & 隔开。query 其实非常常见,比如 https://www.baidu.com/s?wd=nba&ie=utf-8,这里的 query 部分就是 wd=nba&ie=utf-8,这里指定了 wd 是 nba,ie 是 utf-8。由于 query 比刚才所说的 parameters 使用频率高太多,所以平时我们见到的参数、GET 请求参数、parameters、params 等称呼多数情况指代的也是 query。严格意义上来说,其实应该用 query 来表示。

  • fragment:片段。它是对资源描述的部分补充,可以理解为资源内部的书签。目前它有两个主要应用,一个是用作单页面路由,比如 现代前端框架 Vue、React 都可以借助它来做路由管理;另外一个应用是用作 HTML 锚点,用它可以控制一个页面打开时自动下滑滚动到某个特定的位置。

7.1.2 HTTP和HTTPS

URL支持的协议有很多,比如 http、https、ftp、sftp、smb 等等。

在爬虫中,我们抓取的页面通常基于 http 或 https 协议

HTTP 的全称是 Hyper Text Transfer Protocol,中文名叫作超文本传输协议。HTTP 协议是用于从网络传输超文本数据到本地浏览器的传送协议,它能保证高效而准确地传送超文本文档。

「超文本」 它就是超越了普通文本的文本,它是文字、图片、视频等的混合体,最关键有超链接,能从一个超文本跳转到另外一个超文本。

Tip

Chrome浏览器里面打开任意一个页面,右击任一地方并选择 “检查” 项(或者直接按快捷键 F12),即可打开浏览器的开发者工具,这时在 Elements 选项卡即可看到当前网页的源代码,这些源代码都是超文本.

图 7.1 网页超文本

HTTPS 的全称是 Hyper Text Transfer Protocol over Secure Socket Layer,是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版,即在 HTTP 下加入 SSL 层,简称为 HTTPS

HTTPS 的安全基础是 SSL,因此通过它传输的内容都是经过 SSL 加密的,它的主要作用分为以下两种。

  1. 建立一个信息安全通道,保证数据传输的安全性
  2. 确认网站的真实性。凡是使用了 https 的网站,都可以通过点击浏览器地址栏的锁头标志来查看网站认证之后的真实信息,也可以通过 CA 机构颁发的安全签章来查询

7.1.3 HTTP请求过程

浏览器向网站所在的服务器发送了一个请求,网站服务器接收到这个请求后进行处理和解析,然后返回对应的响应,接着传回给浏览器。

图 7.2 http请求过程
Tip

Chrome浏览器里面, Network 面板, 然后刷新网页,这时候就可以看到在 Network 面板下方出现了很多个条目,其中一个条目就代表一次发送请求和接收响应的过程.

图 7.3 请求和接收响应的过程
  • 第一列 Name:请求的名称,一般会将 URL 的最后一部分内容当作名称。
  • 第二列 Status:响应的状态码,这里显示为 200,代表响应是正常的。通过状态码,我们可以判断发送了请求之后是否得到了正常的响应。
  • 第三列 Protocol:请求的协议类型,这里 http/1.1 代表是 HTTP 1.1 版本,h2 代表 HTTP 2.0 版本。
  • 第四列 Type:请求的文档类型。
  • 第五列 Initiator:请求源。用来标记请求是由哪个对象或进程发起的。
  • 第六列 Size:从服务器下载的文件和请求的资源大小。如果是从缓存中取得的资源,则该列会显示 from cache。
  • 第七列 Time:发起请求到获取响应所用的总时间。
  • 第八列 Waterfall:网络请求的可视化瀑布流。

7.1.4 请求(Request)

Request,由客户端向服务器发出,可以分为 4 部分内容:

  • 请求方法(Request Method)

    • GET/POST
  • 请求的网址(Request URL)

  • 请求头(Request Headers)

    • Cookie:也常用复数形式 Cookies,这是网站为了辨别用户进行会话跟踪而存储在用户本地的数据。它的主要功能是维持当前访问会话。例如,我们输入用户名和密码成功登录某个网站后,服务器会用会话保存登录状态信息,后面我们每次刷新或请求该站点的其他页面时,会发现都是登录状态,这就是 Cookie 的功劳。Cookie 里有信息标识了我们所对应的服务器的会话,每次浏览器在请求该站点的页面时,都会在请求头中加上 Cookie 并将其发送给服务器,服务器通过 Cookie 识别出是我们自己,并且查出当前状态是登录状态,所以返回结果就是登录之后才能看到的网页内容。

    • Referer:此内容用来标识这个请求是从哪个页面发过来的,服务器可以拿到这一信息并做相应的处理,如做来源统计、防盗链处理等。

    • User-Agent:简称 UA,它是一个特殊的字符串头,可以使服务器识别客户使用的操作系统及版本、浏览器及版本等信息。在做爬虫时加上此信息,可以伪装为浏览器;如果不加,很可能会被识别为爬虫。

  • 请求体(Request Body)

    • Request Body 一般承载的内容是 POST请求中的表单数据,而对于GET请求,请求体则为空。

GET 请求中的参数包含在 URL 里面,数据可以在 URL 中看到;而 POST 请求的 URL 不会包含这些数据,数据都是通过表单形式传输的,会包含在请求体中。URL 里。

例如, GET https://www.baidu.com/s?wd=Python

GET 请求提交的数据最多只有 1024 字节,而 POST 方式没有限制。

表 7.1 请求方法种类
方法 描述
GET 请求页面,并返回页面内容
HEAD 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
POST 大多用于提交表单或上传文件,数据包含在请求体中
PUT 从客户端向服务器传送的数据取代指定文档中的内容
DELETE 请求服务器删除指定的页面
CONNECT 把服务器当作跳板,让服务器代替客户端访问其他网页
OPTIONS 允许客户端查看服务器的性能
TRACE 回显服务器收到的请求,主要用于测试或诊断

7.1.5 响应(Response)

响应,即 Response,由服务器返回给客户端,可以分为三部分:

  • 响应状态码(Response Status Code)
  • 响应头(Response Headers)
    • Date:标识响应产生的时间。
    • Last-Modified:指定资源的最后修改时间。
    • Content-Encoding:指定响应内容的编码。
    • Server:包含服务器的信息,比如名称、版本号等。
    • Content-Type:文档类型,指定返回的数据类型是什么,如 text/html 代表返回 HTML 文档,application/x-javascript 则代表返回 JavaScript 文件,image/jpeg 则代表返回图片。
    • Set-Cookie:设置 Cookie。响应头中的 Set-Cookie 告诉浏览器需要将此内容放在 Cookie 中,下次请求携带 Cookie 请求。
    • Expires:指定响应的过期时间,可以使代理服务器或浏览器将加载的内容更新到缓存中。
  • 响应体(Response Body)

图 7.4 五大类HTTP状态码

7.1.6 HTTP 2.0

HTTP/2.0 变得更快、更简单、更稳定,HTTP/2.0 在传输层做了很多优化,HTTP/2.0 的主要目标是通过支持完整的请求与响应复用来减少延迟,并通过有效压缩 HTTP 请求头字段将协议开销降至最低,同时增加对请求优先级和服务器推送的支持。

HTTP/2.0 所有性能增强的核心就在于这个新的二进制分帧层。在 HTTP/1.x 中,不管是请求(Request)还是响应(Response),它们都是用文本格式传输的,其头部(Headers)、实体(Body)之间也是用文本换行符分隔开的。HTTP/2.0 对其做了优化,将文本格式修改为了二进制格式,使得解析起来更加高效。同时将请求和响应数据分割为更小的帧,并采用二进制编码。

帧:只存在于 HTTP/2.0 中的概念,是数据通信的最小单位,比如一个请求被分为了请求头帧(Request Headers frame)和请求体 / 数据帧(Request Data frame)。 数据流:一个虚拟通道,可以承载双向的消息,每个流都有一个唯一的整数 ID 来标识。 消息:与逻辑请求或响应消息对应的完整的一系列帧。

  • 帧:只存在于 HTTP/2.0 中的概念,是数据通信的最小单位,比如一个请求被分为了请求头帧(Request Headers frame)和请求体 / 数据帧(Request Data frame)。
  • 数据流:一个虚拟通道,可以承载双向的消息,每个流都有一个唯一的整数 ID 来标识。
  • 消息:与逻辑请求或响应消息对应的完整的一系列帧。

图 7.5 HTTP协议进化

一些编程语言的库还没有完全支持 HTTP/2.0,比如对于 Python 来说,hyperhttpx 等库已经支持了 HTTP/2.0,但广泛使用的 requests 库依然还是只支持 HTTP/1.1。

7.1.7 网络传输层级

图 7.6 网络协议全景

图 7.7 网络协议4层结构

7.2 Web网页基础

7.2.1 网页的组成

网页可以分为三大部分 —— HTMLCSSJavaScript

如果把网页比作一个人的话,HTML 相当于骨架,JavaScript 相当于肌肉,CSS相当于皮肤,三者结合起来才能形成一个完善的网页。

HTML

HTML其英文叫做 HyperText Markup Language 中文翻译叫做超文本标记语言,但我们通常不会用中文翻译来称呼它,一般就叫 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>

CSS

HTML 定义了网页的结构,但是只有 HTML 页面的布局并不美观,可能只是简单的节点元素的排列。为了让网页看起来更好看一些,这里借助了 CSS。

CSS,全称叫作Cascading Style Sheets,即层叠样式表。“层叠” 是指当在 HTML 中引用了数个样式文件,并且样式发生冲突时,浏览器能依据层叠顺序处理。“样式” 指网页中文字大小、颜色、元素间距、排列等格式。CSS 是目前唯一的网页页面排版样式标准,有了它的帮助,页面才会变得更为美观。

在上图中,Styles 面板呈现的就是一系列 CSS 样式,比如摘抄一段 CSS,内容如下:

    <style>
      h2 {
      color: blue;
      font-size: xx-large;
      border-radius: 1cm;
      background-color: aquamarine;
    }
    </style>

这就是一个 CSS 样式。大括号前面是一个 CSS 选择器。

JavaScript

JavaScript,简称 JS,是一种脚本语言。HTML 和 CSS 配合使用,提供给用户的只是一种静态信息,缺乏交互性。

JavaScript 通常也是以单独的文件形式加载的,后缀为js,在 HTML 中通过 script 标签即可引入,例如:

<script src="jquery-2.1.0.js"></script>

7.2.2 节点树及节点间的关系

在 HTML 中,所有标签定义的内容都是节点,它们构成了一个 HTML 节点树,也称之为 HTML DOM 树。DOM 是 W3C(万维网联盟)的标准,其英文全称 Document Object Model,即文档对象模型。它定义了访问 HTML 和 XML 文档的标准。根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点。

  • 整个网站文档是一个文档节点。
  • 每个 html 标签对应一个根元素节点,即上例中的 html 标签,这属于一个跟元素节点。
  • 节点内的文本是文本节点,比如 a 节点代表一个超链接,它内部的文本也被认为是一个文本节点。
  • 每个节点的属性是属性节点,比如 a 节点有一个 href 属性,它就是一个属性节点。
  • 注释是注释节点,在 HTML 中有特殊的语法会被解析为注释,但其也会对应一个节点。

所以,HTML DOM 将 HTML 文档视作树结构,这种结构被称为节点树,如图所示:

图 7.8 节点树

通过 HTML DOM,树中的所有节点均可通过JavaScript访问,所有HTML节点元素均可被修改,也可以被创建或删除。

节点树中的节点彼此拥有层级关系。我们常用父(parent)、子(child)和兄弟(sibling)等术语描述这些关系

图 7.9 节点树及节点间的关系

7.2.3 选择器

在 CSS 中,我们使用 CSS 选择器来定位节点。

CSS 选择器还支持嵌套选择,各个选择器之间加上空格分隔开便可以代表嵌套关系,如 #container .wrapper p 则代表先选择 idcontainer 的节点,然后选中其内部的 classwrapper 的节点,然后再进一步选中其内部的 p 节点。另外,如果不加空格,则代表并列关系,如 div#container .wrapper p.text 代表先选择 idcontainerdiv 节点,然后选中其内部的 classwrapper 的节点,再进一步选中其内部的 classtextp 节点。这就是 CSS 选择器,其筛选功能还是非常强大的。

图 7.10 CSS选择器

7.2.4 静态网页和动态网页

我们将最基本的 HTML 代码保存为一个 test.html 文件,然后把它放在某台具有固定公网 IP 的主机上,主机上装上 Apache 或 Nginx 等服务器,这样这台主机就可以作为服务器了,其他人便可以通过访问服务器看到这个页面,这就搭建了一个最简单的网站。

这种网页的内容是 HTML 代码编写的,文字、图片等内容均通过写好的 HTML 代码来指定,这种页面叫作静态网页。它加载速度快,编写简单,但是存在很大的缺陷,如可维护性差,不能根据 URL 灵活多变地显示内容等。例如,我们想要给这个网页的 URL 传入一个 name 参数,让其在网页中显示出来,是无法做到的。

因此,动态网页应运而生,它可以动态解析 URL 中参数的变化,关联数据库并动态呈现不同的页面内容,非常灵活多变。我们现在遇到的大多数网站都是动态网站,它们不再是一个简单的 HTML,而是可能由 JSP、PHP、Python 等语言编写的,其功能比静态网页强大、丰富太多了。此外,动态网站还可以实现用户登录和注册的功能。

7.2.5 Session会话和Cookies

浏览网站的过程中,我们经常会遇到需要登录的情况,有些页面只有登录之后才可以访问,而且登录之后可以连续访问很多次网站,但是有时候过一段时间就需要重新登录。还有一些网站,在打开浏览器时就自动登录了,而且很长时间都不会失效,其实这里面涉及 Session 和 Cookie 的相关知识。

无状态 HTTP

HTTP 的无状态是指 HTTP 协议对事务处理是没有记忆能力的,也就是说服务器不知道客户端是什么状态。当我们向服务器发送请求后,服务器解析此请求,然后返回对应的响应,服务器负责完成这个过程,而且这个过程是完全独立的,服务器不会记录前后状态的变化,也就是缺少状态记录。这意味着如果后续需要处理前面的信息,则必须重传,这导致需要额外传递一些前面的重复请求,才能获取后续响应,然而这种效果显然不是我们想要的。

这时两个用于保持 HTTP 连接状态的技术就出现了,它们分别是 SessionCookieSession在服务端,也就是网站的服务器,用来保存用户的Session 信息; Cookie在客户端,也可以理解为浏览器端,有了Cookie,浏览器在下次访问网页时会自动附带上它发送给服务器,服务器通过识别 Cookie 并鉴定出是哪个用户,然后再判断用户是否是登录状态,然后返回对应的响应。

我们可以理解为Cookie里面保存了登录的凭证,有了它,只需要在下次请求携带 Cookie 发送请求而不必重新输入用户名、密码等信息重新登录了。

因此,在爬虫中,有时候处理需要登录才能访问的页面时,我们一般会直接将登录成功后获取的 Cookie 放在请求头里面直接请求,而不必重新模拟登录。

好了,了解 Session 和 Cookie 的概念之后,我们在来详细剖析它们的原理。

Session

Session,中文称为会话,其本来的含义是指有始有终的一系列动作/消息。比如,打电话时,从拿起电话拨号到挂断电话这中间的一系列过程可以称为一个 Session。

而在Web中,Session 对象用来存储特定用户 Session 所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户 Session 中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有 Session,则 Web 服务器将自动创建一个 Session 对象。当 Session 过期或被放弃后,服务器将终止该 Session。

Session维持

那么,我们怎样利用 Cookies 保持状态呢?当客户端第一次请求服务器时,服务器会返回一个响应头中带有 Set-Cookie 字段的响应给客户端,用来标记是哪一个用户,客户端浏览器会把 Cookies 保存起来。当浏览器下一次再请求该网站时,浏览器会把此 Cookies 放到请求头一起提交给服务器,Cookies 携带了 Session ID 信息,服务器检查该 Cookies 即可找到对应的 Session 是什么,然后再判断 Session 来辨认用户状态。

在成功登录某个网站时,服务器会告诉客户端设置哪些 Cookies 信息。在后续访问页面时,客户端会把 Cookies 发送给服务器,服务器再找到对应的 Session 加以判断。如果 Session 中的某些设置登录状态的变量是有效的,那就证明用户处于登录状态,此时返回登录之后才可以查看的网页内容,浏览器再进行解析便可以看到了。

反之,如果传给服务器的 Cookies 是无效的,或者 Session 已经过期了,我们将不能继续访问页面,此时可能会收到错误的响应或者跳转到登录页面重新登录。

所以,Cookies 和 Session 需要配合,一个处于客户端,一个处于服务端,二者共同协作,就实现了登录 Session 控制。

属性结构

接下来,我们来看看 Cookies 都有哪些内容。这里以知乎为例,在浏览器开发者工具中打开 Application 选项卡,然后在左侧会有一个 Storage 部分,最后一项即为 Cookies,将其点开,如图所示。

图 7.11 COOKIES列表

可以看到,这里有很多条目,其中每个条目可以称为 Cookie。它有如下几个属性。

  • Name,即该 Cookie 的名称。Cookie 一旦创建,名称便不可更改。
  • Value,即该 Cookie 的值。如果值为 Unicode 字符,需要为字符编码。如果值为二进制数据,则需要使用 BASE64 编码。
  • Domain,即可以访问该 Cookie 的域名。例如如果设置为 .zhihu.com,则所有以 zhihu.com 结尾的域名都可以访问该 Cookie。
  • Path,即该 Cookie 的使用路径。如果设置为 /path/,则只有路径为 /path/ 的页面可以访问该 Cookie。如果设置为 /,则本域名下的所有页面都可以访问该 Cookie。
  • Max-Age,即该 Cookie 失效的时间,单位为秒,常和 Expires 一起使用,通过它可以计算出其有效时间。Max-Age 如果为正数,则该 Cookie 在 Max-Age 秒之后失效。如果为负数,则关闭浏览器时 Cookie 即失效,浏览器也不会以任何形式保存该 Cookie。
  • Size 字段,即此 Cookie 的大小。
  • HTTP 字段,即 Cookie 的 httponly 属性。若此属性为 true,则只有在 HTTP Headers 中会带有此 Cookie 的信息,而不能通过 document.cookie 来访问此 Cookie。
  • Secure,即该 Cookie 是否仅被使用安全协议传输。安全协议有 HTTPS 和 SSL 等,在网络上传输数据之前先将数据加密。其默认值为 false

7.3 爬虫的基本原理

flowchart LR
  A[获取网页 HTTP原理] --> B[提取网页中的信息 WEB网页基础 ] --> C[保存数据 数据库/pandas/文件操作]
图 7.12 爬虫的基本流程

7.3.1 抓取数据类型

  1. html网页

    1. 文字、数值
    2. 下载多媒体,如图片、视频等
  2. JSON数据