1、网页请求方式

常见的网页请求方式主要有两种:GET(请求内容),POST(发送内容)。一般我们爬虫爬取网页大多使用 GET 请求。

在 python 中我们使用 requests 这个模块来模拟网页发送请求:

2、网页的响应

对网页发送的请求叫 requests,从网页得到的响应叫 response。上面的代码我们发送了 get 请求,并将变量放在 response 里面。我们来打印一下 response 看一下里面的内容是什么。

查看输出的结果:

2.1 响应代码

我们会发现输出的内容里面有 200 字样,这个数字描述对本次的响应代码。响应代码分为五类,分别以开头的数字来分,分为 1、2、3、4、5 类。

1xx 状态码——未完成

1xx 表示请求未完成,表示请求正在进行。这种响应只是一个临时的响应。

2xx 状态码——成功

2xx 表示请求成功,常见的状态码有下面几种:

3xx 状态码——搬家了

3xx 通常表示重定向,通常表示请求的内容已经不在目标位置,已经被转移到了新的位置,以便于告诉客户端去新的地方寻找。

4xx 状态码——我错了

4xx 通常表示客户端发生错误,导致请求出现问题。

5xx 状态码——他错了

5xx 通常表示服务器发生错误,服务器正确接受了请求,但是服务器内部产生了错误导致请求无法正常响应

2.2 获取状态码

在 python 中,对于请求得到的 response 对象,可以使用 response.status_code 来获取状态码:

得到的输出结果是:

2.3 获取响应内容

对于响应的内容,有两种获取的方式,分别是:response.textresponse.content ,下面对这两种方法进行简单的介绍和演示:

2.4 响应内容编码

响应到的内容,我们使用文本形式获取的时候,发现了乱码,这个原因是因为响应的内容编码不是我们通常使用的、支持中文的 utf-8 编码。所以我们需要单独设置编码格式:

通过将上面的代码放在使用 response.text 获取网页内容之前,可以发现编码的问题已经成功修复:

输出结果:

3、解析 html 文本——BeautifulSoup4

当我们获取到一个 html 文本之后,直接对这个文本进行索引取值,或者其他的方式来直接获取到我们想要的信息是非常难,可以说是不可能的,所以我们要使用一个专门的模块来做这件事情,这个模块的名字叫 BeautifulSoup4 ,下载方式是使用 pip 下载:

下载完成之后,使用下面的方式导入:

3.1 使用 BeautifulSoup 解析器

BeautifulSoup 解析器的使用方式是,直接调用 BeautifulSoup 函数,来对 html 文本进行解析:

BeautifulSoup 的第一个参数是文本形式的 html 内容,第二个参数固定是 "html.parser" ,表示要解析 html 文本。解析后会将解析结果返回出来,在上面的代码中,解析结果会被保存在 content 变量内。

3.1.1 html 标签

首先我们要了解一下 html 的基本结构:标签。html 的标签通常由以下几个部分构成:

这里的 id 和 class 用于特殊标注这个标签,这也是我们使用解析器找到我们想要爬取的内容的关键途径。

那么我们如何知道我们想要找到的东西在哪里呢?这个时候就要用到浏览器的 “检查” 功能了。随便打开一个网页(这里用我的服务器 http://ky0ha.com/github_style_project.html 来举例。

这里是网页中的一个表,假设我们需要获取这个表当中的第一列的所有名称,这就是我们需要的数据。那么我们可以选择到某一个需要获取的具体的数据,然后点击右键,选择最下面的检查:

点击检查之后,会在右边出现一个新的东西:

这个新的区域,我们称之为调试窗口。在调试窗口内,我们可以查看到上图的这一部分的代码,被称之为 html 代码,我们可以通过将想要的目标选中并点击检查,来快速找到它在 html 中的位置。

上图中,我们不难发现我们想要的 “关键字” 这三个字在一个叫做 td 的标签内,而且这个标签具有一个属性 class="type" 。这个 td 标签在 tr 标签内,tr 在 tbody 内,tbody 在 table 内,这个 table 具有属性 class="color_table" 。这些属性和标签可以帮助我们快速找到我们需要的内容。

3.1.2 通过 class 获取标签

我们之前使用 content = BeautifulSoup(response.text, "html.parser") 得到的解析结果 content 是一个特殊的对象,它有很多好用的方法,我们只介绍其中最简单的两个方法,它们可以帮助我们找到我们想要的东西。

find 方法

我们想要找到某个特定的标签,需要使用 find 来进行这种操作。find 的常见使用方式是:xxx.find("标签名", id="id 属性", class_="class 属性") ,这里的 id 和 class_ 参数是可以省略的,具体是否写,写哪个,按照实际情况来选择。易错点:当我们想要利用 class 属性来找某个标签的时候,在代码中需要使用 class_ 参数,不要忘记下划线

比如我们尝试获取上面的 <table class="color_table"> 这个标签。

得到的结果通过 print 输出后,我们可以看到(由于太长,中间部分省略):

如果 find 方法未找到目标标签,会返回 None 作为结果。

find_all 方法

使用 find 方法只能找到一个,所以通常我们也会使用 find_all 来一次性找到所有符合条件的标签,每找到一个都会将其作为一个元素,将这些元素组合成一个列表。当没找到的时候,会返回空列表;如果只找到一个符合要求的标签,会得到一个只有一个元素的列表。注意!find 得到的是一个标签对象,find_all 得到的是一个列表

接下来我们使用我们刚刚得到的 table 标签来进一步筛选(套娃)。通过前面的内容,我们可以不难发现我们要的东西在 table 里面的 tr 里面的 td 里面。我们可以使用 find_all 来找到所有的 tr 标签:

观察一下输出的结果(太长所以中间省略):

我们获得了里面的每一个 tr 标签,并把每个 tr 标签单独作为一个元素来保存在列表内。这样我们就可以索引取值或者循环遍历,来对里面的每一个 tr 标签获取想要的内容。

综合运用

接下来我们可以看到我们要的就在每一个 tr 标签内的第一个 td 标签,我们结合前面的内容,可以想到有多种方法来进行这一步的筛选:

3.2 课堂项目:获取自己省市的天气信息

这里我们将要使用 https://lishi.tianqi.com/songjiang/index.html 来获取松江的 11 月天气:

首先我们来获取到网页内容并打印看一下:

我们会发现无法正常获取,返回的状态码是 403,拒绝访问,这里的原因是因为网页的服务器发现我们是利用爬虫进行的非正常访问,所以不让我们访问,拒绝了我们的请求。解决这个问题的方式是加入头部信息 headers,在头部信息中使用 User-Agent 来告诉服务器我们的信息(相当于给服务器自我介绍)。

我们可以右键检查,打开调试控制台,在上面选择 network 网络选项,并点击 ctrl+r 来刷新网页。

将下面的表格滑动到最上面,点击 Index.html 这一条,在弹出的窗口中划到最底下,看一下最下面的一条 User-Agent。

将里面的内容复制,并在代码中创建一个字典来代表这个头部信息:

将这个变量传递给 get 请求,来伪装自己的爬虫请求:

查看输出(这里只输出状态码,不然整个网页的内容太长,不方便查看):

成功获取输出。接下来通过右键检查,分析网页内容:

完整代码:

输出结果:

4、从网页获取 json(以百度图片爬取为例子)

从网页获取数据内容除了以 textcontent 返回文本形式和二进制形式以外,还有一种特殊的返回形式:json 返回形式。

4.1 json 文件格式

json 是一种特殊的文件格式,全程是 JavaScript Object Notation,JavaScript对象表示法。它是一种 主要用于网络数据传输的轻量文本格式 。通俗理解,我们可以直接将它认为是 python 的字典。

4.2 浏览器开发者工具——获取 json 网址

我们打开浏览器,搜索百度图片,进入百度图片官方主页:

在上面的搜索栏输入 “柯南”,点击百度一下,就可以看到我们想要的图片了:

接下来我们需要打开 开发者模式 ,有三种方式打开开发者模式:

  1. 点击键盘上的 f12

  2. 右键单击网页,点击 “检查”

  3. 点击网页右上角三个点——更多工具——开发者工具

打开之后的会弹出一个窗口,点击上方的 Network(网络),会显示下面有很多内容的一个表格,如下图:

接下来在下面选择 Fetch/XHR如果出现下图这种空白的情况,需要点击 Ctrl + R 键重新刷新网页

如果像下面一样刷新之后没有出现名为 acjson? 开头的内容,那么就去网页里面,往下滑一下:

成功出现 acjson? 开头的内容后,单击打开第一个 acjson? 开头的请求的内容,重点查看红色框起来的部分,将这部分网页复制下来:

将这个网址复制到代码当中,使用 requests 向这个页面发送一个 get 请求:

输出的结果会是:

是不是看不懂?别急,我们先来看看我们的网页。点击刚刚显示网页上面的 Preview 预览,打开预览之后,可以看到下面是字典的键和值,这里 data 就是我们需要的东西,左键点开 data 看看,就可以看到里面的每个图片的具体数据了:

这里的 data 对应的值是一个列表,列表里每个元素是一个字典,字典里面存储的是我们的图片信息。但是!注意!我们会发现我们在网页中看到的字典和我们自己爬虫获得的字典完全不是一个样子的! 这里的原因在于,百度图片发现我们是爬虫,不是真正的浏览器,所以不给我们真正的信息,所以我们需要伪装我们自己的爬虫,才能获得我们要的内容。

我们需要设置 hearders(前面已经介绍过,这里不再赘述)。

获得的结果如下(因为太长后面省略):

终于我们成功获得了我们用于获取 json 内容的网址,并且成功获取到了我们要的 json内容。

4.3 requests.json() 方法

但是这里,我们如果使用 type 函数查看一下我们获取到的这些 json 的类型,我们会发现它是一个字符串类型,这明显不是我们想要的字典类型:

输出:

那么如何才能以 json 的形式,或者说以字典的形式返回我们的 json 内容呢?这时候我们就要用到 requests.json() 方法了。

这里我们使用这个方法之后,打印查看这个方法返回值的类型和内容:

输出:

这里我们发现得到的结果是一个字典了,我们就可以比较轻松的使用索引取值的方式找到我们想要的内容了。

4.4 获取图片网址

接下来我们回到网页中,去分析一下这个字典的结构,从之前的内容不难得出我们想要的图片内容在字典当中是 data 这个键对应的值,所以我们需要使用索引取值去尝试获取我们需要的内容。

这样我们就将整个图片的信息的列表放在了 data 变量里面。接下来我们可以回到浏览器,分析一下这个列表里面每个元素的内容,看看我们需要的图片的内容在哪里。随便单击点开列表的一条元素:

我们首先可以看到的是,列表的每个元素又是一个字典,并且这个字典里面有一个叫做 hoverURL 的键,我们将对应的值,也就是后面这串网址复制到浏览器里面打开,我们就会发现这就是我们想要的图片。那么如何获得换个网址呢?相信从前面的过程来看,内心应该已经有答案了——字典的索引取值。我们用键 hoverURL 来取值即可。

输出的结果看起来会比较多,所以只显示一小部分:

4.5 获取图片内容并保存

接下来我们只需要再用一次 requests 来获取到图片网址的内容,也就是对应的图片之后,保存图片即可,首先我们获取到我们的图片内容:

这里我们通过 content 获取到二进制形式的内容,也就是图片内容之后,我们用二进制写将图片写入了文件中,文件的名称是索引加上 .jepg 后缀,文件的位置和代码在同一位置,我们也可以创建一个文件夹,使得我们的文件不会到处乱跑:

我们通过这种方式将图片全部保存在了对应的位置:

4.6 完整代码

如果我们仔细观察下面的网址,我们可以看到里面的内容有一些有趣的地方,那就是图片的文件格式的部分:

我们观察后面的部分,有段 f=JPEG ,这里的 JPEG 实际上就是文件的格式,所以我们可以通过尝试获取到这段字符串来自动分析我们的图片的后缀名,这样可以避免格式错误。这部分属于拓展部分,我直接给出完整代码:

拓展部分完整代码