[2012.7.20]易语言post教程 - 飞龙

预备知识:熟悉易语言的常用指令。(了解api,OOP,并且会使用常用控件。)

注:此教程是本人学会易语言post两个月后写完的,本人非大神,所以各位也不要指望看完这篇教程就马上成为大神

【封包】

首先你要有一个概念,你的电脑是客户端,百度服务器是服务端。客户端与服务端想要通信,必须发送数据,这种数据叫做封包。

为什么叫封包呢?这好比你把一个东西打包裹邮寄给另外一个人,很形象吧。

【http协议】

http协议(超文本传输协议)是建立在tcp/ip协议之上的一种数据传输协议。它是一种规范,说白了就是一种格式,不按照这种格式写的封包不能被服务器识别。

一个简单的封包,取百度搜索主页文本:


GET / HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; NP06; .NET4.0C; .NET4.0E; InfoPath.2)
Host: [url=http://www.baidu.com]www.baidu.com[/url]
Connection: Keep-Alive
Cache-Control: no-cache

第一部分是请求行(request line) 
又分为三部分:
方法 子地址 协议版本 

方法:常用的只有post 或get。 一般来说,取网页文本的都是get,而发送信息的都是post,不过也有例外。所以按照抓过来的包如实填写即可。
子地址:主机后面的地址,比如http://tieba.baidu.com/f?kw=wow 的子地址就是/f?kw=wow。如果是http://tieba.baidu.com,则子地址为/。
协议版本:目前是1.1。

第二部分为请求头(request header)
对于服务器给你传的封包,这部分叫做响应头(response header)
各请求头的说明可以查百科,这里只介绍常见的几个。
Accept表示可接受的数据类型,"*/*"表示所有类型,一般来说这个就够。
Accept-Language表示接受语言,一般来说是中文的,国外服务器一般是英文的。
User-Agent,简称UA,标识你用的是什么浏览器,不同浏览器会有很大的不同,当然对于同一平台上的你可以随便。
Host即主机,就是说http标头和第一个/之前的文本
Connection,连接状态,一般为Keep-Alive。
Cookie估计是最重要的东西了。如果你在登录论坛或贴吧时选自动保存,那么下一次再上的话就会直接读取保存的信息来判断登录状态,这个信息就叫cookie。

如果是post,还可能有几个另外的标头
Cache-Control,cache是缓存,这个标头表示要不要存缓存,一般来说不用,值为"no-cache"。
Content-Type,数据类型,一般是网页填表,即。
Content-Length,数据长度,这个真心没什么好讲的,对于svxml这项可以不写。

第三部分 数据(可选)
为什么数据是可选的呢?因为get方法不用提交数据。post提交的数据在封包的最后面,与最后一个请求头之间隔一个空行。

【抓包】

对于具体的一个封包操作,我们不能闭门造车,要抓包看看服务器和客户端之间具体进行了什么通讯。
对于浏览器抓包,我和其他人一样推荐httpwatch,抓软件的推荐wareshark,下载地址百度一下就有。
安装好插件后,打开IE,从“工具”工具栏的httpwatch项把它点出来。

它会附到IE的底部。左上角三个按钮最重要,第一个是开始记录,第二个是停止记录,第三个是清空内容。
点开始后会变成记录状态,这时我们进行的每一个操作都会被记录。这张图是我记录访问百度主页时的截图。当然,会有很多无用信息,比如加载图片什么的,无视就好。
点开我们要想的记录,在下面就可以看到数据流,包括响应头和请求头。左下角的框里面就是我们需要的。

【发送封包】

当你在编辑栏输入“www.baidu.com”后按回车,就会载入百度搜索的页面。这个过程中你看似什么都没有做,实际上是浏览器帮你做的,它向百度某个主机发送了一个请求,服务器返回你想要看的网页html源码,经浏览器翻译后就呈现来屏幕上了。

但是你做的东西不能像浏览器这样自动,你就必须自己连接服务器,编写封包,并控制流程发送。

不同的语言有不同的封装好的类,但由于易语言自身的支持库不足以满足我们的需求,我们要直接调用API,或者COM(公共对象组件)。常用的有wininet(API), svxml(COM) ,winhttp(API或COM)。其中svxml最好用,因为它是模拟浏览器的对象,而且不留缓存。

(代码用易语言编写,其他语言请举一反三。)

第一步

要调用COM,首先要初始化。初始化就是调用一个API,声明如下:

.DLL命令 加载COM, , "ole32.dll", "CoInitialize", , 当前线程上初始化COM库
    .参数 pvReserved, 整数型, , 值为0

然后调用如下:

加载COM (0)

第二步 创建对象

.局部变量 svxml, 对象
svxml.创建 (“MSXML2.ServerXMLHTTP.6.0”, )
' 一定要写上版本 不然就没法用代理了

第三步 进行设置


如果需要代理:

svxml.方法 (“SetProxy”, 2, [文本型] 代理)

如果不需要:

svxml.方法 (“SetProxy”, 1, “ ”)

然后设置超时

svxml.方法 (“SetTimeouts”,[整数型] 超时, [整数型] 超时, [整数型] 超时, [整数型] 超时)

四个超时都有具体含义,需为整数型,具体请翻MSDN

第四部 打开连接并设置请求头

svxml.方法 (“open”, 方法, 完整网址, 真)

方法为"POST"或"GET",都要大写,为文本类型。
最后一个布尔值表示是否为异步,把它选上。


接下来要设置的请求头有六个

svxml.方法 (“SetRequestHeader”, “Accept”, [文本型] Accept)
svxml.方法 (“SetRequestHeader”, “Accept-language”, [文本型] Accept_Language)
svxml.方法 (“SetRequestHeader”, “User-Agent”, [文本型] User_Agent)
svxml.方法 (“SetRequestHeader”, “Content-Type”, [文本型] Content_Type)
svxml.方法 (“SetRequestHeader”, “Connection”, [文本型] Connection)
svxml.方法 (“SetRequestHeader”, “Cache-Control”, [文本型] Cache_Control)

按照封包的内容设置即可,Host已经在open里设置完了所以不用。
有可能还有几个

    svxml.方法 (“SetRequestHeader”, “Cookie”, [文本型] Cookie)
    svxml.方法 (“SetRequestHeader”, “Referer”, [文本型] Referer)

(后面的属性要自己设置)

第五步 发送数据并取回响应信息及响应头

svxml.方法 (“send”, [文本型] 数据)

get的话数据留空

svxml.逻辑方法 (“WaitForResponse”, -1)

等待异步完成

响应头 = svxml.文本方法 (“GetAllResponseHeaders”, )

当然也可以具体取某一项,比如:

响应头 = svxml.文本方法 (“GetResponseHeader”, “Set-Cookie”)

data = svxml.读属性 (“responseBody”, ).取字节集 ()

这里不推荐直接取文本,因为会自动转码,在某些网页会很蛋疼。
因为本机ansi是gbk,所以我这样写

.如果 (编码 = #编码_GBK) '本机编码直接返回
    响应信息 = 到文本 (data))
.否则
    data = 编码转换 (data, 编码, #编码_GBK, )
    响应信息 = 到文本 (data))

当然,编码要事先通过判断网页的编码自行设置,大多数网页都是gbk的,手机网页大部分是utf-8的。

第六步 清除及销毁

如果有下一个发送操作,直接创建就好,它会自动把原先的抹掉。 

最后,如果不再用这个对象了,要把现在的清除,关闭对象,还是调用API。声明:

.DLL命令 卸载COM, , "ole32.dll", "CoUninitialize", , 当前线程上销毁COM库

调用:

svxml.清除 ()
卸载COM ()

看了这么一大长串代码,我不知道你们有什么感受,反正我觉得如果每个封包都要打一遍不得累死= =

所以大家可以把它封装为函数。但是这样会导致传参过多。我的建议是封装成类,诸多协议头利用类初始化来赋值。它们一般不需要改,也就不用管它们。关于这部分的代码请见附录。


【实战登陆百度】

告诉大家一个捷径:cookie中只有BDUSS项是有用的,其他的都没有。
在这一节,我用的是我封装好的svxml类,见附录1
接口采用常规的http

首先抓包(过程略去):
分析一下,双斜线为注释:

POST /?login HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: <a href="https://passport.baidu.com/?login" target="_blank">https://passport.baidu.com/?login</a> //测试一下就可以发现来路不用写也能成功。
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; NP06; .NET4.0C; .NET4.0E; InfoPath.2)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate //这项填写后没多大用,或者返回来都是乱码。
Host: passport.baidu.com
Content-Length: 215
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: BAIDUID=F6D7A289F9CDF94113127DD966AF9EC1:FG=1; BAIDU_WISE_UID=wapp_1342195021834_751; WAPTIME=1342280325; BDUSS=******; PTOKEN=b2258f0edd91bae375f98629feb9cba7; STOKEN=05ffdbaa53bac2f2e911292e8afa10d8; SAVEUSERID=faea0bcaa6647bb77f48c970d5ed5d8098 //登录不需要cookie,所以省。


tpl_ok=&next_target=&tpl=&skip_ok=&aid=&need_pay=&need_coin=&pay_method=&u=.%2F&return_method=get&more_param=&return_type=&psp_tt=0&password=<密码>&safeflg=0&isphone=false&username=<用户名>&verifycode=&mem_pass=on

接着用代码来实现:

初始化实例:


.局部变量 xmlhttp, SvXml

接着 定义一个局部变量放文本


.局部变量 postdata, 文本型
postdata = “tpl_ok=&next_target=&tpl=mn&skip_ok=&aid=&need_pay=&need_coin=&pay_method=&u=http%3A%2F%2Fwww.baidu.com%2F&return_method=get&more_param=&return_type=&psp_tt=0&password=” + 密码 +“&safeflg=0&isphone=tpl&username= ” + 用户名 +“ &verifycode=&mem_pass=on

用户名要求gbk编码,不过gbk就是本机编码倒也无所谓。大家也可以去找一些这样的url编码源码来看。

然后发送封包:


.局部变量 retstream, 文本型
.局部变量 rethdr, 文本型
retstream = xmlhttp.访问网页 (“POST”, “<a href="https://passport.baidu.com/?login" target="_blank">https://passport.baidu.com/?login</a>”, postdata, , 4000, rethdr)


接下来就是查看rethdr中有没有BDUSS。这里大家可以搜索文本,我这里用的是正则。


.局部变量 正则, 正则表达式
.局部变量 结果, 搜索结果
    正则.创建 (“BDUSS=.{192};”, )
    结果 = 正则.搜索 (rethdr, 1, )
    .如果真 (结果.是否为空 ())
        信息框 (“登录失败!”, 0, )
        返回 ()
    .如果真结束
    Cookie = 结果.取匹配文本 (rethdr, )
    信息框 (“登录成功!”, 0, )

至此大功告成


【附录1:svxml类】

by 飞龙,部分代码来自mars访问网页模块

.版本 2
.支持库 iconv


.程序集 SvXml, , 公开
.程序集变量 svxml, 对象
.程序集变量 编码, 文本型
.程序集变量 Cookie, 文本型
.程序集变量 Referer, 文本型
.程序集变量 Accept, 文本型
.程序集变量 Accept_Language, 文本型
.程序集变量 User_Agent, 文本型
.程序集变量 Connection, 文本型
.程序集变量 Content_Type, 文本型
.程序集变量 Cache_Control, 文本型


.子程序 _初始化, , , 当基于本类的对象被创建后,此方法会被自动调用


加载COM (0)
.如果真 (svxml.创建 (“MSXML2.ServerXMLHTTP.6.0”, ) = 假)
    svxml.创建 (“MSXML2.ServerXMLHTTP.5.0”, )
.如果真结束
Accept = “*/*”
Accept_Language = “zh-cn”
User_Agent = “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)”
Connection = “Keep-Alive”
Content_Type = “application/x-www-form-urlencoded”
Cache_Control = “no-cache”
编码 = #编码_GBK


.子程序 _销毁, , , 当基于本类的对象被销毁前,此方法会被自动调用


svxml.清除 ()
卸载COM ()




.子程序 中止, , 公开, 中止正在进行的异步操作


svxml.方法 (“Abort”, )


.子程序 重定义编码, , 公开
.参数 新编码, 文本型


编码 = 新编码


.子程序 取得对象, 对象, 公开


返回 (svxml)


.子程序 访问网页, 文本型, 公开
.参数 方法, 文本型, 可空, 默认GET
.参数 完整网址, 文本型
.参数 数据, 文本型, 可空, POST专用
.参数 代理, 文本型, 可空
.参数 超时, 整数型, 可空, 默认8000
.参数 响应头, 文本型, 参考 可空
.局部变量 data, 字节集


.如果真 (是否为空 (超时))
    超时 = 8000
.如果真结束
.如果真 (是否为空 (方法))
    方法 = “GET”
.如果真结束
.如果 (是否为空 (代理))
    svxml.方法 (“SetProxy”, 1, “”)
.否则
    svxml.方法 (“SetProxy”, 2, 代理)
.如果结束
svxml.方法 (“SetTimeouts”, 超时, 超时, 超时, 超时)
svxml.方法 (“open”, 方法, 完整网址, 真)
svxml.方法 (“SetRequestHeader”, “Accept”, Accept)
svxml.方法 (“SetRequestHeader”, “Accept-language”, Accept_Language)
svxml.方法 (“SetRequestHeader”, “User-Agent”, User_Agent)
svxml.方法 (“SetRequestHeader”, “Content-Type”, Content_Type)
svxml.方法 (“SetRequestHeader”, “Connection”, Connection)
svxml.方法 (“SetRequestHeader”, “Cache-Control”, Cache_Control)
.如果真 (Cookie ≠ “”)
    svxml.方法 (“SetRequestHeader”, “Cookie”, Cookie)
.如果真结束
.如果真 (Referer ≠ “”)
    svxml.方法 (“SetRequestHeader”, “Referer”, Referer)
.如果真结束
svxml.方法 (“send”, 数据)
svxml.逻辑方法 (“WaitForResponse”, -1)
.如果真 (取反 (是否为空 (响应头)))
    响应头 = svxml.文本方法 (“GetAllResponseHeaders”, )
.如果真结束
' 直接取ResponseText会自动转码
data = svxml.读属性 (“responseBody”, ).取字节集 ()
.如果 (编码 = #编码_GBK)
    返回 (到文本 (data))
.否则
    data = 编码转换 (data, 编码, #编码_GBK, )
    返回 (到文本 (data))
.如果结束




.子程序 置Cookie, , 公开
.参数 New_Cookie, 文本型


Cookie = New_Cookie




.子程序 置Referer, , 公开
.参数 New_Referer, 文本型


Referer = New_Referer


.子程序 重定义Accept, , 公开
.参数 New_Accept, 文本型


Accept = New_Accept




.子程序 重定义Accept_Language, , 公开
.参数 New_Accept_Language, 文本型


Accept_Language = New_Accept_Language




.子程序 重定义User_Agent, , 公开
.参数 New_User_Agent, 文本型


User_Agent = New_User_Agent




.子程序 重定义Connection, , 公开
.参数 New_Connection, 文本型


Connection = New_Connection




.子程序 重定义Content_Type, , 公开
.参数 New_Content_Type, 文本型


Content_Type = New_Content_Type




.子程序 重定义Cache_Control, , 公开
.参数 New_Cache_Control, 文本型


Cache_Control = New_Cache_Control




.子程序 访问网页文件, 字节集, 公开
.参数 方法, 文本型, 可空, 默认GET
.参数 完整网址, 文本型
.参数 数据, 文本型, 可空, POST专用
.参数 代理, 文本型, 可空
.参数 超时, 整数型, 可空, 默认8000
.参数 响应头, 文本型, 参考 可空
.局部变量 data, 字节集


.如果真 (是否为空 (超时))
    超时 = 8000
.如果真结束
.如果真 (是否为空 (方法))
    方法 = “GET”
.如果真结束
.如果 (是否为空 (代理))
    svxml.方法 (“SetProxy”, 1, “”)
.否则
    svxml.方法 (“SetProxy”, 2, 代理)
.如果结束
svxml.方法 (“SetTimeouts”, 超时, 超时, 超时, 超时)
svxml.方法 (“open”, 方法, 完整网址, 真)
svxml.方法 (“SetRequestHeader”, “Accept”, Accept)
svxml.方法 (“SetRequestHeader”, “Accept-language”, Accept_Language)
svxml.方法 (“SetRequestHeader”, “User-Agent”, User_Agent)
svxml.方法 (“SetRequestHeader”, “Content-Type”, Content_Type)
svxml.方法 (“SetRequestHeader”, “Connection”, Connection)
svxml.方法 (“SetRequestHeader”, “Cache-Control”, Cache_Control)
.如果真 (Cookie ≠ “”)
    svxml.方法 (“SetRequestHeader”, “Cookie”, Cookie)
.如果真结束
.如果真 (Referer ≠ “”)
    svxml.方法 (“SetRequestHeader”, “Referer”, Referer)
.如果真结束
svxml.方法 (“send”, 数据)
svxml.逻辑方法 (“WaitForResponse”, -1)
.如果真 (取反 (是否为空 (响应头)))
    响应头 = svxml.文本方法 (“GetAllResponseHeaders”, )
.如果真结束
' 直接取ResponseText会自动转码
data = svxml.读属性 (“responseBody”, ).取字节集 ()
返回 (data)

【附录2: svxml对象使用说明】


by fanxiaojie119

http://blog.csdn.net/fanxiaojie119/article/details/5187631

2012-7-20
[The End]

展开阅读全文

oc初学者:请问用application/json方式提交POST请求应该怎么写?

10-18
Header { "Content-Type":"application/json" } 用下面的代码取不到返回值,请问应该怎么写? - (void)POSTRequestWithUrl:(NSString *)urlString paramaters:(NSMutableDictionary *)paramaters successBlock:(SuccessBlock)success FailBlock:(failBlock)fail { NSMutableString *strM = [[NSMutableString alloc] init]; [paramaters enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { // 服务器接收参数的 key 值. NSString *paramaterKey = key; // 参数内容 NSString *paramaterValue = obj; // appendFormat :可变字符串直接拼接的方法! [strM appendFormat:@"%@=%@&",paramaterKey,paramaterValue]; }]; NSString *body = [strM substringToIndex:strM.length - 1]; NSData *bodyData = [body dataUsingEncoding:NSUTF8StringEncoding]; NSURL *url = [NSURL URLWithString:urlString]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60]; // 1.设置请求方法: request.HTTPMethod = @"POST"; // 2.设置请求体 request.HTTPBody = bodyData; //获得会话对象 NSURLSession *session = [NSURLSession sharedSession]; // 2. 发送网络请求. // completionHandler: 说明网络请求完成! NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); // 网络请求成功: if (data && !error) { // 查看 data 是否是 JSON 数据. // JSON 解析. id obj = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; // 如果 obj 能够解析,说明就是 JSON if (!obj) { if ([[obj objectForKey:@"status"] isEqualToString:@"200"]) { obj = data; }else if ([[obj objectForKey:@"status"] isEqualToString:@"500"]) { NSLog(@"%@",[obj objectForKey:@"messageText"]); } } dispatch_async(dispatch_get_main_queue(), ^{// 成功回调 if (success) { success(obj,response); } }); }else{//失败回调 if (fail) { fail(error); } } }]; [dataTask resume]; }
©️2020 CSDN 皮肤主题: 黑客帝国 设计师: 上身试试 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值