基础版

HTTP协议简介

讲解http常见的请求方法和使用

WEB API接口 大都是基于 HTTP 协议的,所以,要进行接口测试 首先要了解 HTTP 协议 的 基础知识。

HTTP 协议 全称是 超文本传输协议, 英文是 Hypertext Transfer Protocol 。

HTTP 最初是用来 在 浏览器和 网站服务器(web服务)之间 传输超文本(网页、视频、图片等)信息的。

由于 HTTP 简洁易用,后来,不仅仅是浏览器 和 服务器之间 使用它, 服务器和服务器之间, 手机App 和 服务器之间, 都广泛的采用。 成了一个软件系统间 通信 的首选协议 之一。

HTTP 有好几个版本,包括: 0.91.01.123 ,当前最广泛使用的是 HTTP/1.1 版本。

HTTP 协议最大的特点是 通讯双方 分为 客户端服务端

HTTP3 以前版本 (除了HTTP3) , 都是 基于 TCP 协议的, 所以要进行通讯,客户端 必须先 和服务端 创建 TCP 连接。

而且 HTTP 双方的信息交互,必须是这样一种方式:

  • 客户端 先发送 http请求(request)给 服务端
  • 然后服务端 发送 http响应(response)给 客户端

特别注意:HTTP协议中,服务端不能主动先发送信息给 客户端。

  • http1.0定义了三种:
    • GET: 向服务器获取资源,比如常见的查询请求
    • POST: 向服务器提交数据而发送的请求
    • Head: 和get类似,返回的响应中没有具体的内容,用于获取报头
  • http1.1定义了六种
    • PUT:一般是用于更新请求,比如更新个人信息、商品信息全量更新
    • PATCH:PUT 方法的补充,更新指定资源的部分数据
    • DELETE:用于删除指定的资源
    • OPTIONS: 获取服务器支持的HTTP请求方法,服务器性能、跨域检查等
    • CONNECT: 方法的作用就是把服务器作为跳板,让服务器代替用户去访问其它网页,之后把数据原原本本的返回给用户,网页开发基本不用这个方法,如果是http代理就会使用这个,让服务器代理用户去访问其他网页,类似中介
    • TRACE:回显服务器收到的请求,主要用于测试或诊断

而且在1.1 以前的版本, 服务端 返回响应给客户端后,连接就会 断开 ,下一次双方要进行信息交流,必须重复上面的过程,重新建立连接,客户端发送请求,服务返回响应。

到了 1.1 版本, 建立连接后,这个连接可以保持一段时间(keep alive), 这段时间,双方可以多次进行 请求和响应, 无需重新建立连接。

如果客户端是浏览器,如何在chrome浏览器中查看 请求和响应的HTTP消息。

HTTP请求消息

响应状态码

  • 浏览器向服务器请求时,服务端响应的消息头里面有状态码,表示请求结果的状态

  • 分类

    • 1XX: 收到请求,需要请求者继续执行操作,比较少用

    • 2XX: 请求成功,常用的 200

    • 3XX: 重定向,浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取;

      • 好处:网站改版、域名迁移等,多个域名指向同个主站导流
      • 必须记住: 301:永久性跳转,比如域名过期,换个域名 302:临时性跳转
    • 4XX: 客服端出错,请求包含语法错误或者无法完成请求

      • 必须记住: 400: 请求出错,比如语法协议 403: 没权限访问 404: 找不到这个路径对应的接口或者文件 405: 不允许此方法进行提交,Method not allowed,比如接口一定要POST方式,而你是用了GET
    • 5XX: 服务端出错,服务器在处理请求的过程中发生了错误

      • 必须记住: 500: 服务器内部报错了,完成不了这次请求 503: 服务器宕机

http请求消息由下面几个部分组成

请求行 request line

是http请求的第一行的内容,表示要操作什么资源,使用的 http协议版本是什么。

里面包含了3部分信息: 请求的方法,操作资源的地址, 协议的版本号

例如

1
GET /mgr/login.html HTTP/1.1

表示要 获取 资源, 资源的 地址/mgr/login.html , 使用的 协议HTTP/1.1

1
POST /api/medicine HTTP/1.1

表示 添加 资源信息, 添加资源 到 地址 /api/medicine , 使用的 协议HTTP/1.1

GET、POST是请求的方法,表示这个动作的大体目的,是获取信息,还是提交信息,还是修改信息等等

常见的HTTP 请求方法包括:

  • GET

    从服务器 获取 资源信息,这是一种最常见的请求。

    比如 要 从服务器 获取 网页资源、获取图片资源、获取用户信息数据等等。

  • POST,请求方法就应该是

    添加 资源信息 到 服务器进行处理(例如提交表单或者上传文件)。

    比如 要 添加用户信息、上传图片数据 到服务器 等等

    具体的数据信息,通常在 HTTP消息体中, 后面会讲到

  • PUT

    请求服务器 更新 资源信息 。

    比如 要 更新 用户姓名、地址 等等

    具体的更新数据信息,通常在 HTTP消息体中, 后面会讲到

  • DELETE

    请求服务器 删除 资源信息 。

    比如 要 删除 某个用户、某个药品 等等

HTTP还有许多其他方法,比如 PATCH、HEAD 等,不是特别常用,暂且不讲。

请求行里面还包括了url,

比如

1
/mgr/login.html

url表示要获取资源的具体路径

url特别要注意的是 url参数 ,英文叫 url query String

什么是url参数?

比如:

1
https://www.baidu.com/s?wd=iphone&rsv_spt=1

问号后面的部分 wd=iphone&rsv_spt=1 就是 url 参数,

每个参数之间是用 & 隔开的。

上面的例子中 有两个参数 wd 和 rsv_spt, 他们的值分别为 iphone 和 1 。

url参数的格式,有个术语叫 urlencoded 格式。

请求头 request headers

请求头是http请求行下面的 的内容,里面存放 一些 信息。

比如,请求发送的服务端域名是什么, 希望接收的响应消息使用什么语言,请求消息体的长度等等。

通常请求头 都有好多个,一个请求头 占据一行

单个请求头的 格式是: 名字: 值

HTTP协议规定了一些标准的请求头,点击查看MDN的描述

开发者,也可以在HTTP消息中 添加自己定义的请求头

消息体 message body

请求的url、请求头中 可以存放 一些数据信息, 但是 有些数据信息,往往需要 存放在消息体中。

特别是 POST、PUT等请求,添加、修改的数据信息 通常都是 存放在 请求消息体 中的。

如果 HTTP 请求 有 消息体, 协议规定 需要在 消息头和消息体 之间 插入一个空行, 隔开 它们。

请求消息体中保存了要提交给服务端的数据信息。

比如:客户端要上传一个文件给服务端,就可以通过HTTP请求发送文件数据给服务端。

文件的数据 就应该在请求的消息体中。

再比如:上面示例中 客户端要添加药品,药品的名称、编码、描述,就存放在请求消息体中。

WEB API 请求消息体 通常是某种格式的文本,常见的有

  • Json
  • Xml
  • www-form-urlencoded

后面会有详细的讲述

HTTP响应消息

下面是1个http响应消息的示例

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Date: Thu, 19 Sep 2019 08:08:27 GMT
Server: WSGIServer/0.2 CPython/3.7.3
Content-Type: application/json
Content-Length: 37
X-Frame-Options: SAMEORIGIN
Vary: Cookie
{"ret": 0, "retlist": [], "total": 0}

HTTP响应消息包含如下几个部分

状态行 status line

状态行在第一行,包含3个部分:

  • 协议版本

    上面的示例中,就是 HTTP/1.1

  • 状态码

    上面的示例中,就是 200

  • 描述状态的短语

    上面的示例中,就是 OK

我们重点来看一下状态码,它表示了 服务端对客户端请求的处理结果 。

状态码用3位的数字来表示,第一位 的 数字代表 处理结果的 大体类型,常见的有如下几种:

• 2xx 通常 表示请求消息 没有问题,而且 服务器 也正确处理了

最常见的就是 200

• 3xx 这是重定向响应,常见的值是 301,302, 表示客户端的这个请求的url地址已经改变了, 需要 客户端 重新发起一个 请求 到另外的一个url。

• 4xx 表示客户端请求有错误, 常见的值有:

400 Bad Request 表示客户端请求不符合接口要求,比如格式完全错误

401 Unauthorized 表示客户端需要先认证才能发送次请求

403 Forbidden 表示客户端没有权限要求服务器处理这样的请求, 比如普通用户请求删除别人账号等

404 Not Found 表示客户端请求的url 不存在

• 5xx 表示服务端在处理请求中,发生了未知的错误。

通常是服务端的代码设计问题,或者是服务端子系统出了故障(比如数据库服务宕机了)

响应头 response headers

响应头 是 响应状态行下面的 的内容,里面存放 一些 信息。 作用 和 格式 与请求头类似,不再赘述。

消息体 message body

有时候,http响应需要消息体。

同样, 如果 HTTP 响应 有 消息体, 协议规定 需要在 消息头和消息体 之间 插入一个空行, 隔开 它们。

比如,白月SMS系统 请求 列出 药品 信息,那么 药品 信息 就在HTTP响应 消息体中

再 比如,浏览器地址栏 输入 登录网址,浏览器 请求一个登录网页的内容,网站服务器,就在响应的消息体中存放登录网页的html内容。

和请求消息体一样,WEB API 响应消息体 通常也是某种格式的文本,常见的有:

  • Json
  • Xml
  • www-form-urlencoded

实例

POST请求

POST请求相比GET请求要复杂一点,因为其需要加入请求Header和请求Body,以下是Pre-request Script在发送POST请求的案例:

  • 构造一个登录请求
1
2
3
4
5
6
7
8
9
const loginRequest = {
url: 'http://115.28.108.130:5000/api/user/login/',
method: "POST",
body: {
mode: 'urlencoded', // 模式为表单url编码模式
urlencoded: 'name=张三&password=123456'
}
};

  • 发送请求
1
2
3
pm.sendRequest(loginRequest, function (err, res) {
console.log(err ? err : res.text());
});

发送JSON格式请求与发送POST请求类似,以下采用了raw模式发送请求体。

  • 构造一个注册请求
1
2
3
4
5
6
7
8
9
const regRequest = {
url: 'http://115.28.108.130:5000/api/user/reg/',
method: 'POST',
header: 'Content-Type: application/json', //注意要在Header中声明内容使用的类型
body: {
mode: 'raw', // 使用raw(原始)格式
raw: JSON.stringify({ name: '小小', password: '123456' }) //要将JSON对象转为文本发送
}
};

因为HTTP请求都支持raw格式,我们只要能够获取请求的raw格式,便可采用raw模式发送任意类型的请求体了。

接口数据用例

数据用例设计

Postman支持的是csv文件作为数据用例,数据用例包含三大部分,分别为:用例标题(title),入参(grant_type,appid,secret),期望结果(expected)

title grant_type appid secret expected
正确的用例 client_credential wx508a5cacbbfc1141 fa4fc7f17ddead12d7cdcd994e7d2543 7200
grant_type错误 client_credentia wx508a5cacbbfc1141 fa4fc7f17ddead12d7cdcd994e7d2543 40002
appid错误 client_credential wx508a5cacbbfc114 fa4fc7f17ddead12d7cdcd994e7d2543 40013
secret错误h client_credential wx508a5cacbbfc1141 fa4fc7f17ddead12d7cdcd994e7d254 40001

我们可以根据黑盒用例设计方法如等价类、边界值、判定表、正交实验法对入参进行用例设计,得到各种不同的测试场景(取值组合)

数据用例参数化

前面我们在csv文件中编写好了数据用例,且保证第一行为参数的名称。将请求中入参值依次进行替换,在Postman中参数的编写规格为两个花括号,如:

设置迭代器

要读取所有的测试用例,需要设置迭代器让其循环读取那些测试数据。Postman中设置迭代器需在Run中完成,请看【4.Runner执行测试】

结果检查(断言)

Postman的断言功能在Test模块中,比如要测试返回结果是否含有某一字符串,就需要在Test中编写相应的代码,Test中的代码使用的是JavaScript语法。

img

自带Tests函数

Postman提供了参考代码供我们选择即可,主要断言代码有如下几种:

# 断言状态码是否为200,在断言中此种断言价值不高

1
2
3
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});

# 断言响应文本中是否包含某个数据串,常用

1
2
3
pm.test("Body matches string", function () {
pm.expect(pm.response.text()).to.include("string_you_want_to_search");
});

# 使用JsonPath断言

1
2
3
4
pm.test("Your test name", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.value).to.eql(100);
});

# 检查响应正文中是否包含某个子串

1
2
3
pm.test("Body is correct", function () {
pm.response.to.have.body("response_body_string");
});

# 响应信息包含列表中其中某一个

1
2
3
pm.test("Successful POST request", function () {
pm.expect(pm.response.code).to.be.oneOf([201,202]);
});

# 将xml响应转为json

1
var jsonObject = xml2Json(responseBody);

手工tests函数

也可以使用断言并赋值的形式,相对而言,以下方式会更为简洁实用。

img

常见的断言代码有:

# 检查response的body中是否包含字符串

1
tests["Body matches string"] = responseBody.has("string_you_want_to_search");

# 检查JSON节点的值和节点元素的个数为5

1
2
3
var data = JSON.parse(responseBody).city; //把JSON字符串转化为对象
tests["Your test name"] = data.value===100;
tests["program's lenght"] = data.programs.length===5;

# 验证Response time是否小于某个值

1
tests["Response time is less than 200ms"] = responseTime < 200;

tests高级操作

我们可以引用数据用例csv文档中的预期结果进行断言。

1
2
3
var jsonData = JSON.parse(responseBody) ;
# data.expected 为csv数据文件中的预期结
tests["测试结果通过"] = jsonData.expires_in===data.expected ;

在tests还可以使用判断语句进行断言,如下:

img

在Postman中断言的操作非常灵活,需要同学们多进行练习。

1
2
3
4
5
6
7
8
9
10
11
12
pm.test("预期结果包含:长沙,实际结果为:"+result, function () {
if(JSON.parse(responseBody).city==="长沙"){
pm.expect(pm.response.text()).to.include("千里");
pm.expect(pm.response.text()).to.include("长沙");
}else if(JSON.parse(responseBody).city==="Changsha"){
pm.expect(pm.response.text()).to.include("千里");
pm.expect(pm.response.text()).to.include("Changsha");
}else if(JSON.parse(responseBody).city==="長沙"){
pm.expect(pm.response.text()).to.include("千里");
pm.expect(pm.response.text()).to.include("長沙");
}
});

Runner执行测试

设置迭代器

img

查看运行结果

img

保存接口配置

待整个接口都调试完毕后,记得点击 Save 去保存接口信息:

img

去保存当前 API 接口,然后需要填写相关的接口信息:

l Request Name: 请求的名字

我一般习惯用保存为 接口的最后的字段名,比如

l Request Description: 接口的描述

最好写上该接口的要实现的基本功能和相关注意事项

支持 Markdown 语法

Select a collection or folder to save: 选择要保存到哪个分组(或文件夹)

往往保存到某个 API 接口到所属的该项目名的分组

Postman的参数

自动解析多个参数Param

比如,对于一个 GET 的请求的 url 是: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx508a5cacbbfc1141&secret=fa4fc7f17ddead12d7cdcd994e7d2543

对应着其实是?key=value形式中包含多个 Http 的 GET 的 query string=query parameters

Postman 可以自动帮我们解析出对应参数:

img

临时禁用参数

在不删除某参数的情况下,如果想要暂时不传参数,可以方便的通过不勾选的方式去实现

img

批量编辑多个参数

如果想要批量的编辑参数,可以点击右上角的Bulk Edit,去实现批量编辑。

Postman发送POST请求详讲

POST 请求不能像GET一样直接在浏览器输入就可以请求,需要借助工具来完成。

发送key-value 的请求:

以login 接口为例,在Body 中选取“form-data” 格式,输入所需的key-value, 选取对应的环境变量。

img

发送json格式的请求:

以add user 接口为例,在Body 中选取“raw” 格式,根据接口文档输入json 数据, 有需要应用环境变量的选取环境变量。

img

发送文件的请求

以file upload 接口为例,在Body 中选取“form-data” 格式,在key 里输入”file”,在右边的下拉里选取类型为”File”,点击”Choose Files” 就可以上传本地文件了。

img

环境变量设置

设置环境变量的意义

在测试 API 期间,往往存在多种环境,对应 IP 地址(或域名也不同)。比如:

Dev:http://192.168.1.21/oa/index.jsp

l 用于开发期间的线上的 Development 的测试环境

LocalTest:http://192.168.1.42/oa/index.jsp

l 用于开发期间配合后台开发人员的本地局域网内的本地环境,用于联合调试 API 接口

Product:http://www.example.com/oa/index.jsp

l 用于开发完成发布到生产环境

在测试API期间,往往需要修改API地址,这样效率会比较低且更换后的地址没法保存。

环境变量设置

在Postman的设置区有Environment 和 Global Variable,用于解决这个问题,实现不同环境的管理:

img

很明显,就可以用来实现不用手动修改 url 中的服务器地址,从而动态的实现,支持不同服务器环境:

l Production 生产环境

l Development 开发环境

l Local 本地局域网环境

环境变量可以使用在以下地方

l URL

l URL params

l Header values

l form-data/url-encoded values

l Raw body content

注意:在你要使用的变量名上附上\**双花括号**\,一个请求只能应用一个环境变量。**

使用代码设置环境变量

我们可以在Pre-request Script和Test模块中进行环境变量设置。

—1.设置环境变量

1
postman.setEnvironmentVariable("key", "value");

—2.获取环境变量

1
pm.environment.get("variable_key");

全局变量

全局变量(Global Variable)顾名思义是针对于所有脚本和所有环境将生效的变量,它的作用域大于环境变量。设置全局变量的方法与环境变量相似:

—1.设置全局变量

1
pm.globals.set("variable_key", "variable_value");

—2.获取全局变量

1
pm.globals.get("variable_key");

进价版

requests

简介

Requests 请求是用 Python 语言编写, 基于 urllib,采用 Apache2 Licensed 开源协议的 HTTP 库。 它比 urllib 更加方便,可以节约我们大量的工作,完全满足 HTTP 测试需求。 Requests 的是以 PEP 20 的习语为中心开发的, 所以它比 urllib 更加 Python 智能化。 更重要的一点是它支持 Python3

安装

1
pip install requests

常用参数

1
2
3
4
5
6
7
8
9
10
method: 请求方式 get,或者 post,put,delete 等
url 请求的: url 地址 接口文档标注的接口请求地址
params:请求数据中的链接,常见的一个 get 请求,请求参数都是在 url 地址中
data :请求数据,参数 表单的数据格式
json: 接口常见的数据请求格式
headers:请求头信息,http 请求中,比如说编码方式等内容添加
cookie:保存的用户登录信息,比如做一些充值功能,但是需要用户已经登录,需要 cookie 信息的请求信息传输
file:接口中上传文件
timeout :超时处理 proxys 设置代理
stream :文件下载功能,通过请求方式,下载文件

响应内容

1
2
3
4
5
6
7
8
r.encoding #获取当前的编码
r.encoding = 'utf-8' #设置编码
r.text #以encoding解析返回内容。字符串方式的响应体,会自动根据响应头部的字符编码进行解码。
r.cookies #返回cookie
r.headers #以字典对象存储服务器响应头,但是这个字典比较特殊,字典键不区分大小写,若键不存在则返回None
r.status_code #响应状态码
r.json() #Requests中内置的JSON解码器,以json形式返回,前提返回的内容确保是json格式的,不然解析出错会抛异常
r.content #以字节形式(二进制)返回。字节方式的响应体,会自动为你解码 gzip 和 deflate 压缩。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
url='http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=xxxxxxxxxxx'
r = requests.get(url=url)
print (r.text)

#返回数据
__GetZoneResult_ = {
mts:'1316375',
province:'广东',
catName:'中国联通',
telString:'XXXXXXXXXXX',
areaVid:'30517',
ispVid:'xxxxxxxxxxx',
carrier:'广东联通'
}

把参数添加到params中

1
2
3
4
5
import requests
url='http://tcc.taobao.com/cc/json/mobile_tel_segment.htm'
params={'tel':''}
r = requests.get(url=url,params=params)
print (r.text) #打印接口请求返回的值

Requests 中 post 请求

From-data 数据格式的接口

注册接口,
数据格式是 data 格式
url 地址:​ ​http://127.0.0.1:8000/register​​请求方式:post
请求 请求参数:username password

1
2
3
4
5
6
import requests 
url="http://127.0.0.1:8000/register"
#表单数据格式,参数 data ,数据都是字典去保存
data={"username":"liang001", "password":"123456" }
r_reg=requests.post(url=url,data=data)
print (r_reg.text)

json 数据格式的接口

登录接口,数据格式是 json 格式
url 地址:​​ ​http://127.0.0.1:8000/login​​​请求方式:post 请求
请求参数:username,password

1
2
3
4
5
6
import requests 
url="http://127.0.0.1:8000/login"
#表单数据格式,参数 data ,数据都是字典去保存
data={"username":"liang001", "password":"123456" }
r_login=requests.post(url=url,json=data)
print (r_login.text)

如果要用参数data传参的话,在请求头要声明参数格式

1
2
3
4
5
6
7
import requests 
import json
url="http://127.0.0.1:8000/login"
header={'content-type':'application/json'}
data={"username":"liang001", "password":"123456" }
r_login=requests.post(url=url,data=json.dumps(data))
print (r_login.text)

定制头和cookie信息

在HTTP应用中Cookies和Header处理是非常普遍的事情,一般情况下用于记录用户的持久化信息和验证等功能,在运用requests库进行接口请求时,我们往往也会遇到需要带上请求头或者cookie的情况。

1
2
3
header = {'user-agent': 'my-app/0.0.1''}
cookie = {'key':'value'}
r = requests.get/post('your url',headers=header,cookies=cookie)

登录后复制

1
2
3
4
5
6
data = {'some': 'data'}
headers = {'content-type': 'application/json',
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0) Gecko/20100101 Firefox/22.0'}

r = requests.post('https://api.github.com/some/endpoint', data=data, headers=headers)
print(r.text)

响应状态码

使用requests方法后,会返回一个response对象,其存储了服务器响应的内容,如上实例中已经提到的 r.text、r.status_code……
获取文本方式的响应体实例:当你访问 r.text 之时,会使用其响应的文本编码进行自动解码,并且你可以修改其编码让 r.text 使用自定义的编码进行解码。

1
2
3
4
r = requests.get('http://www.itwhy.org')
print(r.text, '\n{}\n'.format('*'*79), r.encoding)
r.encoding = 'GBK'
print(r.text, '\n{}\n'.format('*'*79), r.encoding)

示例代码:

1
2
3
4
5
6
7
import requests

r = requests.get('https://github.com/Ranxf') # 最基本的不带参数的get请求
print(r.status_code) # 获取返回状态
r1 = requests.get(url='http://dict.baidu.com/s', params={'wd': 'python'}) # 带参数的get请求
print(r1.url)
print(r1.text) # 打印解码后的返回数据

运行结果:

1
2
3
4
5
6
/usr/bin/python3.5 /home/rxf/python3_1000/1000/python3_server/python3_requests/demo1.py
200
http://dict.baidu.com/s?wd=python
…………

Process finished with exit code

登录后复制

1
r.status_code                      #如果不是200,可以使用 r.raise_for_status() 抛出

响应

1
2
3
4
r.headers                                  #返回字典类型,头信息
r.requests.headers #返回发送到服务器的头信息
r.cookies #返回cookie
r.history #返回重定向信息,当然可以在请求时加上allow_redirects = false 阻止重定向

超时

1
r = requests.get('url',timeout=1)           #设置秒数超时,仅对于连接有效

会话对象,能够跨请求保持某些参数

1
2
3
4
5
s = requests.Session()  # cookies或者session或者token保存于session对象中
s.auth = ('auth','passwd')
s.headers = {'key':'value'}
r = s.get('url')
r1 = s.get('url1')

代理

登录后复制

1
2
proxies = {'http':'ip1','https':'ip2'}
requests.get('url',proxies=proxies) # 请求时,通过代理可设置抓包

https 请求处理

当协议是 https 的协议的时候,有些系统,需要校验 https 证书,如果校验失败,可能会有警告提示等信息

1
2
3
4
5
6
7
import requests 
url="https://www.ctrip.com/"
#解决方案 # 发送请求时候忽略证书,证书的参数 verify-用的比较多 r=requests.get(url=url,verify=False) #verify 参数默认为 True,值为 False 表示忽略证书
print (r.text)
#第二种解决方案,verify 里面添加证书的路径
r=requests.get(url=url,verify='证书的路径')
print (r.text)

文件上传

当需要到上传文件接口时候,我们需要 files 参数处理

1
2
3
4
5
6
7
import requests
url = 'https://127.0.0.1:8000/UploadPhoto'
#filename 为 liang.jpg 文件
#open('D:\\test_data\\liang.jpg','rb') 打开本地的一个文件
files = {'file':open('D:\\test_data\\liang.jpg','rb')}
r=requests.post(url=url,files=files,verify=False)
print (r.text)
1
2
3
4
5
6
7
8
9
10
#  上传文件(图片)
url="https://www.imooc.com/user/postpic"
file = {
"fileField":("fullimage.png",open("C:/Users/Admin/Pictures/Saved Pictures/fullimage.png","rb"),"image/png"),
"type":"1"
}
cookie = { "apsid":"U4ODM2MWI2MGZhZjk2ZGEyODEwODY2YWRmMzNjM2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTEzOTU1NzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADZkNDBhNWM5OTk2YzQ2MzJhMWJkMWU3NWFlZWZiZjdkDkmeZQAAAAA%3DYm"
}
res = requests.post(url,files=file,cookies=cookie,verify=False).json()
print(res)

文件下载

一次性下载

1
2
3
4
5
6
import requests

url = 'http://www.xxxx/demo.zip'
r = requests.get(url)
with open("filename.zip", "wb") as code:
code.write(r.content)

大文件下载:

如果文件比较大的话,那么下载下来的文件先放在内存中,内存还是比较有压力的。所以为了防止内存不够用的现象出现,我们要想办法把下载的文件分块写到磁盘中,需要用到request的参数stream

1
2
3
4
5
6
7
import requests
url = 'http://www.xxxx/demo.zip'
r = requests.get(url, stream=True) ##stream-true,限制文件的大小
with open("filename.zip", "wb") as code:
for chunk in r.iter_content(chunk_size=1024):#iter_content 循环去读取信息写入,chunk_size 文件大 小
if chunk:
code.write(chunk) #把循环读取的值,写入文件里面

timeout 参数

1、python 的requests请求都可以使用timeout参数。
2、timeout参数可以传入一个简单的浮点数,它将请求的连接部分和读取部分设为相同的超时时间。
3、timeout参数也可以传入一个包含两个简单浮点数的元组,用来分别设置请求超时时间和读取超时时间。
4、参数值设为None或者不设置该参数为一直等待,知道请求成功或错误

1
2
3
4
5
url = "http://127.0.0.1:8000/login"
# 表单数据格式,参数 data ,数据都是字典去保存
data = {"username": "", "password": "123456"}
r_login = requests.post(url=url, json=data, timeout=0.5)
print(r_login.text)

汇总:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# HTTP请求类型
# get类型
r = requests.get('https://github.com/timeline.json')
# post类型
r = requests.post("http://m.ctrip.com/post")
# put类型
r = requests.put("http://m.ctrip.com/put")
# delete类型
r = requests.delete("http://m.ctrip.com/delete")
# head类型
r = requests.head("http://m.ctrip.com/head")
# options类型
r = requests.options("http://m.ctrip.com/get")

# 获取响应内容
print(r.content) #以字节的方式去显示,中文显示为字符
print(r.text) #以文本的方式去显示

#URL传递参数
payload = {'keyword': '香港', 'salecityid': '2'}
r = requests.get("http://m.ctrip.com/webapp/tourvisa/visa_list", params=payload)
print(r.url) #示例为http://m.ctrip.com/webapp/tourvisa/visa_list?salecityid=2&keyword=香港

#获取/修改网页编码
r = requests.get('https://github.com/timeline.json')
print (r.encoding)

#json处理
r = requests.get('https://github.com/timeline.json')
print(r.json()) # 需要先import json

# 定制请求头
url = 'http://m.ctrip.com'
headers = {'User-Agent' : 'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 4 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19'}
r = requests.post(url, headers=headers)
print (r.request.headers)

#复杂post请求
url = 'http://m.ctrip.com'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload)) #如果传递的payload是string而不是dict,需要先调用dumps方法格式化一下

# post多部分编码文件
url = 'http://m.ctrip.com'
files = {'file': open('report.xls', 'rb')}
r = requests.post(url, files=files)

# 响应状态码
r = requests.get('http://m.ctrip.com')
print(r.status_code)

# 响应头
r = requests.get('http://m.ctrip.com')
print (r.headers)
print (r.headers['Content-Type'])
print (r.headers.get('content-type')) #访问响应头部分内容的两种方式

# Cookies
url = 'http://example.com/some/cookie/setting/url'
r = requests.get(url)
r.cookies['example_cookie_name'] #读取cookies

url = 'http://m.ctrip.com/cookies'
cookies = dict(cookies_are='working')
r = requests.get(url, cookies=cookies) #发送cookies

#设置超时时间
r = requests.get('http://m.ctrip.com', timeout=0.001)

#设置访问代理
proxies = {
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.100:4444",
}
r = requests.get('http://m.ctrip.com', proxies=proxies)


#如果代理需要用户名和密码,则需要这样:
proxies = {
"http": "http://user:pass@10.10.1.10:3128/",
}

GET请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1、无参数实例
import requests

ret = requests.get('https://github.com/timeline.json')
print(ret.url)
print(ret.text)

# 2、有参数实例
import requests

payload = {'key1': 'value1', 'key2': 'value2'}
ret = requests.get("http://httpbin.org/get", params=payload)
print(ret.url)
print(ret.text)

POST请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1、基本POST实例
import requests

payload = {'key1': 'value1', 'key2': 'value2'}
ret = requests.post("http://httpbin.org/post", data=payload)
print(ret.text)
# 2、发送请求头和数据实例
import requests
import json
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
headers = {'content-type': 'application/json'}
ret = requests.post(url, data=json.dumps(payload), headers=headers)

print(ret.text)
print(ret.cookies)

示例参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def param_method_url():
# requests.request(method='get', url='http://127.0.0.1:8000/test/')
# requests.request(method='post', url='http://127.0.0.1:8000/test/')
pass

def param_param():
# - 可以是字典
# - 可以是字符串
# - 可以是字节(ascii编码以内)
# requests.request(method='get',
# url='http://127.0.0.1:8000/test/',
# params={'k1': 'v1', 'k2': '水电费'})

# requests.request(method='get',
# url='http://127.0.0.1:8000/test/',
# params="k1=v1&k2=水电费&k3=v3&k3=vv3")

# requests.request(method='get',
# url='http://127.0.0.1:8000/test/',
# params=bytes("k1=v1&k2=k2&k3=v3&k3=vv3", encoding='utf8'))

# 错误
# requests.request(method='get',
# url='http://127.0.0.1:8000/test/',
# params=bytes("k1=v1&k2=水电费&k3=v3&k3=vv3", encoding='utf8'))
pass

def param_data():
# 可以是字典
# 可以是字符串
# 可以是字节
# 可以是文件对象

# requests.request(method='POST',
# url='http://127.0.0.1:8000/test/',
# data={'k1': 'v1', 'k2': '水电费'})

# requests.request(method='POST',
# url='http://127.0.0.1:8000/test/',
# data="k1=v1; k2=v2; k3=v3; k3=v4"
# )

# requests.request(method='POST',
# url='http://127.0.0.1:8000/test/',
# data="k1=v1;k2=v2;k3=v3;k3=v4",
# headers={'Content-Type': 'application/x-www-form-urlencoded'}
# )

# requests.request(method='POST',
# url='http://127.0.0.1:8000/test/',
# data=open('data_file.py', mode='r', encoding='utf-8'), # 文件内容是:k1=v1;k2=v2;k3=v3;k3=v4
# headers={'Content-Type': 'application/x-www-form-urlencoded'}
# )
pass

def param_json():
# 将json中对应的数据进行序列化成一个字符串,json.dumps(...)
# 然后发送到服务器端的body中,并且Content-Type是 {'Content-Type': 'application/json'}
requests.request(method='POST',
url='http://127.0.0.1:8000/test/',
json={'k1': 'v1', 'k2': '水电费'})


def param_headers():
# 发送请求头到服务器端
requests.request(method='POST',
url='http://127.0.0.1:8000/test/',
json={'k1': 'v1', 'k2': '水电费'},
headers={'Content-Type': 'application/x-www-form-urlencoded'}
)


def param_cookies():
# 发送Cookie到服务器端
requests.request(method='POST',
url='http://127.0.0.1:8000/test/',
data={'k1': 'v1', 'k2': 'v2'},
cookies={'cook1': 'value1'},
)
# 也可以使用CookieJar(字典形式就是在此基础上封装)
from http.cookiejar import CookieJar
from http.cookiejar importobj = CookieJar()
obj.set_cookie(Cookie(version=0, name='c1', value='v1', port=None, domain='', path='/', secure=False, expires=None,
discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False,
port_specified=False, domain_specified=False, domain_initial_dot=False, path_specified=False)
)='POST',
url='http://127.0.0.1:8000/test/',
data={'k1': 'v1', 'k2': 'v2'},
cookies=obj)

def param_files():
# 发送文件
# file_dict = {
# 'f1': open('readme', 'rb')
# }
# requests.request(method='POST',
# url='http://127.0.0.1:8000/test/',
# files=file_dict)

# 发送文件,定制文件名
# file_dict = {
# 'f1': ('test.txt', open('readme', 'rb'))
# }
# requests.request(method='POST',
# url='http://127.0.0.1:8000/test/',
# files=file_dict)

# 发送文件,定制文件名
# file_dict = {
# 'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf")
# }
# requests.request(method='POST',
# url='http://127.0.0.1:8000/test/',
# files=file_dict)

# 发送文件,定制文件名
# file_dict = {
# 'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf", 'application/text', {'k1': '0'})
# }
# requests.request(method='POST',
# url='http://127.0.0.1:8000/test/',
# files=file_dict)

pass

def param_auth():
from requests.auth import HTTPBasicAuth, HTTPDigestAuth

ret = requests.get('https://api.github.com/user', auth=HTTPBasicAuth('wupeiqi', 'sdfasdfasdf'))
print(ret.text)

# ret = requests.get('http://192.168.1.1',
# auth=HTTPBasicAuth('admin', 'admin'))
# ret.encoding = 'gbk'
# print(ret.text)

# ret = requests.get('http://httpbin.org/digest-auth/auth/user/pass', auth=HTTPDigestAuth('user', 'pass'))
# print(ret)
#

def param_timeout():
# ret = requests.get('http://google.com/', timeout=1)
# print(ret)

# ret = requests.get('http://google.com/', timeout=(5, 1))
# print(ret)
pass

def param_allow_redirects():
ret = requests.get('http://127.0.0.1:8000/test/', allow_redirects=False)
print(ret.text)

def param_proxies():
# proxies = {
# "http": "61.172.249.96:80",
# "https": "http://61.185.219.126:3128",
# }
# proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}
# ret = requests.get("http://www.proxy360.cn/Proxy", proxies=proxies)
# print(ret.headers)

# from requests.auth import HTTPProxyAuth
#
# proxyDict = {
# 'http': '77.75.105.165',
# 'https': '77.75.105.165'
# }
# auth = HTTPProxyAuth('username', 'mypassword')
#
# r = requests.get("http://www.google.com", proxies=proxyDict, auth=auth)
# print(r.text)

pass

def param_stream():
ret = requests.get('http://127.0.0.1:8000/test/', stream=True)
print(ret.content)
ret.close()

# from contextlib import closing
# with closing(requests.get('http://httpbin.org/get', stream=True)) as r:
# # 在此处理响应。
# for i in r.iter_content():
# print(i)

def requests_session():
import requests

session = requests.Session()

### 1、首先登陆任何页面,获取cookie

i1 = session.get(url="http://dig.chouti.com/help/service")

### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权
i2 = session.post(
url="http://dig.chouti.com/login",
data={
'phone': "8615131255089",
'password': "xxxxxx",
'oneMonth': ""
}
)
i3 = session.post(
url="http://dig.chouti.com/link/vote?linksId=8589623",
)
print(i3.text)

json请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#! /usr/bin/python3
import requests
import json

class url_request():
def __init__(self):
''' init '''

if __name__ == '__main__':
heard = {'Content-Type': 'application/json'}
payload = {'CountryName': '中国',
'ProvinceName': '四川省',
'L1CityName': 'chengdu',
'L2CityName': 'yibing',
'TownName': '',
'Longitude': '107.33393',
'Latitude': '33.157131',
'Language': 'CN'}
r = requests.post("http://www.xxxxxx.com/CityLocation/json/LBSLocateCity", heards=heard, data=payload)
data = r.json()
if r.status_code!=200:
print('LBSLocateCity API Error' + str(r.status_code))
print(data['CityEntities'][0]['CityID']) # 打印返回json中的某个key的value
print(data['ResponseStatus']['Ack'])
print(json.dump(data, indent=4, sort_keys=True, ensure_ascii=False)) # 树形打印json,ensure_ascii必须设为False否则中文会显示为unicode

Xml请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#! /usr/bin/python3
import requests

class url_request():
def __init__(self):
"""init"""

if __name__ == '__main__':
heards = {'Content-type': 'text/xml'}
XML = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Request xmlns="http://tempuri.org/"><jme><JobClassFullName>WeChatJSTicket.JobWS.Job.JobRefreshTicket,WeChatJSTicket.JobWS</JobClassFullName><Action>RUN</Action><Param>1</Param><HostIP>127.0.0.1</HostIP><JobInfo>1</JobInfo><NeedParallel>false</NeedParallel></jme></Request></soap:Body></soap:Envelope>'
url = 'http://jobws.push.mobile.xxxxxxxx.com/RefreshWeiXInTokenJob/RefreshService.asmx'
r = requests.post(url=url, heards=heards, data=XML)
data = r.text
print(data)

状态异常处理

1
2
3
4
5
6
7
8
9
10
11
import requests

URL = 'http://ip.taobao.com/service/getIpInfo.php' # 淘宝IP地址库API
try:
r = requests.get(URL, params={'ip': '8.8.8.8'}, timeout=1)
r.raise_for_status() # 如果响应状态码不是 200,就主动抛出异常
except requests.RequestException as e:
print(e)
else:
result = r.json()
print(type(result), result, sep='\n')

上传文件:

1
2
3
4
5
6
7
8
import requests

url = 'http://127.0.0.1:8080/upload'
files = {'file': open('/home/rxf/test.jpg', 'rb')}
#files = {'file': ('report.jpg', open('/home/lyb/sjzl.mpg', 'rb'))} #显式的设置文件名

r = requests.post(url, files=files)
print(r.text)

request更加方便的是,可以把字符串当作文件进行上传:

1
2
3
4
5
6
7
import requests

url = 'http://127.0.0.1:8080/upload'
files = {'file': ('test.txt', b'Hello Requests.')} #必需显式的设置文件名

r = requests.post(url, files=files)
print(r.text)

加密

用md5进行加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests
import json
import hashlib

imooc = "imooc.com"
# header 加密
md5 = hashlib.md5()
data = str({
'user': '11111'
})
md5.update(data.encode('utf-8'))
res1 = md5.hexdigest()
print("解析:",res1)
header = {
'Host': 'm.imooc.com',
'Connection': 'keep-alive',
'Pragma': 'no-cache',
'Cache-Control': 'no-cache',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'X-Requested-With': 'XMLHttpRequest',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X',
'Referer': 'https://m.imooc.com/',
'Accept-Language': 'zh-CN,zh;q=0.9',
'token': res1
}
url = 'https://m.imooc.com/api/search/searchword'
response = requests.get(url, headers=header, verify=False) # 请尽量避免禁用 SSL 验证
print(response) # 输出响应文本
try:
json_response = response.json()
print(json.dumps(json_response, indent=4, ensure_ascii=False))
except json.decoder.JSONDecodeError as e:
print(f"JSON 解码错误: {e}")

身份验证:

基本身份认证(HTTP Basic Auth):

1
2
3
4
5
6
import requests
from requests.auth import HTTPBasicAuth

r = requests.get('https://httpbin.org/hidden-basic-auth/user/passwd', auth=HTTPBasicAuth('user', 'passwd'))
# r = requests.get('https://httpbin.org/hidden-basic-auth/user/passwd', auth=('user', 'passwd')) # 简写
print(r.json())

另一种非常流行的HTTP身份认证形式是摘要式身份认证,Requests对它的支持也是开箱即可用的:

1
requests.get(URL, auth=HTTPDigestAuth('user', 'pass')

Cookies与会话对象:

如果某个响应中包含一些Cookie,你可以快速访问它们:

1
2
3
4
5
import requests

r = requests.get('http://www.google.com.hk/')
print(r.cookies['NID'])
print(tuple(r.cookies))

要想发送你的cookies到服务器,可以使用 cookies 参数:

1
2
3
4
5
6
7
import requests

url = 'http://httpbin.org/cookies'
cookies = {'testCookies_1': 'Hello_Python3', 'testCookies_2': 'Hello_Requests'}
# 在Cookie Version 0中规定空格、方括号、圆括号、等于号、逗号、双引号、斜杠、问号、@,冒号,分号等特殊符号都不能作为Cookie的内容。
r = requests.get(url, cookies=cookies)
print(r.json())

会话对象让你能够跨请求保持某些参数,最方便的是在同一个Session实例发出的所有请求之间保持cookies,且这些都是自动处理的,甚是方便。
下面就来一个真正的实例,如下是快盘签到脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests

headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate, compress',
'Accept-Language': 'en-us;q=0.5,en;q=0.3',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0) Gecko/20100101 Firefox/22.0'}

s = requests.Session()
s.headers.update(headers)
# s.auth = ('superuser', '123')
s.get('https://www.kuaipan.cn/account_login.htm')

_URL = 'http://www.kuaipan.cn/index.php'
s.post(_URL, params={'ac':'account', 'op':'login'},
data={'username':'****@foxmail.com', 'userpwd':'********', 'isajax':'yes'})
r = s.get(_URL, params={'ac':'zone', 'op':'taskdetail'})
print(r.json())
s.get(_URL, params={'ac':'common', 'op':'usersign'})

requests模块抓取网页源码并保存到文件示例:

这是一个基本的文件保存操作,但这里有几个值得注意的问题:

1.安装requests包,命令行输入pip install requests即可自动安装。很多人推荐使用requests,自带的urllib.request也可以抓取网页源码

2.open方法encoding参数设为utf-8,否则保存的文件会出现乱码。

3.如果直接在cmd中输出抓取的内容,会提示各种编码错误,所以保存到文件查看。

4.with open方法是更好的写法,可以自动操作完毕后释放资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#! /urs/bin/python3
import requests

'''requests模块抓取网页源码并保存到文件示例'''
html = requests.get("http://www.baidu.com")
with open('test.txt', 'w', encoding='utf-8') as f:
f.write(html.text)

'''读取一个txt文件,每次读取一行,并保存到另一个txt文件中的示例'''
ff = open('testt.txt', 'w', encoding='utf-8')
with open('test.txt', encoding="utf-8") as f:
for line in f:
ff.write(line)
ff.close()

因为在命令行中打印每次读取一行的数据,中文会出现编码错误,所以每次读取一行并保存到另一个文件,这样来测试读取是否正常。(注意open的时候制定encoding编码方式)

自动登陆

抽屉新热榜:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import requests

# ############## 方式一 ##############
"""
# ## 1、首先登陆任何页面,获取cookie
i1 = requests.get(url="http://dig.chouti.com/help/service")
i1_cookies = i1.cookies.get_dict()

# ## 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权
i2 = requests.post(
url="http://dig.chouti.com/login",
data={
'phone': "8615131255089",
'password': "xxooxxoo",
'oneMonth': ""
},
cookies=i1_cookies
)

# ## 3、点赞(只需要携带已经被授权的gpsd即可)
gpsd = i1_cookies['gpsd']
i3 = requests.post(
url="http://dig.chouti.com/link/vote?linksId=8589523",
cookies={'gpsd': gpsd}
)

print(i3.text)
"""
# ############## 方式二 ##############
"""
import requests

session = requests.Session()
i1 = session.get(url="http://dig.chouti.com/help/service")
i2 = session.post(
url="http://dig.chouti.com/login",
data={
'phone': "8615131255089",
'password': "xxooxxoo",
'oneMonth': ""
}
)
i3 = session.post(
url="http://dig.chouti.com/link/vote?linksId=8589523"
)
print(i3.text)

"""

github:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import requests
from bs4 import BeautifulSoup

# ############## 方式一 ##############
#
# # 1. 访问登陆页面,获取 authenticity_token
# i1 = requests.get('https://github.com/login')
# soup1 = BeautifulSoup(i1.text, features='lxml')
# tag = soup1.find(name='input', attrs={'name': 'authenticity_token'})
# authenticity_token = tag.get('value')
# c1 = i1.cookies.get_dict()
# i1.close()
#
# # 1. 携带authenticity_token和用户名密码等信息,发送用户验证
# form_data = {
# "authenticity_token": authenticity_token,
# "utf8": "",
# "commit": "Sign in",
# "login": "wupeiqi@live.com",
# 'password': 'xxoo'
# }
#
# i2 = requests.post('https://github.com/session', data=form_data, cookies=c1)
# c2 = i2.cookies.get_dict()
# c1.update(c2)
# i3 = requests.get('https://github.com/settings/repositories', cookies=c1)
#
# soup3 = BeautifulSoup(i3.text, features='lxml')
# list_group = soup3.find(name='div', class_='listgroup')
#
# from bs4.element import Tag
#
# for child in list_group.children:
# if isinstance(child, Tag):
# project_tag = child.find(name='a', class_='mr-1')
# size_tag = child.find(name='small')
# temp = "项目:%s(%s); 项目路径:%s" % (project_tag.get('href'), size_tag.string, project_tag.string, )
# print(temp)

# ############## 方式二 ##############
# session = requests.Session()
# # 1. 访问登陆页面,获取 authenticity_token
# i1 = session.get('https://github.com/login')
# soup1 = BeautifulSoup(i1.text, features='lxml')
# tag = soup1.find(name='input', attrs={'name': 'authenticity_token'})
# authenticity_token = tag.get('value')
# c1 = i1.cookies.get_dict()
# i1.close()
#
# # 1. 携带authenticity_token和用户名密码等信息,发送用户验证
# form_data = {
# "authenticity_token": authenticity_token,
# "utf8": "",
# "commit": "Sign in",
# "login": "wupeiqi@live.com",
# 'password': 'xxoo'
# }
#
# i2 = session.post('https://github.com/session', data=form_data)
# c2 = i2.cookies.get_dict()
# c1.update(c2)
# i3 = session.get('https://github.com/settings/repositories')
#
# soup3 = BeautifulSoup(i3.text, features='lxml')
# list_group = soup3.find(name='div', class_='listgroup')
#
# from bs4.element import Tag
#
# for child in list_group.children:
# if isinstance(child, Tag):
# project_tag = child.find(name='a', class_='mr-1')
# size_tag = child.find(name='small')
# temp = "项目:%s(%s); 项目路径:%s" % (project_tag.get('href'), size_tag.string, project_tag.string, )
# print(temp)

知乎:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time

import requests
from bs4 import BeautifulSoup

session = requests.Session()

i1 = session.get(
url='https://www.zhihu.com/#signin',
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36',
}
)

soup1 = BeautifulSoup(i1.text, 'lxml')
xsrf_tag = soup1.find(name='input', attrs={'name': '_xsrf'})
xsrf = xsrf_tag.get('value')

current_time = time.time()
i2 = session.get(
url='https://www.zhihu.com/captcha.gif',
params={'r': current_time, 'type': 'login'},
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36',
})

with open('zhihu.gif', 'wb') as f:
f.write(i2.content)

captcha = input('请打开zhihu.gif文件,查看并输入验证码:')
form_data = {
"_xsrf": xsrf,
'password': 'xxooxxoo',
"captcha": 'captcha',
'email': '424662508@qq.com'
}
i3 = session.post(
url='https://www.zhihu.com/login/email',
data=form_data,
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36',
}
)

i4 = session.get(
url='https://www.zhihu.com/settings/profile',
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36',
}
)

soup4 = BeautifulSoup(i4.text, 'lxml')
tag = soup4.find(id='rename-section')
nick_name = tag.find('span',class_='name').string
print(nick_name)

博客园:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
import json
import base64

import rsa
import requests


def js_encrypt(text):
b64der = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp0wHYbg/NOPO3nzMD3dndwS0MccuMeXCHgVlGOoYyFwLdS24Im2e7YyhB0wrUsyYf0/nhzCzBK8ZC9eCWqd0aHbdgOQT6CuFQBMjbyGYvlVYU2ZP7kG9Ft6YV6oc9ambuO7nPZh+bvXH0zDKfi02prknrScAKC0XhadTHT3Al0QIDAQAB'
der = base64.standard_b64decode(b64der)

pk = rsa.PublicKey.load_pkcs1_openssl_der(der)
v1 = rsa.encrypt(bytes(text, 'utf8'), pk)
value = base64.encodebytes(v1).replace(b'\n', b'')
value = value.decode('utf8')

return value


session = requests.Session()

i1 = session.get('https://passport.s.com/user/signin')
rep = re.compile("'VerificationToken': '(.*)'")
v = re.search(rep, i1.text)
verification_token = v.group(1)

form_data = {
'input1': js_encrypt('wptawy'),
'input2': js_encrypt('asdfasdf'),
'remember': False
}

i2 = session.post(url='https://passport.s.com/user/signin',
data=json.dumps(form_data),
headers={
'Content-Type': 'application/json; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
'VerificationToken': verification_token}
)

i3 = session.get(url='https://i.s.com/EditDiary.aspx')

print(i3.text)

拉勾网:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import requests


# 第一步:访问登陆页,拿到X_Anti_Forge_Token,X_Anti_Forge_Code
# 1、请求url:https://passport.lagou.com/login/login.html
# 2、请求方法:GET
# 3、请求头:
# User-agent
r1 = requests.get('https://passport.lagou.com/login/login.html',
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
},
)

X_Anti_Forge_Token = re.findall("X_Anti_Forge_Token = '(.*?)'", r1.text, re.S)[0]
X_Anti_Forge_Code = re.findall("X_Anti_Forge_Code = '(.*?)'", r1.text, re.S)[0]
print(X_Anti_Forge_Token, X_Anti_Forge_Code)
# print(r1.cookies.get_dict())
# 第二步:登陆
# 1、请求url:https://passport.lagou.com/login/login.json
# 2、请求方法:POST
# 3、请求头:
# cookie
# User-agent
# Referer:https://passport.lagou.com/login/login.html
# X-Anit-Forge-Code:53165984
# X-Anit-Forge-Token:3b6a2f62-80f0-428b-8efb-ef72fc100d78
# X-Requested-With:XMLHttpRequest
# 4、请求体:
# isValidate:true
# username:15131252215
# password:ab18d270d7126ea65915c50288c22c0d
# request_form_verifyCode:''
# submit:''
r2 = requests.post(
'https://passport.lagou.com/login/login.json',
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
'Referer': 'https://passport.lagou.com/login/login.html',
'X-Anit-Forge-Code': X_Anti_Forge_Code,
'X-Anit-Forge-Token': X_Anti_Forge_Token,
'X-Requested-With': 'XMLHttpRequest'
},
data={
"isValidate": True,
'username': '15131255089',
'password': 'ab18d270d7126ea65915c50288c22c0d',
'request_form_verifyCode': '',
'submit': ''
},
cookies=r1.cookies.get_dict()
)
print(r2.text)

Flask

Flask是一个轻量级的Web应用框架,基于Python编写,其核心思想是保持简洁,灵活性和易于扩展。Flask提供了基本的Web开发工具和应用程序的结构,但同时也允许开发者自由选择其他库和工具来扩展其功能。Flask的设计目标是使开发者能够快速构建具有基本功能的Web应用程序。在日常生活中我们需要将模型打包为api接口,这里需要借助一些web框架,最常用的就是flask框架。

作为测试人员,在工作或者学习的过程中,有时会遇到没有可以调用的现成的接口,导致我们的代码没法调试跑通的情况。

这时,我们使用python中的web框架Flask就可以很方便的编写简单的接口,用于调用或调试。在之前的pytest系列文章中,已经使用过Flask编写接口用于代码调试。相比于python的另一个web框架Django,Flask编写接口要方便简单很多。

下面是一些Flask框架的主要特点:

  • 轻量级:Flask是一个轻量级的框架,其代码库非常小,并且不需要依赖大量的外部库和工具,因此可以轻松地安装和部署。
  • 灵活性:Flask允许开发者自由选择其他库和工具来扩展其功能,这使得开发者可以根据自己的需求进行灵活的配置。
  • 易于扩展:Flask提供了简单易用的扩展接口,开发者可以使用这些接口来添加新功能或定制框架的行为。
  • Web服务器支持:Flask支持多种Web服务器,如内置的开发服务器、Gunicorn和uWSGI等。
  • RESTful支持:Flask提供了内置的RESTful路由,使得开发RESTful API变得非常容易。
  • Jinja2模板引擎:Flask集成了Jinja2模板引擎,开发者可以使用模板来构建灵活的Web应用程序。
  • Flask-WTF表单:Flask提供了Flask-WTF扩展,使得表单处理变得简单而直观。
  • 内置的调试器:Flask提供了内置的调试器,使得开发者可以轻松地调试和排除错误。
  • 序列化与反序列化:Flask提供了内置的序列化和反序列化功能,使得开发RESTful API变得更加容易。

总的来说,Flask是一个非常适合快速构建小型Web应用程序的框架。它提供了基本的Web开发工具和应用程序的结构,同时也支持灵活的扩展和定制。如果您需要快速构建一个小型的Web应用程序,Flask是一个非常好的选择。

安装

1
2
pip install flask
验证是否安装成功或查看版本命令:pip show flask

get请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from flask import Flask
from flask import request
import json
app = Flask(__name__)
@app.route('/')
def Home():
data = json.dumps({
'username': "Hello World",
'password': "123456",
})
return data
@app.route('/passport/user/login', methods=['GET'])
def Login():
username = request.args.get("username")
password = request.args.get("password")
if username and password:
data = json.dumps({
'username': username,
'password': password,
'code':"200",
'message':'Hello World!',
'info':'www.baidu.com'
})
else:
data = json.dumps({
"message":"username or password ???"
})
return data
if __name__ == '__main__':
app.run()

post请求

以下是使用 Flask 框架封装接口的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from flask import Flask, request, jsonify

app = Flask(__name__)

# methods: 指定请求方式
@app.route('/process', methods=['POST'])
def process_data():
# 请求方式为post时,可以使用 request.get_json()接收到JSON数据
data = request.get_json() # 获取 POST 请求中的 JSON 数据

# 处理数据
# 调用do_something_with_data函数来处理接收到的数据。
processed_data = do_something_with_data(data)

# 请求方得到处理后的数据
return jsonify(processed_data)

if __name__ == '__main__':
app.run()

代码改进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 添加异常检测代码
from flask import Flask, request, jsonify

app = Flask(__name__)

# methods: 指定请求方式
@app.route('/process', methods=['POST'])
def process_data():
# 请求方式为post时,可以使用 request.get_json()接收到JSON数据
try:
data = request.get_json()
# 如果得到的data是字符串格式,则需要用json.loads来变换成python格式,看个人需求
# data = json.loads(data)
print(data)# 获取 POST 请求中的 JSON 数据
except Exception as e:
return jsonify({'error': '请求数据失败'}), 400

# 处理数据
# 调用do_something_with_data函数来处理接收到的数据。
# 判断是否接收到数据
if data:
try:
processed_data= do_something_with_data(data)
except Exception as e:
return jsonify({'error': '处理数据失败'}), 666
# 返回的数据格式看请求方的要求了,也有可能需要json处理后的数据,即jsonify(processed_data)
return str(processed_data)

if __name__ == '__main__':
app.run()

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from flask import Flask
from flask import request
import json
app = Flask(__name__)
@app.route('/')
def Home():
data = json.dumps({
'username': "Hello World",
'password': "123456",
'message': 'Hello World!',
'info': 'www.baidu.com'
})
return data
@app.route('/passport/user/login', methods=['GET', 'POST'])
def Login():
request_method = request.method
if request_method == "POST":
username = request.form.get('username')
password = request.form.get('password')
data = json.dumps({
'username': username,
'password': password,
'code':"200",
'message':'Hello World!',
'info':'www.baidu.com'
})
else:
data = json.dumps({
"message":"username or password ???"
})
return data
if __name__ == '__main__':
app.run()

Mock

Moco框架

Moco框架是Mock概念中的一种实现。
Moco框架是一个简单搭建模拟服务器的程序库/工具,这个基于 Java 开发的开源项目。
Moco框架已经在 Github 上获得了不少的关注,该项目的简介是这样描述自己的:

  • Moco 是一个简单搭建 stub 的框架,主要用于测试和集成。
  • 这个框架的开发灵感来自 Mock 框架,如 MockitoPlayframework

为什么要开发这个框架?

  • 用于基于HTTP协议的集成:web serviceREST等,在我们的项目开发中被广泛应用。
  • 以前,我们每次都要往JettyTomcat等应用服务器上部署一个新的 WAR。
  • 大家都知道,开发部署一个 WAR 的过程是很枯燥的,即使在嵌入式服务器上也是如此。而且,每次我们做一点改动,整个 WAR 都要重新组装。
  • Moco框架的出现,正是为了解决这些问题。开发团队只要根据自己的需要进行相应的配置,就会很方便得到一个模拟服务器。
  • 而且,由于 Moco框架本身的灵活性,其用途已经不再局限于最初的集成测试。
    比如:
    Moco 可以用于移动开发;
    模拟尚未开发的服务;
    Moco 还可以用于前端开发,模拟一个完整的 Web 服务器等等。

作用

一般接口文档编写完成后,测试人员需要提前进行接口测试用例的编写,而这时接口开发工作可能还没完成。
如果要等到开发人员完成接口的开发,再进行测试用例的编写,这样会降低测试开发的效率。
这时如果测试人员使用Moco框架搭建一个模拟服务器,就可以根据接口文档,自己模拟出接口的调用并返回结果。
在用例设计完成后,即使接口开发工作还未完成,也可以立即进行接口测试用例的执行。并且在这个过程中可以修改、补充测试用例。
在接口开发完成以后,只需要简单的切换服务器地址,就可以测试所有的开发人员实现的接口了。这样可以节省很多的测试时间,减少项目开发的周期。
Moco框架支持HTTP协议,采用热部署,修改配置后,立刻生效,无需重启服务。
Moco框架接口测试中的作用:方法隔离,服务解耦。即可并行又可驱动。

Moco框架的启动

  1. 条件:
    因为Moco框架工具是一个jar包,所以需要Java环境才能启动。
    即:配置好Java环境。
  2. 启动:
    把Moco框架Jar包和Json文件放入同一个文件夹中,
    命令行终端里进入到Json文件所在的目录,执行启用命令,如下:
    java -jar ./moco-runner-0.11.0-standalone.jar http -p 12306 -c foo.json即可。
    说明:
    ./moco-runner-0.11.0-standalone.jar为Moco工具所在目录。
    http:表示服务器协议,server type: http, https, socket
    -p:表示端口号。
    -c:表示一个json文件。接口所有的信息都配置在该json文件中。

提示:Moco工具路径和json文件路径都可以写相对路径和绝对路径。

当需要调用接口来编写测试用例的时候,此时该接口并没有被实现,这个时候我们就可以用Mock框架来模拟一个接口出来。
使用Mock模拟接口以下功能:

  • 拦截服务:httphttps
  • 请求方式:GET,POST。
  • 模拟请求地址:URL。
  • 模拟参数:包括headercookie的数据。
  • 模拟响应结果。
  • 支持重定向。

Json文件的配置属性说明:
像我们上面练习过的Json文件配置,所有的数据值是固定的,
如:descriptionrequestresponseredirectTo等这些都是固定的,不能修改,修改可能连Moco服务都启动不来。
还有request的属性值,如:urimethodcookiesheaders,也是必须这样写的。
还有GET请求传递参数用queries属性,POST请求传递参数用formsjson属性都可以。(PUT,DELETE请求同Post请求。)
Moco框架原理:
就是把所有接口的数据,包括发送请求的所有数据和返回结果的所有数据,以Json数据格式进行编写。
把这些数据放入Moco框架提供的HTTP或者HTTPS的服务上,就实现了接口数据的模拟。
在使用的时候,我们只要按照json文件中接口配置的信息进行请求即可,如果调用接口传递的数据和Json文件中接口编写要接收的数据不一致,则无法请求成功。

(1)Moco框架第一个练习
编写一个Json文件,接口所有的信息都配置在该json文件中。

1
2
3
4
5
6
7
8
9
10
11
[
{
"description": "第一个Moco框架例子。", # 描述:增加接口的可读性
"request": {
"uri": "/api/moco/demo",
},
"response": {
"text": "hello Moco !"
}
}
]

在cmd命令行或者PyCharm的命令行终端执行启动命令。

  • 进入json文件的所在目录。
  • 执行命令:java -jar ./moco-runner-0.12.0-standalone.jar http -p 12306 -c test.json

Moco服务启动后,我们可以使用Requests库请求接口,也可以用浏览器接口。

1
2
3
4
5
6
7
8
9
# 1.导入requests库
import requests

# 2.明确请求地址
url = "http://127.0.0.1:12306/api/moco/demo"

# 3.发送请求
response = requests.get(url=url)
print(response.text)

搭建mock服务端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import random
impor
from flask import Flask,request,json
#实例化一个web服务对象
app=Flask(__name__)
#创建一个方法来处理请求
#定义一个路由--访问服务的根目录就可以得到结果
@app.route('/')
def hello():
return '<h1>hello flask</h1>'
#构造一个接受post请求的响应
@app.route('/post',methods=['POST'])
def test_post():
#处理接口发送过来的两个参数,将两个参数合并成一个字符串返回
d1=request.form['d1']
d2=request.form['d2']
return d1+d2
if __name__ == '__main__':
#运行服务,并确定服务运行的IP和端口
app.run('127.0.0.1','9090')

客户端代码:

import requests

1
2
3
4
5
6
body={
'd1':'hi',
'd2':'falsk12312312'
}
resp=requests.post('http://127.0.0.1:9090/post',data=body)
print(resp.text)

get请求

没有参数的get请求

1
2
3
4
5
6
7
8
9
10
11
12
[
{
"description": "模拟一个没有参数的get请求。",
"request": {
"uri": "/api/moco/get/demo",
"method": "get" # 这里添加了要给method属性
},
"response": {
"text": "hello get request !"
}
}
]

有参数的get请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[
{
"description": "模拟一个没有参数的get请求。",
"request": {
"uri": "/api/moco/get/demo",
"method": "get"
},
"response": {
"text": "hello get request !"
}
},
{
"description": "模拟一个带参数的get请求。",
"request": {
"uri": "/api/moco/get/param/demo",
"method": "get",
"queries": { # get请求参数的选项,queries固定属性。
"name": "xiaoming",
"age": "18"
}
},
"response": {
"text": "hello xiaoming !"
}
}
]

说明:请求地址为:http://127.0.0.1:12306/api/moco/get/param/demo?name=xiaoming&age=18

post请求

没有参数的post请求

1
2
3
4
5
6
7
8
9
10
11
12
[
{
"description": "模拟一个不带数据的post请求。",
"request": {
"uri": "/api/moco/post/demo",
"method": "post"
},
"response": {
"text": "hello post request !"
}
}
]

提示:POST请求就不能用浏览器进行查看了。只能用Request库或者JMeter,Postman等进行查看。(能进行接口调用的工具都可以)

1
2
3
4
5
6
7
8
9
# 1.导入requests库
import requests

# 2.明确请求地址
url = "http://127.0.0.1:12306/api/moco/post/demo"

# 3.发送请求
response = requests.post(url=url)
print(response.text)

有参数的post请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[
{
"description": "模拟一个带数据post请求。",
"request": {
"uri": "/api/moco/post/param/demo",
"method": "post",
"forms": { # post请求带参数,参数要添加到forms属性中。
"name": "xiaoming",
"age": "18"
}
},
"response": {
"text": "hello post xiaoming !"
}
}
]

调用接口查看结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.导入requests库
import requests

# 2.明确请求地址
url = "http://127.0.0.1:12306/api/moco/post/param/demo"

data = {
"name": "xiaoming",
"age": "18"
}

# 3.发送请求
response = requests.post(url=url, data=data)
print(response.text)

Cookies

使用的是request中的cookies属性。

get请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[
{
"description": "模拟一个带cookie的get请求。",
"request": {
"uri": "/api/moco/get/cookies/demo",
"method": "get",
"cookies": { # 这里添加cookies参数
"login": "true"
}
},
"response": {
"text": "hello get cookies !"
}
}
]

调用接口查看结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1.导入requests库
import requests

# 2.明确请求地址
url = "http://127.0.0.1:12306/api/moco/get/cookies/demo"

cookies = {
"login": "true"
}

# 3.发送请求
response = requests.get(url=url, cookies=cookies)
print(response.text)

post请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
{
"description": "模拟一个带cookie的post请求。",
"request": {
"uri": "/api/moco/post/cookies/demo",
"method": "post",
"cookies": {
"login": "true"
},
"json": { # post请求的参数也可以用json格式的数据进行传输
"name": "xiaoming",
"age": "18"
}
},
"response": {
"status": 201,
"json": {
"text": "hello post cookies !"
}
}
}
]

调用接口查看结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.导入requests库
import requests

# 2.明确请求地址
url = "http://127.0.0.1:12306/api/moco/post/cookies/demo"

cookies = {
"login": "true"
}

json = {
"name": "xiaoming",
"age": "18"
}

# 3.发送请求
response = requests.post(url=url, json=json ,cookies=cookies)
print(response.text)

使用的是request中的headers属性。
Header是添加请求头信息,关于请求头信息get请求和post请求都是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
{
"description": "模拟一个带Header的post请求。",
"request": {
"uri": "/api/moco/post/headers/demo",
"method": "post",
"headers": { # 添加请求头信息
"content-type": "application/json"
},
"json": {
"name": "xiaoming",
"age": "18"
}
},
"response": {
"status": 201,
"json": {
"text": "hello get Headers !"
}
}
}
]

调用接口查看结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.导入requests库
import requests

# 2.明确请求地址
url = "http://127.0.0.1:12306/api/moco/post/headers/demo"

headers = {
"content-type": "application/json"
}

json = {
"name": "xiaoming",
"age": "18"
}

# 3.发送请求
response = requests.post(url=url, json=json, headers=headers)
print(response.text)

Moco模拟重定向

重定向使用的是和request同级的redirectTo属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[
{
"description": "重定向到百度",
"request": {
"uri": "/api/moco/redirect/demo",
"method": "get"
},
"redirectTo": "http://www.baidu.com"
},
{
"description": "重定向到自己的接口",
"request": {
"uri": "/api/moco/redirect/new/demo",
"method": "get"
},
"redirectTo": "http://www.baidu.com",
"response": {
"text": "hello redirectTo !"
}
}
]

读取文件

1.python使用自带的configparser模块用来读取配置文件,配置文件可以为.conf或.ini结尾

在使用前需要先安装该模块,使用pip安装即可

2.新建一个名为a.conf的配置文件

a) 配置文件中包含一个或多个 section, 每个 section 有自己的 option;

b) section 用 [sect_name] 表示,每个option是一个键值对,使用分隔符 = 或 : 隔开;

c) 在 option 分隔符两端的空格会被忽略掉

d) 配置文件使用 # 和 ; 注释

下面给出一个配置文件的例子。

1
2
3
4
5
# 文件路径的配置
[file path] # 区域名,即section
# 测试用例excel文件的路径
cases_path = D:\\cases.xlsx # 选项 option
log_path = result.txt[excel]actual_col = 6result_col = 7

3.导入ConfigParser模块,创建配置解析器对象并读取配置文件名

from configparser import ConfigParser

1
2
3
4
# 1.创建配置解析器对象
config = ConfigParser()
# 2.读取配置文件名
config.read('a.conf', encoding='utf-8')

4.读操作

1
2
3
4
5
6
7
# 方法一,第一个参数为区域名,第二个参数为选项名
cases_path = config['file path']['cases_path']
# 方法二
cases_path = config.get('file path', 'cases_path')
# 方法三# 通过getint方法得到的结果值是int类型
# 同样也有getboolean,getfloat方法
actual_col = config.getint('excel', 'actual_col')

注意:

# 使用getboolean去读取值,只能读取如下的数据,如果是非0和1,也会报错
# 1 yes on true True得到的结果为True
# 0 no off false False得到的结果为False

5.写操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from configparser import ConfigParser
# 1.创建配置解析器对象
config = ConfigParsr()
# 2.将需要写入配置文件中的数据组合
# 方法一
config['file path'] = {'cases_path': 'D:\\cases.xlsx', 'log_path': 'result.txt'}
config['msg'] = {'success_result': 'Pass', 'fail_result': 'Fail'}
# 方法二
# 可以使用嵌套字典的字典来创建配置文件
data = {
'file path': {'cases_path': 'D:\\cases.xlsx', 'log_path': 'result.txt'},
'msg': {'success_result': 'Pass', 'fail_result': 'Fail'}
}
for key in data:
config[key] = data[key]
# 3.保存到文件
with open('b.conf', 'w') as file:
config.write

excel

安装

1
pip install openpyxl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# coding=utf8
import openpyxl
import os
class HandleExcel():
def load_excel(self):
# 拼接文件路径
file_path = os.path.join("..", "ReadFile", "read_test.xlsx")
# 加载 Excel 文件
open_excel = openpyxl.load_workbook(file_path)
return open_excel

def get_sheet_data(self, index=None):
sheetnames = self.load_excel().sheetnames
if index is None:
index = 0
data = self.load_excel()[sheetnames[index]]
return data
def get_all_values(self):
sheet_data = self.get_sheet_data()
all_values = []
for row in sheet_data.iter_rows(values_only=True):
all_values.append(list(row))
# json格式
# for row in all_values:
# print(row)
return all_values
def get_cell_value(self, row, cols):
# 获取指定单元格的值
data = self.get_sheet_data().cell(row=row, column=cols).value
return data
def get_rows(self):
# 获取行数
row = self.get_sheet_data().max_row
return row
def get_rows_value(self, row):
row_list = []
for i in self.get_sheet_data()[row]:
# 将每个单元格的值添加到列表中
row_list.append(i.value)
return row_list
excel_data = HandleExcel()
if __name__ == '__main__':
handle = HandleExcel()
print("全部获取",handle.get_all_values())
# 输出第三行的数据
print("获取值",handle.get_rows_value(3))

ini

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import os
import configparser
class HandleIni():
def load_ini(self):
# 加载 server.ini 文件
file_path = os.path.join("..", "ReadFile", "server.ini")
cf = configparser.ConfigParser()
cf.read(file_path, encoding='utf-8-sig')
return cf
def get_all_values(self):
cf = self.load_ini()
all_values = {}
for section in cf.sections():
all_values[section] = dict(cf[section])
return all_values

def print_all_values(self):
all_values = self.get_all_values()
for section, values in all_values.items():
print(f"[{section}]")
for key, value in values.items():
print(f"{key} = {value}")

def get_value(self, key, node=None):
if node == None:
node = 'server'
cf = self.load_ini()
try:
# 获取指定节点和键对应的值
data = cf.get(node, key)
except Exception:
print("没有获取到值")
data = None
return data
handle_ini = HandleIni()
if __name__ == '__main__':
hit = HandleIni()
print("获取全部",hit.print_all_values())
print("获取值",hit.get_value("host"))

json

读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# coding=utf-8
import os
import sys
import json
base_path = os.getcwd()
sys.path.append(base_path)
def handle_json(file_name=None):
# 检查 file_name 是否为 None,如果是,则使用默认文件路径
if file_name is None:
file_path = os.path.join("..", "ReadFile", "code_json.json")
else:
file_path = os.path.join(base_path, file_name)
# 打开 JSON 文件并加载数据
with open(file_path, encoding='UTF-8') as f:
data = json.load(f)
return data
def json_value(key, file_name=None):
# 获取 JSON 数据
data = handle_json(file_name)
# 返回指定键对应的值
return data.get(key)
if __name__ == '__main__':
# 示例用法
print("全部读取:",handle_json())
print("读取值:",json_value("api3/getbanneradvertver2"))

写入文件

json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import os
import sys
import json
base_path = os.getcwd()
sys.path.append(base_path)

def write_value(data, cookie_key):
# 构建文件路径
file_path = os.path.join("..", "ReadFile", "cookie.json")
# 以只读模式打开文件,并读取已有数据
with open(file_path, "r") as f:
existing_data = json.load(f)
# 将给定数据添加到已有数据中
existing_data[cookie_key] = data
# 以写入模式打开文件,并将更新后的数据写回文件
with open(file_path, "w") as f:
json.dump(existing_data, f, indent=4)

if __name__ == '__main__':
data = {
"jee": "123456789"
}
write_value(data, '123')
print("写入完成")