前言
因特网联系的是世界各地的计算机 (通过电缆), 万维网联系的是网上的各种各样资源(通过文本超链接), 如静态的 html 文件, 动态的软件程序....... 由于万维网的存在, 处于因特网中的每台计算机可以很方便地进行消息交流, 文件资源交流....... 基于因特网的帮助, 我们可以在 web 客户端(如浏览器等) 通过 HTTP 访问或者下载 Web 服务端 (如网站服务器) 上面的 Web 资源.
因特网由 TCP/IP 统筹, 在 TCP/IP 的基础上进行 HTTP 活动. HTTP 位于 TCP/IP 的应用层. 了解 HTTP 是为了让爬虫程序模拟客户端的行为去请求服务器数据和反爬虫.
通过在开发者工具里查看分析网页客户端 (浏览器)HTTP 的请求报文, 获得网页 HTTP 的请求 URL, 请求方法, 请求头, cookie......, 爬虫程序可以完备的模拟浏览器去爬取网站的资源; 通过查看分析服务器(网站) 返回的 HTTP 响应报文, 了解响应状态, 响应主体......, 爬虫程序就可以根据这些响应内容去实现程序逻辑, 处理响应内容, 提取目标信息......
HTTP 基础
相关术语
Internet: 因特网, 一种把各个网络联系起来的网络, 主要由许多计算机和电缆组成
WWW(world wide Web): 万维网, 一种抽象的信息空间
Web 浏览器(Web browser): 可以显示网页服务器或者文件系统的 HTML 网页, 并让用户与网页交互的软件.
Web 服务器(Web Server): 是驻留于因特网上某种类型计算机的程序, 可以向浏览器等 Web 客户端提供文档, 也可以放置网站文件, 让全世界浏览; 可以放置数据文件, 让全世界下载. 一般指网站服务器,
HTTP(HyperText Transfer Protocol): 超文本传输 (转移) 协议, 处于 TCP/IP 协议簇的应用层.
URL(Uniform Resource Locator): 统一资源定位符
URI(Uniform Resource Identifier): 统一资源标识符
URN(Universal Resource Name): 统一资源名称
HTML(HyperText Markup Language): 超文本标记语言
TCP/IP(TCP/IP Protocol Suite):TCP/IP 协议族, 其中一种定义为互联网相关的各类协议族的总称
TCP(Transmission Control Protocol): 传输控制协议
IP(Internet Protocol Address): 网际协议地址
cookie: 一种用户识别机制, 一种功能强大且高效的持久身份识别的技术
DNS(Domain Name System): 域名系统, 提供域名到 IP 地址的解析服务, 处于 TCP/IP 协议簇的应用层.
IPv4(Internet Protocol Version 4): 网际协议第四版, 规定 IP 长度为 32 位
IPv6(Internet Protocol Version 6): 网际协议第六版, 规定 IP 长度为 128 位
URL
通常我们说的网址就是一个 URL,URL 是 URI 的一个子集, URN 也是 URI 的一个子集, URL 和 URN 存在交集, 大概率的情况都是 URI=URL, 关系如下:
当我们在 Web 客户端浏览器输入网址 (URL) 的时候, 如果网址无误, 通过 HTTP 就能得到 Web 服务端的响应. URL 语法如下:
scheme: 方案, 访问 Web 服务器时使用的协议类型, 如 http: 或 https: 不区分大小写, 最后加一个冒号:
user: 用户, 访问服务器时指定用户登录, 为可选项
password: 用户密码, 和用户相连, 可选
host: 主机, 服务器地址, 可以是 DNS 可解释的域名 (人性化) 或 IPv4/IPv6 地址
port: 端口号, 指定服务器连接的端口号, 即监听的端口号, 若客户端省略则为默认端口, HTTP 默认端口为 80
path: 路径, 指定服务器上的文件路径, 定位资源. 由一个斜杠 / 与前面的 URL 组件分隔开
params: 参数, 指定输入参数, 形式为键值对, 用; 将其与 path 的部分隔开. 可选
query: 查询, 为查询字符串, 针对已选的路径内的资源, 传入参数, 用? 将其与 URL 其他部分隔开. 可选
frag: 片段, 为片段标识符, 通常标记出以获取的资源的子资源, 通过 #与 URL 其它部分隔开, 不会传递到服务端, 由客户端内部使用. 可选
了解这些是有用的, 其中的一个用途就是在爬虫中构建自己的 URL 请求参数. 例如书上所说的如果要爬取作者新的浪微博, 由于微博是是 Ajax 的方式加载, 需要在开发者工具才能看到 Ajax 请求和服务器的响应, 所以请求 url 需要在开发者工具里查找, 经过查找分析, 发现 xhr(可以查看 Ajax 的请求和响应信息)中的请求 URL 传入了 4 个参数(问号后面的即为查询传入的参数), 前面三个是不变的, 而变化的是最后一个, 我们可以利用 urllib 模块中的 urlencode 模块来传递这些参数, 链接如下:
https://m.weibo.cn/api/container/getIndex?type=uid&value=2830678474&containerid=1076032830678474&page=2
代码如下:
- import requests
- from urllib.parse import urlencode
- base_url = 'https://m.weibo.cn/api/container/getIndex?'
- headers = {
- 'Host': 'm.weibo.cn',
- 'Referer': 'https://m.weibo.cn/u/2830678474',
- 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
- 'X-Requested-With': 'XMLHttpRequest',
- } # 模拟请求头
- def get_page(page):
- params = {
- 'type': 'uid',
- 'value': '2830678474',
- 'containerid': '1076032830678474',
- 'page': page
- }
- url = base_url + urlencode(params)
- response = requests.get(url, headers=headers)
- return response.JSON() # 由于内容是由 Ajax 加载, 响应的内容是 JSON 的形式
- if __name__ == '__main__':
- for page in range(1,3):
- result = get_page(page)
- print(result)
再如要要抓取今日头条一些街拍的图片, 在搜索框输入 "街拍" 二字之后回车便进入到街拍页面, 看下网页的 url 是: https://www.toutiao.com/search/?keyword=街拍, 但是直接拿这个去爬取图片是不成功的, 因为这些数据是 Ajax 加载, 不存在网页源代码文件中, 为之奈何? 和前面的处理方法一样啊, 其中可变参数是 offset, 这是构造的关键, 其中链接的形式如下:
https://www.toutiao.com/search_content/?offset=20&format=json&keyword=街拍&autoload=true&count=20&curtab=1&from=search_tab
代码如下:
- import requests
- from urllib.parse import urlencode
- def get_page(offset):
- headers = {
- 'User-Agent': "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0;"
- ".NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
- }
- params = {
- 'offset': offset,
- 'format': 'json',
- 'keyword': '街拍',
- 'autoload': 'true',
- 'count': '20',
- 'cur_tab': '1',
- 'from': 'search_tab',
- }
- base_url = 'https://www.toutiao.com/search_content/?'
- url = base_url + urlencode(params)
- try:
- response = requests.get(url, headers=headers)
- if response.status_code == 200:
- print(response.status_code)
- return response.JSON()
- except requests.ConnectionError:
- return None
- if __name__ == '__main__':
- for offset in range(20, 60, 20):
- result = get_page(offset)
- print(result)
HTTP 方法
GET 方法和 POST 方法是 HTTP 中最常用的方法. 它们能够访问和下载和访问网站服务器资源, 这些网页就是我们要爬取并摘取数据的资源, 爬虫程序模拟了浏览器实现这种 HTTP 的 GET 或者 POST 等方法去获取资源.
GET 方法
GET 方法用来请求访问已被 URI 识别的资源. 指定的资源经服务器端解析后返回响应内容. 也就是说, 如果请求的资源是文本, 那就保持原样返回; 如果是像 CGI(Common Gateway Interface, 通用网关接口)那样的程序, 则返回经过执行后的输出结果. 如图:
POST 方法
POST 方法用来传输实体的主体.
虽然用 GET 方法也可以传输实体的主体, 但一般不用 GET 方法进行传输, 而是用 POST 方法. 虽说 POST 的功能与 GET 很相似, 但 POST 的主要目的并不是获取响应的主体内容. 如图:
HTTP 报文
HTTP 报文是指客户端和服务器用于 HTTP 交互的的信息, 客户端的 HTTP 报文称为请求报文, 服务器端的报文称为响应报文. 需要爬虫某网页的时候, 我们可以在开发者工具里面查找并分析这些报文以了解客户端的 HTTP 请求及 URL 等, 从而模拟客户端去进行数据爬取.
HTTP 报文大致可以分为报文首部和主体, 两者由最初出现的空行 (CR+LF) 来划分, 如图:
请求首部和请求实例
请求首部由请求行 (HTTP 请求方法, URL,HTTP 协议版本) 和首部字段 (请求首部字段, 通用首部字段, 实体首部字段) 及其他内容组成, 以下是请求头部结构和请求实例, 其中请求头 User-Agent 是爬虫在模拟 HTTP 方法请求网页资源时常常需要添加的一个参数.
响应首部和响应实例
响应首部则由状态行 (HTTP 协议版本, 响应状态码(数字和原因短语)) 和 HTTP 首部字段 (响应首部字段, 通用首部字段, 实体首部字段) 组成, 以下分别是响应头部结构和响应实例
通用首部字段
请求首部字段
响应首部字段
Cookie
HTTP 是无状态协议, 它不对之前发生过的请求和响应的状态进行管理. 也就是说, 无法根据之前的状态进行本次的请求处理. 为解决此矛盾, Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态. Cookie 会根据从服务端发送过来的报文内的一个叫 Set-Cookie 的首部字段信息, 通知客户端保存 cookie. 当下次客户端再向此服务器发送请求时, 客户端会自动在请求报文加入值后再发过去给服务端. 服务端接收到客户端发送过来的 cookie 之后, 会检查这个 cookie 究竟是从哪一个客户端发送过来, 然后对比之前的服务记录, 最后得到之前的状态信息. 爬虫中也会模拟这种带 cookie 的 HTTP 请求来实现反爬虫或使得抓取的数据更全面等, 如图
TCP/IP 的分层管理
因特网由 TCP/IP 统筹, 所以万维网间接由它统筹. 由于 HTTP 位于 TCP/IP 协议簇的应用层, 了解 TCP/IP 协议簇有助于我们更加了解 HTTP.CP/IP 协议族里重要的一点就是分层, 分层的好处在于, 当互联网需要改动时, 分层之后只需把变动对应的层替换掉即可, 设计也变得相对简单. TCP/IP 协议族按层次分别分为以下 4 层: 应用层, 传输层, 网络层和数据链路层. 如图:
应用层: 应用层决定了向用户提供应用服务时通信的活动.
TCP/IP 协议族内预存了各类通用的应用服务. 比如 FTP(FileTransfer Protocol, 文件传输协和 DNS(Domain Name System, 域名系统)服务就是其中两类. HTTP 协议也处于该层.
传输层: 传输层对上层应用层, 提供处于网络连接中的两台计算机之间的数据传输.
在传输层有两个性质不同的协议: TCP(Transmission ControlProtocol, 传输控制协议)和 UDP(User Data Protocol, 用户数据报协议).
网络层(又名网络互连层):
网络层用来处理在网络上流动的数据包. 数据包是网络传输的最小数据单位. 该层规定了通过怎样的路径 (所谓的传输路线) 到达对方计算机, 并把数据包传送给对方. 与对方计算机之间通过多台计算机或网络设备进行传输时, 网络层所起的作用就是在众多的选项内选择一条传输路线.
链路层(又名数据链路层, 网络接口层):
用来处理连接网络的硬件部分. 包括控制操作系统, 硬件的设备驱动, NIC(Network Interface Card, 网络适配器, 即网卡), 及光纤等物理可见部分(还包括连接器等一切传输媒介). 硬件上的范畴均在链路层的作用范围之内.
利用 TCP/IP 协议族进行网络通信时, 会通过分层顺序与对方进行通信. 发送端从应用层往下走, 接收端则往应用层往上走. 如图:
首先, 客户端在应用层 (HTTP 协议) 发出一个想看某个网页的 HTTP 请求, 接着为了传输方便, 传输层 (TCP 协议) 把从应用层接受到的数据 (请求报文) 进行分割, 并在各个报文上打上标记序号和端口号发送给网络层, 在网络层(IP 协议), 增加作为通信目的地的 Mac 地址发送给链路层, 至此, 发往服务器的通信请求就准备齐全了
接着服务端在链路层接收到客户端发来的请求, 然后按序一直往上发送到应用层, 当请求传到应用层服务端才算真正接收到客户端发送过来的 HTTP 请求. 发送端在层与层之间传输数据时, 每经过一层时必定会被打上一个该层所属的首部信息. 反之, 接收端在层与层传输数据时, 每经过一层时会把对应的首部消去. 这种把数据信息包装起来的做法称为封装(encapsulate)
TCP 三次握手
TCP 处于 HTTP 协议的传输层, 三次握手的目的在于保证请求信息的有效性, 防止失效的连接请求报文段被服务端接收, 从而产生错误. 关于三次握手网上很多有趣的解释
参考
本文叙述的是一些与 Python 爬虫相关的 HTTP 内容, 主要参考自《HTTP 权威指南》,《图解 HTTP》和《Python3 网络爬虫开发实战》, 仅仅是个人理解, 望指正.
来源: https://www.cnblogs.com/ydkh/p/9987124.html