整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

三句话测试你是否懂git

事和组长的一番对话引起了笔者对 git 的思考

先介绍一下我司小工坊式的 git 提交流程,本地打包,删除 dist 文件,重建 dist 文件,git add .git commit -m 'XX'git push origin 分支名

和传统公司的 git 提交不同,我司打包是本地打包,而且是把 dist 文件直接上传到仓库

事故现象

同事把代码推上去后,浏览器访问的还是原来的 js 和 css。

同事说:组长,需要你把 dist 删掉,重新再从仓库里拉一下最新的

组长:git 提交后不就把原来的 dist 替换了吗,你让我删 dist 有什么意义

扯皮了一会儿,组长还是删了然后重新拉,没想到好了

组长说:你的 dist 现在是最新的,所以现在就好了

同事具体说了什么笔者忘记了,大致上在辩护 git 提交不会把原来的 dist 文件删除问题,不过他没说服组长,组长也没说服他,反正已经安全上线而不了了之。

我正好在旁边听到了,要是两年前我也许会一直提出问题参与辩论,申援同事。但笔者没动,不是怕 PUA,而是表达能力太差,即使是对的,也说不好。其根本原因是笔者对这块知识了解的不深刻,所以不敢说大话

理论知识

按照理论知识,你 push 整个 dist 文件,即使远程仓库中有 dist,也不会把整个 dist 文件夹替换,只会替换其中相同的数据,而因为打了 hash 值,所以 css 和 js 都是不同的,所以一直这样做,dist 中的文件会越来越多,而因为 index.html 文件只有一个,所以不会出现替换了还引用之前文件的问题,如果出现,清除下浏览器的缓存就能解决

实战检验

因为生产环境和测试环境发布代码流程不同,所以先要把环境配置成一致先

需要做的事情很简单,把 nginx 中指向仓库地址,到时候从远端拉下代码即可

先修改 nginx 中的配置

server {
    listen 7000;
    # root /usr/share/nginx/html/dist
    root /home/jingqb-web/dist
    ...
}

再检查一下 nginx 配置是否 ok

nginx -t

接着重启 nginx

nginx -s reload

接着把代码提交到远端仓库,再上服务器进入 /home/jingqb-web 目录下,git pull origin XX ,进入 dist 文件,查看打包后的 js

原js的hash值

我们修改在项目中打印一些日志,表示文件改动,这样 build 之后会打出不同 hash 的 js

git push origin XX

再次登录服务器,进入 /home/jingqb-web 目录,再拉代码git pull origin xx

再次提交后的代码

发现,umi.b0f5511b.js 被删掉了,新生成的 umi.f8280c0e.js 在其中,dist 中是干净的源文件,这是为什么呢?

你 build 之后,是先删掉 dist 文件,生成的是一个干净的 dist,然后我的操作是:

  • git add .
  • git commit -m 'XX'
  • git push origin 'XX 分支'

我的操作中没有 pull 代码,而是直接 push 代码,这就意味着 dist 就是我本地的 dist,而非合并之后的

想想这种做法的缺点是多人开发时,pull 别人的代码后,merge 之后还要重新 build,才能再次提交

好险,还好没有逞英雄

谨言慎行是一辈子的学问

三句话测试你是否懂 git

这触发了笔者对 git 的新认知,结合平时经验,笔者觉得三个问题能测试别人对 git 的理解程度

  1. 你和同事基于同一 commit 开发,后续合并时,如何按照时间顺序显示提交记录 git rebase master XX(分支)获得更优雅的提交树
  2. 代码如何回滚 git reset --hard XX把当前代码指向另一个commit上
  3. 你开发代码,提交了好几个 commit,后续使用 git reset --hard xxxxx 把代码指针指回原始 commit ,并在这个 commit 上开发了一个功能,并提交了一个 commit,怎么找回之前提交的那好几个 commit 首先使用 git reflog ,它能展示你之前所有的 git 操作 比较 git log,它不仅包括了 git log 上的操作,而且它记录了被删除的 commit 记录和 reset 操作 git reset --hard XX 将 git 指针指向回到原始代码前的那个 commit git cherry-pick XX 合并二次开发时的 commitcherry-pick 意为取出,将二次开发时的 commit 取出放入主分支上

面几篇文章我们讲到了跨站脚本(XSS)漏洞的几种类型和验证方法以及防御措施,有兴趣的朋友可以到我的主页翻看文章《十大常见web漏洞——跨站脚本漏洞》和《实操web漏洞验证——跨站脚本漏洞》,今天我们继续由易到难实战演示一下跨站脚本漏洞的形成,以便更好地了解漏洞的产生原理,进一步做好防御。

上一篇文章我们已经闯过了5关,今天我们继续。

html事件中的xss

html事件是在满足一定条件的用户行为发生时,所触发的的事件,例如当单击鼠标时的“onclick”以及当浏览器加载图像时的“onload”,我们可以将这些特定的html事件发生时,将JavaScript语句作为属性传递给特定的标签,从而构成一个或多个JavaScript命令或函数。

下图我们从网上搜索了一些html事件属性,有兴趣的可以自己搜索学习。

html事件属性

在这一关中,我们可以构造语句:

111" onmouseover="alert(document.domain);"

前边的“111"”是为了闭合标签,后边的“onmouseover”属性表示当鼠标移动到输入框时执行后边的语句,点击“search”按钮,将鼠标移动到输入框时,页面在弹出内容为当前页面域名的弹窗后,提示“恭喜!”,就可以顺利进入下一关了,如下图所示:

恭喜通关

空格分隔属性中的xss

这一关我们先来探测一下注入点的情况,我们和之前一样先闭合标签,输入“123456"”来闭合标签,找到对应的代码,发现我们输入的内容被另一对引号括住了,如下图所示:

页面代码

这表明我们输入的内容直接被实体化了,那我们不使用引号闭合,直接输入“123456 onmouseover=“alert(document.domain)””,发现只要是等号后边的参数都被引号括了起来,如下图所示:

页面代码

因此我们都不加引号,构造:

123456 onmouseover=alert(document.domain)

注意中间有个空格,点击“search”按钮时,页面在弹出内容为当前页面域名的弹窗后,提示“恭喜!”,就可以顺利进入下一关了,如下图所示:

恭喜通关

JavaScript伪协议触发XSS

有时候我们需要将JavaScript代码添加到客户端中,这时就需要JavaScript伪协议来帮助,它的格式为JavaScript:url,例如:JavaScript:alert("hello word!"),就是一个简单的通过JavaScript伪协议来执行alert("hello word!")语句,它表示在页面显示“hello word!”。

因此我们可以构造语句

JavaScript:alert(document.domain);

点击“Make a Link”按钮时,可见输入框下边出现一个URL超链接,我们点击这个链接,如下图所示:

URL超链接

页面在弹出内容为当前页面域名的弹窗后,提示“恭喜!”,就可以顺利进入下一关了,如下图所示:

恭喜通关

UTF-7中的xss

这个比较简单,因为UTF-7绝大多数浏览器都已经不用了,我们很少会遇到,因此我们直接构造语句:

onclick=alert(document.domain);

我们按F12键,根据下图提示找到第三步位置,将以上语句写入到对应位置,再点击第二步的位置,如下图所示:

修改页面代码

页面在弹出内容为当前页面域名的弹窗后,提示“恭喜!”,就可以顺利进入下一关了,如下图所示:

恭喜通关

绕过过滤domain为空的xss

首先我们还是老办法构造闭合语句,如下所示:

"><script>alert(document.domain);</script>

点击“search”,按F12,找到如下图红框中的位置,发现我们上边构造的语句中“domain”被删除了,如下图所示:

页面代码

既然被删除了,这时我们可以通过双写来绕过domain被删除这种情况,我们可以构造:

"><script>alert(document.dodomainmain);</script>

注意我们在单词domain中间又加了一个单词domain,这时系统在删除一个单词domain后,还会留下一个domain,这样我们就成功执行了语句了。

点击“search”按钮时,页面在弹出内容为当前页面域名的弹窗后,提示“恭喜!”,就可以顺利进入下一关了,如下图所示:

恭喜通关

当然我们也可以通过编码的方法来绕过,我们可以构造:

"><script>eval(atob('YWxlcnQoZG9jdW1lbnQuZG9kb21haW5tYWluKTs='));</script>

其中“eval”是用来执行字符串,其后边的内容会当成JavaScript语句执行,“atob”表示将加密的base64密文,转换成原文,而里边的一串乱码就是通过base64加密过的的“alert(document.domain)”语句,关于加密,有兴趣的可以阅读我的文章《Web渗透测试——密码学基础》,其实和上边的语句一样,这样就可以避免domain被删除了。

以上就是跨站脚本(XSS)漏洞实战演示——由易到难第二部分的全部内容,希望对你了解XSS漏洞有所帮助,欢迎关注@科技兴了解更多科技尤其是网络安全方面的资讯和知识。

正式学习网络爬虫之前,我们需要详细了解 HTTP 的基本原理,了解在浏览器中敲入 URL 到获取网页内容之间发生了什么。了解这些内容,有助于我们进一步了解爬虫的基本原理。

1.1 HTTP 基本原理

在本节中,我们会详细了解 HTTP 的基本原理,了解在浏览器中敲入 URL 到获取网页内容之间发生了什么。了解这些内容,有助于我们进一步了解爬虫的基本原理。

1. URI 和 URL

这里我们先了解一下 URI 和 URL。URI 的全称为 Uniform Resource Identifier,即统一资源标志符;而 URL 的全称为 Universal Resource Locator,即统一资源定位符。举例来说,github.com/favicon.ico 是一个 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 的关系可以用图 1-1 表示。

但是在目前的互联网,URN 使用得非常少,几乎所有的 URI 都是 URL,所以对于一般的网页链接,我们既可以称之为 URL,也可以称之为 URI,我个人习惯称之为 URL。

但 URL 也不是随便写的,它也是需要遵循一定的格式规范的,基本的组成格式如下:

scheme://[username:password@]hostname[:port][/path][;parameters][?query][#fragment]
复制代码

其中这些括号包括的内容代表非必要部分,比如 www.baidu.com 这个 URL,这里就只包含了 scheme 和 host 两部分,其他的 port、path、parameters、query、fragment 都没有。

这里我们分别介绍下几部分代表的含义和作用:

  • scheme:协议。比如常用的协议有 http、https、ftp 等等,另外 scheme 也被常称作 protocol,都代表协议的意思。
  • username、password:用户名和密码。在某些情况下 URL 需要提供用户名和密码才能访问,这时候可以把用户名密码放在 host 前面。比如 ssr3.scrape.center 这个 URL 需要用户名密码才能访问,那么可以直接写为 admin:admin@ssr3.scrape.center 则可以直接访问。
  • hostname:主机地址。可以是域名或 IP 地址,比如 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。所以 www.baidu.com 其实相当于 www.baidu.com:443,而 www.baidu.com 其实相当于 www.baidu.com:80。
  • path:路径。指的是网络资源在服务器中的指定地址,比如 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 其实非常常见,比如 www.baidu.com/s?wd=nba&ie… query 部分就是 wd=nba&ie=utf-8,这里指定了 wd 是 nba,ie 是 utf-8。由于 query 比刚才所说的 parameters 使用频率高太多,所以平时我们见到的参数、GET 请求参数、parameters、params 等称呼多数情况指代的也是 query。严格意义上来说,其实应该用 query 来表示。
  • fragment:片段。它是对资源描述的部分补充,可以理解为资源内部的书签。目前它有两个主要应用,一个是用作单页面路由,比如 现代前端框架 Vue、React 都可以借助它来做路由管理;另外一个应用是用作 HTML 锚点,用它可以控制一个页面打开时自动下滑滚动到某个特定的位置。

以上我们就简单了解了 URL 的基本概念和构成,后文我们会结合多个实战案例练习来帮助加深其理解。

2. HTTP 和 HTTPS

刚才我们了解了 URL 的基本构成,其支持的协议有很多,比如 http、https、ftp、sftp、smb 等等。

在爬虫中,我们抓取的页面通常基于 http 或 https 协议,这里首先我们先来了解一下这两个协议的含义。

HTTP 的全称是 Hyper Text Transfer Protocol,中文名叫作超文本传输协议。HTTP 协议是用于从网络传输超文本数据到本地浏览器的传送协议,它能保证高效而准确地传送超文本文档。HTTP 由万维网协会(World Wide Web Consortium)和 Internet 工作小组 IETF(Internet Engineering Task Force)共同合作制定的规范,目前广泛使用的是 HTTP 1.1 版本,当然 HTTP 2.0 现在不少网站也增加了支持。

其发展历史见下表:

版本

产生时间

主要特点

发展现状

HTTP/0.9

1991 年

不涉及数据包传输,规定客户端和服务器之间通信格式,只能 GET 请求

没有作为正式的标准

HTTP/1.0

1996 年

传输内容格式不限制,增加 PUT、PATCH、HEAD、 OPTIONS、DELETE 命令

正式作为标准

HTTP/1.1

1997 年

持久连接(长连接)、节约带宽、HOST 域、管道机制、分块传输编码

正式作为标准并广泛使用

HTTP/2.0

2015 年

多路复用、服务器推送、头信息压缩、二进制协议等

逐渐覆盖市场

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 已经是大势所趋。

注:HTTP 和 HTTPS 协议都属于计算机网络中的应用层协议,其下层是基于 TCP 协议实现的,TCP 协议属于计算机网络中的传输层协议,包括建立连接时的三次握手和断开时的四次挥手等过程。但本书主要讲的是网络爬虫相关,主要爬取的是 HTTP/HTTPS 协议相关的内容,所以这里就不再展开深入讲解 TCP、IP 等相关知识了,感兴趣的读者可以搜索相关资料了解下,如《计算机网络》、《图解 HTTP》等书籍。

3. HTTP 请求过程

我们在浏览器中输入一个 URL,回车之后便会在浏览器中观察到页面内容。

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

由于响应里包含页面的源代码等内容,浏览器再对其进行解析,便将网页呈现了出来,流程如图 1-3 所示。

此处客户端即代表我们自己的 PC 或手机浏览器,服务器即要访问的网站所在的服务器。

为了更直观地说明这个过程,这里用 Chrome 浏览器开发者模式下的 Network 监听组件来做下演示,它可以显示访问当前请求网页时发生的所有网络请求和响应。

打开 Chrome 浏览器,访问百度 www.baidu.com/,这时候鼠标右键并选择“检查”菜单(或直接按快捷键 F12),即可打开浏览器的开发者工具,如下图所示:

我们切换到 Network 面板,然后重新刷新网页,这时候就可以看到在 Network 面板下方出现了很多个条目,其中一个条目就代表一次发送请求和接收响应的过程,如图所示:

我们先观察第一个网络请求,即 www.baidu.com,其中各列的含义如下。

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

我们点击这个条目,即可看到其更详细的信息,如图所示。

首先是 General 部分,其中 Request URL 为请求的 URL,Request Method 为请求的方法,Status Code 为响应状态码,Remote Address 为远程服务器的地址和端口,Referrer Policy 为 Referrer 判别策略。

再继续往下可以看到,有 Response Headers 和 Request Headers,它们分别代表响应头和请求头。请求头里带有许多请求信息,例如浏览器标识、Cookie、Host 等信息,这是请求的一部分,服务器会根据请求头内的信息判断请求是否合法,进而作出对应的响应。图 1-5 中看到的 Response Headers 就是响应的一部分,其中包含了服务器的类型、文档类型、日期等信息,浏览器接收到响应后,会解析响应内容,进而呈现网页内容。

下面我们分别来介绍一下请求和响应都包含哪些内容。

4. 请求(Request)

请求,英文为 Request,由客户端向服务器发出,可以分为 4 部分内容:请求方法(Request Method)、请求的网址(Request URL)、请求头(Request Headers)、请求体(Request Body)。

下面我们分别予以介绍。

请求方法(Request Method)

请求方法,英文为 Request Method,用于标识请求客户端请求服务端的方式,常见的请求方法有两种:GET 和 POST。

在浏览器中直接输入 URL 并回车,这便发起了一个 GET 请求,请求的参数会直接包含到 URL 里。例如,在百度中搜索 Python,这就是一个 GET 请求,链接为 www.baidu.com/s?wd=Python,其中 URL 中包含了请求的 query 信息,这里的参数 wd 表示要搜寻的关键字。POST 请求大多在表单提交时发起。比如,对于一个登录表单,输入用户名和密码后,点击 “登录” 按钮,这通常会发起一个 POST 请求,其数据通常以表单的形式传输,而不会体现在 URL 中。

GET 和 POST 请求方法有如下区别:

  • GET 请求中的参数包含在 URL 里面,数据可以在 URL 中看到;而 POST 请求的 URL 不会包含这些数据,数据都是通过表单形式传输的,会包含在请求体中。
  • GET 请求提交的数据最多只有 1024 字节,而 POST 方式没有限制。

一般来说,登录时,需要提交用户名和密码,其中包含了敏感信息,使用 GET 方式请求的话,密码就会暴露在 URL 里面,造成密码泄露,所以这里最好以 POST 方式发送。上传文件时,由于文件内容比较大,也会选用 POST 方式。

我们平常遇到的绝大部分请求都是 GET 或 POST 请求。另外,还有一些请求方法,如 GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE 等,我们简单将其总结为下表。

方  法

描  述

GET

请求页面,并返回页面内容

HEAD

类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头

POST

大多用于提交表单或上传文件,数据包含在请求体中

PUT

从客户端向服务器传送的数据取代指定文档中的内容

DELETE

请求服务器删除指定的页面

CONNECT

把服务器当作跳板,让服务器代替客户端访问其他网页

OPTIONS

允许客户端查看服务器的性能

TRACE

回显服务器收到的请求,主要用于测试或诊断

本表参考:www.runoob.com/http/http-m…。

请求的网址(Request URL)

请求的网址,英文为 Reqeust URL,它可以唯一确定我们想请求的资源。关于 URL 的构成和各个部分的功能我们在前文已经提及到了,这里就不再赘述。

请求头(Request Headers)

请求头,英文为 Request Headers,用来说明服务器要使用的附加信息,比较重要的信息有 Cookie、Referer、User-Agent 等。

下面简要说明一些常用的头信息:

  • Accept:请求报头域,用于指定客户端可接受哪些类型的信息。
  • Accept-Language:指定客户端可接受的语言类型。
  • Accept-Encoding:指定客户端可接受的内容编码。
  • Host:用于指定请求资源的主机 IP 和端口号,其内容为请求 URL 的原始服务器或网关的位置。从 HTTP 1.1 版本开始,请求必须包含此内容。
  • Cookie:也常用复数形式 Cookies,这是网站为了辨别用户进行会话跟踪而存储在用户本地的数据。它的主要功能是维持当前访问会话。例如,我们输入用户名和密码成功登录某个网站后,服务器会用会话保存登录状态信息,后面我们每次刷新或请求该站点的其他页面时,会发现都是登录状态,这就是 Cookie 的功劳。Cookie 里有信息标识了我们所对应的服务器的会话,每次浏览器在请求该站点的页面时,都会在请求头中加上 Cookie 并将其发送给服务器,服务器通过 Cookie 识别出是我们自己,并且查出当前状态是登录状态,所以返回结果就是登录之后才能看到的网页内容。
  • Referer:此内容用来标识这个请求是从哪个页面发过来的,服务器可以拿到这一信息并做相应的处理,如做来源统计、防盗链处理等。
  • User-Agent:简称 UA,它是一个特殊的字符串头,可以使服务器识别客户使用的操作系统及版本、浏览器及版本等信息。在做爬虫时加上此信息,可以伪装为浏览器;如果不加,很可能会被识别为爬虫。
  • Content-Type:也叫互联网媒体类型(Internet Media Type)或者 MIME 类型,在 HTTP 协议消息头中,它用来表示具体请求中的媒体类型信息。例如,text/html 代表 HTML 格式,image/gif 代表 GIF 图片,application/json 代表 JSON 类型,更多对应关系可以查看此对照表:tool.oschina.net/commons。

因此,请求头是请求的重要组成部分,在写爬虫时,大部分情况下都需要设定请求头。

请求体(Request Body)

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

例如,这里我登录 GitHub 时捕获到的请求和响应如图 1-6 所示。

登录之前,我们填写了用户名和密码信息,提交时这些内容就会以表单数据的形式提交给服务器,此时需要注意 Request Headers 中指定 Content-Type 为 application/x-www-form-urlencoded。只有设置 Content-Type 为 application/x-www-form-urlencoded,才会以表单数据的形式提交。另外,我们也可以将 Content-Type 设置为 application/json 来提交 JSON 数据,或者设置为 multipart/form-data 来上传文件。

如下表是 Content-Type 和 POST 提交数据方式的关系

Content-Type

提交数据的方式

application/x-www-form-urlencoded

表单数据

multipart/form-data

表单文件上传

application/json

序列化 JSON 数据

text/xml

XML 数据

在爬虫中,如果要构造 POST 请求,需要使用正确的 Content-Type,并了解各种请求库的各个参数设置时使用的是哪种 Content-Type,不然可能会导致 POST 提交后无法正常响应。

5. 响应(Response)

响应,即 Response,由服务器返回给客户端,可以分为三部分:响应状态码(Response Status Code)、响应头(Response Headers)和响应体(Response Body)。

响应状态码(Response Status Code)

响应状态码,即 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)

响应头,即 Response Headers,包含了服务器对请求的应答信息,如 Content-Type、Server、Set-Cookie 等。下面简要说明一些常用的头信息。

  • 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)

响应体,即 Response Body,这可以说是最关键的部分了,响应的正文数据都在响应体中,比如请求网页时,它的响应体就是网页的 HTML 代码;请求一张图片时,它的响应体就是图片的二进制数据。我们做爬虫请求网页后,要解析的内容就是响应体,如图 1-7 所示。

在浏览器开发者工具中点击 Preview,就可以看到网页的源代码,也就是响应体的内容,它是解析的目标。

在做爬虫时,我们主要通过响应体得到网页的源代码、JSON 数据等,然后从中做相应内容的提取。

本节中,我们了解了 HTTP 的基本原理,大概了解了访问网页时背后的请求和响应过程。本节涉及的知识点需要好好掌握,后面分析网页请求时会经常用到。

6. HTTP/2.0

前面我们也提到了 HTTP 协议从 2015 年起发布了 2.0 版本,相比 HTTP/1.1 来说,HTTP/2.0 变得更快、更简单、更稳定,HTTP/2.0 在传输层做了很多优化,HTTP/2.0 的主要目标是通过支持完整的请求与响应复用来减少延迟,并通过有效压缩 HTTP 请求头字段将协议开销降至最低,同时增加对请求优先级和服务器推送的支持,这些优化一笔勾销了 HTTP/1.1 为做传输优化想出的一系列“歪招”。

有读者这时候可能会问,为什么不叫 HTTP/1.2 而叫 HTTP/2.0 呢?因为 HTTP/2.0 在内部实现上新的二进制分帧层,这是没法与之前的 HTTP/1.x 的服务器和客户端实现向后兼容的,所以直接修改了主版本号为 2.0。

下面我们就来了解下 HTTP/2.0 相比 HTTP/1.1 来说做了哪些优化吧。

二进制分帧层

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 中,同域名下的所有通信都可以在单个连接上完成,该连接可以承载任意数量的双向数据流,数据流是用于承载双向消息的,每条消息都是一条逻辑 HTTP 消息(例如请求或响应),它可以包含一个或多个帧。

简而言之,HTTP/2.0 将 HTTP 协议通信分解为二进制编码帧的交换,这些帧对应着特定数据流中的消息,所有这些都在一个 TCP 连接内复用,这是 HTTP/2.0 协议所有其他功能和性能优化的基础。

多路复用

在 HTTP/1.x 中,如果客户端要想发起多个并行请求以提升性能,则必须使用多个 TCP 连接,而且浏览器位了控制资源,还会对单个域名有 6-8 个 TCP 连接请求的限制。但在 HTTP/2.0 中,由于又了二进制分帧技术的加持,HTTP/2.0 不用再以来 TCP 连接去实现多路并行了,客户端和服务器可以将 HTTP 消息分解为互不依赖的帧,然后交错发送,最后再在另一端把它们重新组装起来,让我们可以:

  • 并行交错地发送多个请求,请求之间互不影响。
  • 并行交错地发送多个响应,响应之间互不干扰。
  • 使用一个连接并行发送多个请求和响应。
  • 不必再为绕过 HTTP/1.x 限制而做很多工作。
  • 消除不必要的延迟和提高现有网络容量的利用率,从而减少页面加载时间。

这样以来,整个数据传输使性能就有了极大提升:

  • 同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应,消除了因多个 TCP 连接而带来的延时和内存消耗。
  • 并行交错地发送多个请求和详情,而且之间互不影响。
  • 在 HTTP/2.0 中,每个请求都可以带一个 31bit 的优先值,0 表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。

流量控制

流控制是一种阻止发送方向接收方发送大量数据的机制,以免超出后者的需求或处理能力。可以理解为,接收方已经太繁忙了,来不及处理收到的消息了,但是发送方还在一直大量发送消息,这样就会出现一些问题。

比如说,客户端可能请求了一个具有较高优先级的大型视频流,但是用户已经暂停视频,客户端现在希望暂停或限制从服务器的传输,以免提取和缓冲不必要的数据。 再比如,一个代理服务器可能具有较快的下游连接和较慢的上游连接,并且也希望调节下游连接传输数据的速度以匹配上游连接的速度来控制其资源利用率等等。

由于 HTTP 是基于 TCP 实现的,虽然 TCP 原生有流量控制机制,但是由于 HTTP/2.0 数据流在一个 TCP 连接内复用,TCP 流控制既不够精细,也无法提供必要的应用级 API 来调节各个数据流的传输。

为了解决这一问题,HTTP/2.0 提供了一组简单的构建块,这些构建块允许客户端和服务器实现其自己的数据流和连接级流控制:

  • 流控制具有方向性。 每个接收方都可以根据自身需要选择为每个数据流和整个连接设置任意的窗口大小。
  • 流控制基于信用。 每个接收方都可以公布其初始连接和数据流流控制窗口(以字节为单位),每当发送方发出 DATA 帧时都会减小,在接收方发出 WINDOW_UPDATE 帧时增大。
  • 流控制无法停用。 建立 HTTP/2.0 连接后,客户端将与服务器交换 SETTINGS 帧,这会在两个方向上设置流控制窗口。 流控制窗口的默认值设为 65535 字节,但是接收方可以设置一个较大的最大窗口大小(2^31-1 字节),并在接收到任意数据时通过发送 WINDOW_UPDATE 帧来维持这一大小。
  • 流控制为逐跃点控制,而非端到端控制。 即,可信中介可以使用它来控制资源使用,以及基于自身条件和启发式算法实现资源分配机制。

由此可见,HTTP/2.0 提供了简单的构建块实现了自定义策略来调节资源使用和分配,以及实现新传输能力,同时提升了网页应用的实际性能和感知性能。

服务端推送

HTTP/2.0 新增的另一个强大的新功能是,服务器可以对一个客户端请求发送多个响应。 换句话说,除了对最初请求的响应外,服务器还可以向客户端推送额外资源,而无需客户端明确地请求。

如果某些资源客户端是一定会请求的,这时就可以采取服务端推送的技术,在客户端发起一次请求后,额外提前给客户端推送必要的资源,这样就可以相对减少一点延迟时间。例如,服务端可以主动把 JS 和 CSS 文件推送给客户端,而不需要客户端解析 HTML 时再发送这些请求。

服务端可以主动推送,当然客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送 RST_STREAM 帧来拒收。

另外主动推送也遵守同源策略,即服务器不能随便将第三方资源推送给客户端,而必须是经过双方确认才行,这样也能保证一定的安全性。

HTTP/2.0 发展现状

HTTP/2.0 的普及是一件任重而道远的事情,一些主流的网站现在已经支持了 HTTP/2.0,主流浏览器现在都已经实现了 HTTP/2.0 的支持,但总的来看,目前大部分网站依然还是以 HTTP/1.1 为主。

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

7. 总结

本节介绍了关于 HTTP 的一些基础知识,内容不少,需要好好掌握,这些知识对于后面我们编写和理解网络爬虫具有非常大的帮助。

由于本节的内容多数为概念介绍,内容参考了很多书籍、文档、博客,来源如下:

  • 书籍 - 《HTTP 权威指南》- 作者 David Gourley / Brian Totty
  • 文档 - HTTP - 维基百科:en.wikipedia.org/wiki/Hypert…
  • 文档 - HTTP - 百度百科:baike.baidu.com/item/HTTP/2…
  • 文档 - HTTP - MDN Web Docs:developer.mozilla.org/en-US/docs/…
  • 文档 - HTTP/2 简介 - Google 开发文档:developers.google.com/web/fundame…
  • 博客 - 一文读懂 HTTP/2 及 HTTP/3 特性:blog.fundebug.com/2019/03/07/…
  • 博客 - 一文读懂 HTTP/2 特性:zhuanlan.zhihu.com/p/26559480

更多精彩内容,请关注我的公众号「进击的 Coder」和「崔庆才丨静觅」。


作者:崔庆才丨静觅
链接:https://juejin.cn/post/7081288652034146312