摘要:简述爬虫的基本原理,回答爬虫能爬取什么样的数据,介绍URL的构成、请求的具体过程和响应的方式,小白初学者必读篇。
目录
一、爬虫的基本原理
爬虫就是获取网页并提取和保存信息的自动化程序。
1、获取网页
爬虫的首要工作就是获取网页,明确的讲是获取网页源代码。网页的源代码包含网页的部分有用信息,获取到了源代码就相当于获取了网页中的信息。
通过想一个网站的服务器发送一个请求,服务器返回的响应体便是网页源代码。所以,获取网页的最关键部分就是构造一个请求并发送给服务器,然后接收到相应并对其进行解析。这个流程该如何实现呢?
Python提供了许多库,可以帮助我们实现以上流程。这会令我们十分便捷的编写爬虫程序,这也是为什么用Python写爬虫程序得到大家的广泛认同。如:使用requests库,我们可以完成HTTP请求操作。此外,请求和响应都可以用类库提供的数据结构来表示解析。因此,得到响应之后只需要解析数据结构中的body部分,即可得到网页的源代码。
2、提取信息
获取到了网页源代码后,接下来就是分析源代码,从中提取我们需要的信息。最通用的提取方法就是正则表达式,这是一个万能的方法,缺点就是构造正则表达式的过程比较复杂且容易出错。
另外,由于网页结构是具备一定的规则的(html、css、js),所以还有一些库是根据网页节点属性、CSS选择器或XPath来提取网页信息的,如:Beautiful Soup、lxml、pyquery等。使用这些库,可以高效地从源代码中提取网页信息。
3、保存数据
提取信息后,我们一般会将提前到的数据保存到某处以后以便后续使用。保存数据的形式多种多样,可以简单保存为txt文本或json文本,也可以保存到数据库,还可以保存到远程服务器等。
二、能爬取怎样的数据
网页中存在各种各样的信息,最常见的便是常规网页,这些网页对应着HTML代码,最常抓取的便是HTML源代码。
另外,有些网页返回的不是HTML代码,而是一个JSON字符串(其中API接口大多采用这样的形式),这种格式的数据便于传输和解析。爬虫可以抓取这些数据,而且数据提取会更加方便。
网页中还包含各种二进制数据,如图片、视频和音频等。利用爬虫,也可以将这些二进制数据抓取下来,然后保存成对应的文件名。
此外,网页中还有各种扩展名文件,如HTML、CSS、JavaScript和配置文件等。这些文件其实最普通,只要在浏览器里面可以访问到,就可以抓取下来。
三、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文件, 我们也就无法看到完整的页面内容。这也解释了为什么有时我们得到的源代码和在浏览器中看到的不一样。对于这样的情况, 我们可以分析源代码后台Ajax接口, 也可使用Selenium、Splash、Pyppeteer、Playwright这样的库来模拟JavaScript渲染。
四、请求
请求(request),由客户端发往服务器,分为四部分内容:请求网址,请求方法,请求头和请求体。
1、请求的网址
网址,也就是网页链接,也可以叫URL(统一资源定位符),用来定位资源所在位置的。它的构成遵循一定格式规范,基本的组成格式如下:
scheme://[username:password@]hostname[:port][/path][;parameters][? query][#fragment]
其中,中括号包括的内容代表非必要部分,比如 https://www.baidu.com 这个URL,这里就只包含了scheme 和 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这就是百度的二级域名。比如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. icon。
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 锚点,用它可以控制一个页面打开时自动下滑滚动到某个特定的位置。
2、请求方法
请求方法:用于标识请求客户端请求服务端的方式,常见的请求方法有两种:GET和POST。
在浏览器中直接输入URL并回车,便发起了一个 GET请求,请求的参数会直接包含到 URL里。例如,在百度搜索引擎中搜索 Python 就是一个 GET请求,链接为 https://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 请求。其实除了这两个,还有一些请求方法,如HEAD、PUT、DELETE、CONNECT、OPTIONS、TRACE 等,我们简单将请求方法总结为下表。
方法
描述
GET
HEAD
POST
PUT
DELETE
CONNECT
OPTIONS
TRACE
请求页面, 并返回页面内容
类似于GET请求, 只不过返回的响应中没有具体内容。用于获取报头
大多用于提交表单或上传文件,数据包含在请求体中
用客户端传向服务器的数据取代指定文档中的内容
请求服务器删除指定的页面
把服务器当作跳板, 让服务器代替客户端访问其他网页
允许客户端查看服务器的性能
回显服务器收到的请求。主要用于测试或诊断
3、请求头
请求头,用来说明服务器要使用的附加信息, 比较重要的信息有 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 类型。
请求头是请求的重要组成部分,在写爬虫时,通常都需要设定请求头。
4、请求体
请求体,一般承载的内容是 POST 请求中的表单数据,对于 GET请求,请求体为空。
例如,我登录 GitHub时捕获到的请求和响应如下图所示。
登录之前,需要先填写用户名和密码信息,登录时这些内容会以表单数据的形式提交给服务器,此时需要注意 Request Headers 中指定 Content-Type 为 application /x-www-form-urlencoded。只有这样设置Content-Type,内容才会以表单数据的形式提交。另外,也可以将Content-Type设置为 application/json 来提交 JSON数据, 或者设置为 multipart/form-data 来上传文件。
下表列出了 Content-Type 和 POST 提交数据方式的关系。
Content-Type |
POST 提交数据的方式 |
application/x-www-form-urlencoded |
表单数据 |
multipart/form-data |
表单文件上传 |
application/json |
序列化 JSON数据 |
text/xml |
XML 数据 |
在爬虫中,构造 POST 请求需要使用正确的 Content-Type,并了解设置各种请求库的各个参数时使用的都是哪种 Content-Type,如若不然可能会导致 POST提交后无法得到正常响应。
五、响应
响应,即Response,由服务器返回给客户端,可以分为三部分:响应状态码(Response Status Code)、响应头(Response Headers) 和响应体(Response Body)。
1、响应状态码
响应状态码,表示服务器的响应状态,如200代表服务器正常响应、404代表页面未找到、500代表服务器内部发生错误。在爬虫中,我们可以根据状态码判断服务器的响应状态,如状态码为 200,证明成功返回数据,可以做进一步的处理,否则直接忽略。下表列出了常见的错误状态码及错误原因。
状 态 码 |
说 明 |
详 情 |
406 |
不接收 |
无法使用请求的内容响应请求的网页 |
407 |
需要代理授权 |
请求者需要使用代理授权 |
408 |
请求超时 |
服务器请求超时 |
409 |
冲突 |
服务器在完成请求时发生冲突 |
410 |
已删除 |
请求的资源已永久删除 |
411 |
需要有效长度 |
服务器不接收不含有效内容长度标头字段的请求 |
412 |
未满足前提条件 |
服务器未满足请求者在请求中设置的某一个前提条件 |
413 |
请求实体过大 |
请求实体过大, 超出服务器的处理能力 |
414 |
请求 URI 过长 |
请求网址过长, 服务器无法处理 |
415 |
不支持类型 |
请求格式不被请求页面支持 |
416 |
请求范围不符 |
页面无法提供请求的范围 |
417 |
未满足期望值 |
服务器未满足期望请求标头字段的要求 |
500 |
服务器内部错误 |
服务器遇到错误, 无法完成请求 |
501 |
未实现 |
服务器不具备完成请求的能力 |
502 |
错误网关 |
服务器作为网关或代理, 接收到上游服务器的无效响应 |
503 |
服务不可用 |
服务器目前无法使用 |
504 |
网关超时 |
服务器作为网关或代理, 没有及时从上游服务器接收到请求 |
505 |
HTTP版本不支持 |
服务器不支持请求中使用的 HTTP 协议版本 |
2、响应头
响应头,包含了服务器对请求的应答信息,如 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:用于指定响应的过期时间,可以让代理服务器或浏览器将加载的内容更新到缓存中。当再次访问相同的内容时,就可以直接从缓存中加载,达到降低服务器负载、缩短加载时间的目的。
3、响应体
响应体, 这可以说是最关键的部分了,响应的正文数据都存在于响应体中, 例如请求网页时,响应体就是网页的 HTML 代码;请求一张图片时,响应体就是图片的二进制数据。我们做爬虫请求网页时, 要解析的内容就是响应体, 如图所示。
在浏览器开发者工具中单击 Preview,就可以看到网页的源代码,也就是响应体的内容,这是爬虫的解析目标。在做爬虫时,我们主要通过响应体得到网页的源代码、JSON 数据等,然后从中提取相应内容。
六、HTTP请求过程
在浏览器地址栏中输入一个 URL,按下回车之后便可观察到对应的页面内容。实际上,这个过程是浏览器先向网站所在的服务器发送一个请求,网站服务器接收到请求后对其进行处理和解析,然后返回对应的响应,接着传回浏览器。由于响应里包含页面的源代码等内容,所以浏览器再对其进行解析,便将网页呈现出来,流程如图所示。
上图中的客户端代表我们自己的电脑或手机浏览器,服务器就是要访问的网站所在的服务器。为了更直观地说明上述过程,这里用 Chrome 浏览器开发者模式下的 Network监听组件来做一下演示。Network监听组件可以在访问当前请求的网页时,显示产生的所有网络请求和响应。打开Chrome浏览器,访问百度,这时候单击鼠标右键并选择“检查”菜单(或者直接按快捷键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等信息,这些是请求的一部分,服务器会根据请求头里的信息判断请求是否合法,进而做出对应的响应。响应头是响应的一部分,其中包含服务器的类型、文档类型、 日期等信息,浏览器在接收到响应后,会对其进行解析,进而呈现网页内容。