发表回复 
9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
2015-03-26, 10 : 07 (这个帖子最后修改于: 2015-09-12 12 : 57 by 兔子.)
9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
本楼附件中总是最新版,即最新版总是在本楼更新!!!

2015.09.12
优化代码结构
2015.09.11
修正tmplinshi发现一个关于超时设置的大bug。


附件是压缩包,其中包含超级详细的例子,本库,及本库的1个支持函数。
请下载附件后使用,本楼代码只作备份用。


与内置命令 UrlDownloadToFile 的区别有以下几点:
1.可以直接下载到变量,没有临时文件。当然也可以下载到文件。
2.下载速度更快,大概100%。
3.内置命令执行时,整个AHK程序都是卡顿状态。此函数不会。
4.内置命令下载一些诡异网站(例如“牛杂网”)时,会概率性让进程或线程彻底死掉。此函数不会。
5.支持设置网页字符集、URL的编码。乱码问题轻松解决。
6.支持设置所有“Request Header”。常见的有:Cookie、Referer、User-Agent。网站检测问题轻松解决。
7.支持设置超时,不必死等。
8.支持设置代理及白名单。
9.支持设置是否自动重定向网址。
10.“RequestHeaders”参数格式与chrome的开发者工具中的“Request Header”相同,因此可直接复制过来就用,方便调试。
11.可以使用“POST”方法,因此可上传数据。当然也可以只用“GET”方法。
12.支持存取“Cookie”,可用于模拟登录状态。
13.支持判断网页返回时的状态码,例如200,404等。


代码: (全选)
/*
版本:1.4

更新日志:
    2015.09.12
    优化代码结构
    版本号为1.4

    2015.09.11
    修正超时会在错误时间被激活的问题。(http://ahk8.com/thread-5658-post-33736.html#pid33736)
    以下是tmplinshi对这个问题的详细描述。
    ----------------------------------------------------------------------------------------------
    WebRequest.WaitForResponse(超时秒数)
    默认情况下,这个超时秒数并不是你设置为空就一直等待,设置 60 就等待 60 秒。而是要受限于默认的超时设置。

    默认的超时设置为:
    解析超时: 0 秒
    连接超时: 60 秒
    发送超时: 30 秒
    接收超时: 30 秒

    WaitForResponse 应该是指接收超时吧。所以呢,默认的话即使你设置 WaitForResponse(60) 实际上还是最多就等待 30 秒。。

    默认值可以通过 WebRequest.SetTimeouts(解析超时, 连接超时, 发送超时, 接收超时) 来设置,详见 MSDN 的说明。比如把接收超时修改为 120 秒 —— WebRequest.SetTimeouts(0, 60000, 30000, 120000)

    这点没有明白可把我害惨了。最近写的一个查询软件,经常查询失败。我原本以为是网站无响应,因为我没有设置超时,以为软件会一直等待(但是上面说了,“一直等待​”会受限于​默认的最大超时)。后来仔细看抓包数据,看到每次都 30 秒超时返回。而用浏览器测试却正常,在 40 多秒的时候返回了结果,这才发觉是软件不对劲。

    希望更多人知道这点说明,有空我还要再发几个帖子说明这一点。。另外@兔子 你也可以修改下代码,如果传递的超时超过了默认的 30 秒则调用一下 SetTimeouts。
    ----------------------------------------------------------------------------------------------
    版本号为1.3

    2015.06.05
    添加静态变量Status、StatusText,用法和ResponseHeaders一致。
    添加新功能,若指定状态码与重试次数,将重试n次,直至状态码与预期一致。
    版本号为1.2

已知问题:
    类名和函数名都很长,比如要使用下载到变量时,需要输入“WinHttp.UrlDownloadToVar()”。
    解决的办法有两种:
        1.自己在scite的“AhkAbbrevs.properties”文件中写入“wv=WinHttp.UrlDownloadToVar(|)”,也就是​缩略语。这样在你输入“wv”后再按“ctrl+b”就可以自动把这一切输入好了。
        2.自己把这个类的类名和其中函数名改成简单一点的,然后自己用。

    不支持gzip压缩后的数据。正常情况下,你不向服务器说明你需要gzip压缩后的数据,它们是不会给你发送的,所以没影响。

    cookie没有实现像浏览器那样的自动管理。但是你可以在需要的时候随时取出,自行管理。
*/

class WinHttp
{
    /*
    “ResponseHeaders”这个变量中存储的就是每次访问网址时,服务器返回的“ResponseHeaders”。当然,它已经被解析成对象了,方便​直接使用。
    不需要的时候,可以不用管它。需要的时候,则在下载网址后,紧接着读取这个对象就行了。
    例如“obj:=WinHttp.ResponseHeaders”,此时obj中就包含了刚才访问网址时服务器返回的所有“ResponseHeaders”​。
    于是“MsgBox, % obj["Content-type"]”,就得到了“Content-type”。
    于是“MsgBox, % obj["Set-Cookie"]”,就得到了“Set-Cookie”。
    需要注意的是,由于“Set-Cookie”很可能一次返回了多条,所以如果存在多条“Set-Cookie”,它们是用“`r`n”分隔的。
    “Status”和“StatusText”用法与“ResponseHeaders”一致,区别为前两者是纯变量。
    例如“MsgBox, % WinHttp.Status”,就得到了状态码。
    */
    static ResponseHeaders:=[],Status:="",StatusText:="",extra:=[]

    /*
    *****************版本*****************
    v 1.4

    *****************说明*****************
    此函数与内置命令 UrlDownloadToFile 的区别有以下几点:
    1.下载速度更快,大概100%。
    2.内置命令执行时,整个AHK程序都是卡顿状态。此函数不会。
    3.内置命令下载一些诡异网站(例如“牛杂网”)时,会概率性让进程或线程彻底死掉。此函数不会。
    4.支持设置网页字符集、URL的编码。乱码问题轻松解决。
    5.支持设置所有“Request Header”。常见的有:Cookie、Referer、User-Agent。网站检测问题轻松解决。
    6.支持设置超时,不必死等。
    7.支持设置代理及白名单。
    8.支持设置是否自动重定向网址。
    9.“RequestHeaders”参数格式与chrome的开发者工具中的“Request Header”相同,因此可直接复制过来就用,方便调试。
    10.支持存取“Cookie”,可用于模拟登录状态。
    11.支持判断网页返回时的状态码,例如200,404等。

    *****************参数*****************
    URL 网址,必须包含类似“http://”的开头。“www.”最好也带上,有些网站需要。
    Options、RequestHeaders的格式为:每行一个参数,行首至第一个冒号为参数名,之后至行尾为参数值。多个参数换行。具体可参照“解析信息到​对象()”注释中的例子。

    *****************Options*****************
    支持以下7种设置,输入其它值无任何效果,无大小写要求。
    proxy_setting 代理服务器设置,0表示使用“Proxycfg.exe”的设置;1表示无视“Proxy”指定的代理而直接连接;2表示使用“Proxy”指定的代理。
    Proxy 代理服务器,是形如“http://www.tuzi.com:80”的字符。程序会根据此处的值自动设置合适的“proxy_setting”,即通常情况下不用管“proxy_setting”,除非你​想自己控制。
    ProxyBypassList 代理服务器绕行名单,是形如“*.microsoft.com”的域名。符合域名的网址,将不通过代理服务器访问。
    EnableRedirects 重定向,默认获取跳转后的页面信息,0为不跳转。
    Timeout 超时,单位为秒,默认不使用超时(Timeout=-1)。
    expected_status    状态码,通常200表示网页正常,404表示网页找不到了。设置后当网页返回的状态码与此处不一致则抛出调试信息并报错(故使用此参数后建议同时使用try语句​)。
    number_of_retries    重试次数(与状态码配对使用),当网页返回的状态码与期望状态码不一致时,可以重试的次数。

    *****************RequestHeaders*****************
    支持所有RequestHeader,大小写的改变可能会影响结果。常见的有以下这些。
    Cookie ,常用于登录验证。
    Referer 引用网址,常用于防盗链。
    User-Agent 用户信息,常用于防盗链。

    *****************注意*****************
    类似下面的参数不要加入到“RequestHeaders”中,它表示浏览器支持gzip数据压缩,会导致服务器发送压缩后的数据过来,也就会出错。
    Accept-Encoding:gzip,deflate,sdch
    */
    URLDownloadToFile(URL, FilePath, Options:="", RequestHeaders:="")
    {
        Options:=this.解析信息到对象(Options)
        RequestHeaders:=this.解析信息到对象(RequestHeaders)

        ComObjError(0)                                                                  ;禁用 COM 错误通告。禁用后,检查 A_LastError 的值,脚本可以实现自己的错误处理
        WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")

        if (Options["EnableRedirects"]<>"")                            ;设置是否获取跳转后的页面信息
            WebRequest.Option(6):=Options["EnableRedirects"]
        ;proxy_setting没值时,根据Proxy值的情况智能设定是否要进行代理访问。
        ;这样的好处是多数情况下需要代理时依然只用给出代理服务器地址即可。而在已经给出代理服务器地址后,又可以很方便的对是否启用代理进行开关。
        if (Options["proxy_setting"]="" and Options["Proxy"]<>"")
            Options["proxy_setting"]:=2                                        ;0表示 Proxycfg.exe 运行了且遵循 Proxycfg.exe 的设置(没运行则效果同设置为1)。1表示忽略代理直连。2表示使用代理
        if (Options["proxy_setting"]="" and Options["Proxy"]="")
            Options["proxy_setting"]:=1
        ;设置代理服务器。微软的代码 SetProxy() 是放在 Open() 之前的,所以我也放前面设置,以免无效
        WebRequest.SetProxy(Options["proxy_setting"],Options["Proxy"],Options["ProxyBypassList"])
        if (Options["Timeout"]="")                                            ;Options["Timeout"]如果被设置为-1,并不代表无限超时,而是依然遵循SetTimeouts第4个参数设置的最大超时时间
            WebRequest.SetTimeouts(0,60000,30000,0)            ;0或-1都表示超时无限等待,正整数则表示最大超时(单位毫秒)
        else if (Options["Timeout"]>30)                                    ;如果超时设置大于30秒,则需要将默认的最大超时时间修改为大于30秒
            WebRequest.SetTimeouts(0,60000,30000,Options["Timeout"]*1000)
        else
            WebRequest.SetTimeouts(0,60000,30000,30000)    ;此为SetTimeouts的默认设置。这句可以不加,因为默认就是这样,加在这里是为了表述清晰。

        WebRequest.Open("GET", URL, true)                           ;true为异步获取。默认是false,龟速的根源!!!卡顿的根源!!!

        ;SetRequestHeader() 必须 Open() 之后才有效
        for k, v in RequestHeaders
        {
            if (k="Cookie")
            {
                WebRequest.SetRequestHeader("Cookie","tuzi")    ;先设置一个cookie,防止出错,msdn推荐这么做
                WebRequest.SetRequestHeader("Cookie",v)
            }
            WebRequest.SetRequestHeader(k,v)
        }

        Loop
        {
            WebRequest.Send()
            WebRequest.WaitForResponse(-1)                                ;WaitForResponse方法确保获取的是完整的响应。-1表示总是使用SetTimeouts设置的超时

            ;获取状态码,一般status为200说明请求成功
            this.Status:=WebRequest.Status()
            this.StatusText:=WebRequest.StatusText()

            if (Options["expected_status"]="" or Options["expected_status"]=this.Status)
                break
            ;尝试指定次数后页面返回的状态码依旧与预期状态码不一致,则抛出错误及详细错误信息(可使用我另一个错误处理函数专门记录处理它们)
            ;即使number_of_retries为空,表达式依然成立,所以不用为number_of_retries设置初始值。
            else if (A_Index>=Options["number_of_retries"])
            {
                this.extra.URL:=URL
                this.extra.Expected_Status:=Options["expected_status"]
                this.extra.Status:=this.Status
                this.extra.StatusText:=this.StatusText
                throw, Exception("经过" Options.number_of_retries "次尝试后,服务器返回状态码依旧与期望值不一致", -1, Object(this.extra))
            }
        }

        ADO:=ComObjCreate("adodb.stream")           ;使用 adodb.stream 编码返回值。参考 http://bbs.howtoadmin.com/ThRead-814-1-1.html
        ADO.Type:=1                                                        ;以二进制方式操作
        ADO.Mode:=3                                                     ;可同时进行读写
        ADO.Open()                                                          ;开启物件
        ADO.Write(WebRequest.ResponseBody())        ;写入物件。注意没法将 WebRequest.ResponseBody() 存入一个变量,所以必须用这种方式写文件
        ADO.SaveToFile(FilePath,2)                                ;文件存在则覆盖
        ADO.Close()
        this.ResponseHeaders:=this.解析信息到对象(WebRequest.GetAllResponseHeaders())
        return, 1
    }

    /*
    *****************版本*****************
    v 1.4

    *****************说明*****************
    此函数与内置命令 UrlDownloadToFile 的区别有以下几点:
    1.直接下载到变量,没有临时文件。
    2.下载速度更快,大概100%。
    3.内置命令执行时,整个AHK程序都是卡顿状态。此函数不会。
    4.内置命令下载一些诡异网站(例如“牛杂网”)时,会概率性让进程或线程彻底死掉。此函数不会。
    5.支持设置网页字符集、URL的编码。乱码问题轻松解决。
    6.支持设置所有“Request Header”。常见的有:Cookie、Referer、User-Agent。网站检测问题轻松解决。
    7.支持设置超时,不必死等。
    8.支持设置代理及白名单。
    9.支持设置是否自动重定向网址。
    10.“RequestHeaders”参数格式与chrome的开发者工具中的“Request Header”相同,因此可直接复制过来就用,方便调试。
    11.支持存取“Cookie”,可用于模拟登录状态。
    12.支持判断网页返回时的状态码,例如200,404等。

    *****************参数*****************
    URL 网址,必须包含类似“http://”的开头。“www.”最好也带上,有些网站需要。
    Options、RequestHeaders的格式为:每行一个参数,行首至第一个冒号为参数名,之后至行尾为参数值。多个参数换行。具体可参照“解析信息到​对象()”注释中的例子。

    *****************Options*****************
    支持以下9种设置,输入其它值无任何效果,无大小写要求。
    Charset 网页字符集,不能是“936”之类的数字,必须是“gb2312”这样的字符。
    URLCodePage URL的编码,是“936”之类的数字,默认是“65001”。有些网站需要UTF-8,有些网站又需要gb2312。
    proxy_setting 代理服务器设置,0表示使用“Proxycfg.exe”的设置;1表示无视“Proxy”指定的代理而直接连接;2表示使用“Proxy”指定的代理。
    Proxy 代理服务器,是形如“http://www.tuzi.com:80”的字符。程序会根据此处的值自动设置合适的“proxy_setting”,即通常情况下不用管“proxy_setting”,除非你​想自己控制。
    ProxyBypassList 代理服务器绕行名单,是形如“*.microsoft.com”的域名。符合域名的网址,将不通过代理服务器访问。
    EnableRedirects 重定向,默认获取跳转后的页面信息,0为不跳转。
    Timeout 超时,单位为秒,默认不使用超时(Timeout=-1)。
    expected_status    状态码,通常200表示网页正常,404表示网页找不到了。设置后当网页返回的状态码与此处不一致则抛出调试信息并报错(故使用此参数后建议同时使用try语句​)。
    number_of_retries    重试次数(与状态码配对使用),当网页返回的状态码与期望状态码不一致时,可以重试的次数。

    *****************RequestHeaders*****************
    支持所有RequestHeader,大小写的改变可能会影响结果。常见的有以下这些。
    Cookie ,常用于登录验证。
    Referer 引用网址,常用于防盗链。
    User-Agent 用户信息,常用于防盗链。

    *****************注意*****************
    类似下面的参数不要加入到“RequestHeaders”中,它表示浏览器支持gzip数据压缩,会导致服务器发送压缩后的数据过来,也就会出错。
    Accept-Encoding:gzip,deflate,sdch
    */
    UrlDownloadToVar(URL, Options:="", RequestHeaders:="")
    {
        Options:=this.解析信息到对象(Options)
        RequestHeaders:=this.解析信息到对象(RequestHeaders)

        ComObjError(0)                                                                  ;禁用 COM 错误通告。禁用后,检查 A_LastError 的值,脚本可以实现自己的错误处理
        WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")

        if (Options["URLCodePage"]<>"")                                ;设置URL的编码
            WebRequest.Option(2):=Options["URLCodePage"]
        if (Options["EnableRedirects"]<>"")                            ;设置是否获取跳转后的页面信息
            WebRequest.Option(6):=Options["EnableRedirects"]
        ;proxy_setting没值时,根据Proxy值的情况智能设定是否要进行代理访问。
        ;这样的好处是多数情况下需要代理时依然只用给出代理服务器地址即可。而在已经给出代理服务器地址后,又可以很方便的对是否启用代理进行开关。
        if (Options["proxy_setting"]="" and Options["Proxy"]<>"")
            Options["proxy_setting"]:=2                                        ;0表示 Proxycfg.exe 运行了且遵循 Proxycfg.exe 的设置(没运行则效果同设置为1)。1表示忽略代理直连。2表示使用代理
        if (Options["proxy_setting"]="" and Options["Proxy"]="")
            Options["proxy_setting"]:=1
        ;设置代理服务器。微软的代码 SetProxy() 是放在 Open() 之前的,所以我也放前面设置,以免无效
        WebRequest.SetProxy(Options["proxy_setting"],Options["Proxy"],Options["ProxyBypassList"])
        if (Options["Timeout"]="")                                            ;Options["Timeout"]如果被设置为-1,并不代表无限超时,而是依然遵循SetTimeouts第4个参数设置的最大超时时间
            WebRequest.SetTimeouts(0,60000,30000,0)            ;0或-1都表示超时无限等待,正整数则表示最大超时(单位毫秒)
        else if (Options["Timeout"]>30)                                    ;如果超时设置大于30秒,则需要将默认的最大超时时间修改为大于30秒
            WebRequest.SetTimeouts(0,60000,30000,Options["Timeout"]*1000)
        else
            WebRequest.SetTimeouts(0,60000,30000,30000)    ;此为SetTimeouts的默认设置。这句可以不加,因为默认就是这样,加在这里是为了表述清晰。

        WebRequest.Open("GET", URL, true)                           ;true为异步获取。默认是false,龟速的根源!!!卡顿的根源!!!

        ;SetRequestHeader() 必须 Open() 之后才有效
        for k, v in RequestHeaders
        {
            if (k="Cookie")
            {
                WebRequest.SetRequestHeader("Cookie","tuzi")    ;先设置一个cookie,防止出错,msdn推荐这么做
                WebRequest.SetRequestHeader("Cookie",v)
            }
            WebRequest.SetRequestHeader(k,v)
        }

        Loop
        {
            WebRequest.Send()
            WebRequest.WaitForResponse(-1)                                ;WaitForResponse方法确保获取的是完整的响应。-1表示总是使用SetTimeouts设置的超时

            ;获取状态码,一般status为200说明请求成功
            this.Status:=WebRequest.Status()
            this.StatusText:=WebRequest.StatusText()

            if (Options["expected_status"]="" or Options["expected_status"]=this.Status)
                break
            ;尝试指定次数后页面返回的状态码依旧与预期状态码不一致,则抛出错误及详细错误信息(可使用我另一个错误处理函数专门记录处理它们)
            ;即使number_of_retries为空,表达式依然成立,所以不用为number_of_retries设置初始值。
            else if (A_Index>=Options["number_of_retries"])
            {
                this.extra.URL:=URL
                this.extra.Expected_Status:=Options["expected_status"]
                this.extra.Status:=this.Status
                this.extra.StatusText:=this.StatusText
                throw, Exception("经过" Options.number_of_retries "次尝试后,服务器返回状态码依旧与期望值不一致", -1, Object(this.extra))
            }
        }

        if (Options["Charset"]<>"")                                     ;设置字符集
        {
            ADO:=ComObjCreate("adodb.stream")              ;使用 adodb.stream 编码返回值。参考 http://bbs.howtoadmin.com/ThRead-814-1-1.html
            ADO.Type:=1                                                         ;以二进制方式操作
            ADO.Mode:=3                                                     ;可同时进行读写
            ADO.Open()                                                          ;开启物件
            ADO.Write(WebRequest.ResponseBody())      ;写入物件。注意 WebRequest.ResponseBody() 获取到的是无符号的bytes,通过 adodb.stream 转换成字符串string
            ADO.Position:=0                                                 ;从头开始
            ADO.Type:=2                                                         ;以文字模式操作
            ADO.Charset:=Options["Charset"]                    ;设定编码方式
            ret_var:=ADO.ReadText()                                   ;将物件内的文字读出
            ADO.Close()
            this.ResponseHeaders:=this.解析信息到对象(WebRequest.GetAllResponseHeaders())
            return, ret_var
        }
        else
        {
            this.ResponseHeaders:=this.解析信息到对象(WebRequest.GetAllResponseHeaders())
            return, WebRequest.ResponseText()
        }
    }

    /*
    *****************版本*****************
    v 1.4

    *****************说明*****************
    此函数与内置命令 UrlDownloadToFile 的区别有以下几点:
    1.直接下载到变量,没有临时文件。
    2.下载速度更快,大概100%。
    3.内置命令执行时,整个AHK程序都是卡顿状态。此函数不会。
    4.内置命令下载一些诡异网站(例如“牛杂网”)时,会概率性让进程或线程彻底死掉。此函数不会。
    5.支持设置网页字符集、URL的编码。乱码问题轻松解决。
    6.支持设置所有“Request Header”。常见的有:Cookie、Referer、User-Agent。网站检测问题轻松解决。
    7.支持设置超时,不必死等。
    8.支持设置代理及白名单。
    9.支持设置是否自动重定向网址。
    10.“RequestHeaders”参数格式与chrome的开发者工具中的“Request Header”相同,因此可直接复制过来就用,方便调试。
    11.使用“POST”方法,因此可上传数据。
    12.支持存取“Cookie”,可用于模拟登录状态。
    13.支持判断网页返回时的状态码,例如200,404等。

    *****************参数*****************
    URL 网址,必须包含类似“http://”的开头。“www.”最好也带上,有些网站需要。
    Data 数据,默认是文本,即开发者工具中“Request Payload”段中的内容。
    Options、RequestHeaders的格式为:每行一个参数,行首至第一个冒号为参数名,之后至行尾为参数值。多个参数换行。具体可参照“解析信息到​对象()”注释中的例子。

    *****************Options*****************
    支持以下6种设置,输入其它值无任何效果,无大小写要求。
    Charset 网页字符集,不能是“936”之类的数字,必须是“gb2312”这样的字符。
    URLCodePage URL的编码,是“936”之类的数字,默认是“65001”。有些网站需要UTF-8,有些网站又需要gb2312。
    proxy_setting 代理服务器设置,0表示使用“Proxycfg.exe”的设置;1表示无视“Proxy”指定的代理而直接连接;2表示使用“Proxy”指定的代理。
    Proxy 代理服务器,是形如“http://www.tuzi.com:80”的字符。程序会根据此处的值自动设置合适的“proxy_setting”,即通常情况下不用管“proxy_setting”,除非你​想自己控制。
    ProxyBypassList 代理服务器绕行名单,是形如“*.microsoft.com”的域名。符合域名的网址,将不通过代理服务器访问。
    EnableRedirects 重定向,默认获取跳转后的页面信息,0为不跳转。
    Timeout 超时,单位为秒,默认不使用超时(Timeout=-1)。
    expected_status    状态码,通常200表示网页正常,404表示网页找不到了。设置后当网页返回的状态码与此处不一致则抛出调试信息并报错(故使用此参数后建议同时使用try语句​)。
    number_of_retries    重试次数(与状态码配对使用),当网页返回的状态码与期望状态码不一致时,可以重试的次数。

    *****************RequestHeaders*****************
    支持所有RequestHeader,大小写的改变可能会影响结果。常见的有以下这些。
    Cookie ,常用于登录验证。
    Referer 引用网址,常用于防盗链。
    User-Agent 用户信息,常用于防盗链。

    *****************注意*****************
    类似下面的参数不要加入到“RequestHeaders”中,它表示浏览器支持gzip数据压缩,会导致服务器发送压缩后的数据过来,也就会出错。
    Accept-Encoding:gzip,deflate,sdch
    */
    UrlPost(URL, Data, Options:="", RequestHeaders:="")
    {
        Options:=this.解析信息到对象(Options)
        RequestHeaders:=this.解析信息到对象(RequestHeaders)

        ComObjError(0)                                                                  ;禁用 COM 错误通告。禁用后,检查 A_LastError 的值,脚本可以实现自己的错误处理
        WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")

        if (Options["URLCodePage"]<>"")                                ;设置URL的编码
            WebRequest.Option(2):=Options["URLCodePage"]
        if (Options["EnableRedirects"]<>"")                            ;设置是否获取跳转后的页面信息
            WebRequest.Option(6):=Options["EnableRedirects"]
        ;proxy_setting没值时,根据Proxy值的情况智能设定是否要进行代理访问。
        ;这样的好处是多数情况下需要代理时依然只用给出代理服务器地址即可。而在已经给出代理服务器地址后,又可以很方便的对是否启用代理进行开关。
        if (Options["proxy_setting"]="" and Options["Proxy"]<>"")
            Options["proxy_setting"]:=2                                        ;0表示 Proxycfg.exe 运行了且遵循 Proxycfg.exe 的设置(没运行则效果同设置为1)。1表示忽略代理直连。2表示使用代理
        if (Options["proxy_setting"]="" and Options["Proxy"]="")
            Options["proxy_setting"]:=1
        ;设置代理服务器。微软的代码 SetProxy() 是放在 Open() 之前的,所以我也放前面设置,以免无效
        WebRequest.SetProxy(Options["proxy_setting"],Options["Proxy"],Options["ProxyBypassList"])
        if (Options["Timeout"]="")                                            ;Options["Timeout"]如果被设置为-1,并不代表无限超时,而是依然遵循SetTimeouts第4个参数设置的最大超时时间
            WebRequest.SetTimeouts(0,60000,30000,0)            ;0或-1都表示超时无限等待,正整数则表示最大超时(单位毫秒)
        else if (Options["Timeout"]>30)                                    ;如果超时设置大于30秒,则需要将默认的最大超时时间修改为大于30秒
            WebRequest.SetTimeouts(0,60000,30000,Options["Timeout"]*1000)
        else
            WebRequest.SetTimeouts(0,60000,30000,30000)    ;此为SetTimeouts的默认设置。这句可以不加,因为默认就是这样,加在这里是为了表述清晰。

        WebRequest.Open("POST", URL, true)   ;true为异步获取。默认是false,龟速的根源!!!卡顿的根源!!!

        ;SetRequestHeader() 必须 Open() 之后才有效
        for k, v in RequestHeaders
        {
            if (k="Cookie")
            {
                WebRequest.SetRequestHeader("Cookie","tuzi")    ;先设置一个cookie,防止出错,msdn推荐这么做
                WebRequest.SetRequestHeader("Cookie",v)
            }
            WebRequest.SetRequestHeader(k,v)
        }

        Loop
        {
            WebRequest.Send(Data)
            WebRequest.WaitForResponse(-1)                                ;WaitForResponse方法确保获取的是完整的响应。-1表示总是使用SetTimeouts设置的超时

            ;获取状态码,一般status为200说明请求成功
            this.Status:=WebRequest.Status()
            this.StatusText:=WebRequest.StatusText()

            if (Options["expected_status"]="" or Options["expected_status"]=this.Status)
                break
            ;尝试指定次数后页面返回的状态码依旧与预期状态码不一致,则抛出错误及详细错误信息(可使用我另一个错误处理函数专门记录处理它们)
            ;即使number_of_retries为空,表达式依然成立,所以不用为number_of_retries设置初始值。
            else if (A_Index>=Options["number_of_retries"])
            {
                this.extra.URL:=URL
                this.extra.Expected_Status:=Options["expected_status"]
                this.extra.Status:=this.Status
                this.extra.StatusText:=this.StatusText
                throw, Exception("经过" Options.number_of_retries "次尝试后,服务器返回状态码依旧与期望值不一致", -1, Object(this.extra))
            }
        }

        if (Options["Charset"]<>"")                                    ;设置字符集
        {
            ADO:=ComObjCreate("adodb.stream")              ;使用 adodb.stream 编码返回值。参考 http://bbs.howtoadmin.com/ThRead-814-1-1.html
            ADO.Type:=1                                                         ;以二进制方式操作
            ADO.Mode:=3                                                     ;可同时进行读写
            ADO.Open()                                                          ;开启物件
            ADO.Write(WebRequest.ResponseBody())        ;写入物件。注意 WebRequest.ResponseBody() 获取到的是无符号的bytes,通过 adodb.stream 转换成字符串string
            ADO.Position:=0                                                 ;从头开始
            ADO.Type:=2                                                         ;以文字模式操作
            ADO.Charset:=Options["Charset"]                   ;设定编码方式
            ret_var:=ADO.ReadText()                                   ;将物件内的文字读出
            ADO.Close()
            this.ResponseHeaders:=this.解析信息到对象(WebRequest.GetAllResponseHeaders())
            return, ret_var
        }
        else
        {
            this.ResponseHeaders:=this.解析信息到对象(WebRequest.GetAllResponseHeaders())
            return, WebRequest.ResponseText()
        }
    }

    /*
    infos的格式:每行一个参数,行首至第一个冒号为参数名,之后至行尾为参数值。多个参数换行。
    换句话说,chrome的开发者工具中“Request Header”那段内容直接复制过来就能用。
    需要注意第一行“GET /?tn=sitehao123 HTTP/1.1”其实是没有任何作用的,因为没有“:”。但复制过来了也并不会影响正常解析。

    infos=
    (
    GET /?tn=sitehao123 HTTP/1.1
    Host: www.baidu.com
    Connection: keep-alive
    Cache-Control: max-age=0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0
    DNT: 1
    Referer: http://www.hao123.com/
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: zh-CN,zh;q=0.8
    )
    */
    解析信息到对象(infos)
    {
        if (IsObject(infos)=1)
            return, infos

        ;以下两步可将“infos”换行符统一为`r`n,避免正则表达式提取时出错
        StringReplace, infos, infos, `r`n, `n, All
        StringReplace, infos, infos, `n, `r`n, All

        infos_temp:=GlobalRegExMatch(infos,"m)(^.*?):(.*$)",1)
        ;将正则匹配到的信息存入新的对象中,像这样{"Connection":"keep-alive","Cache-Control":"max-age=0"}
        infos:=[]
        Loop, % infos_temp.MaxIndex()
        {
            name:=Trim(infos_temp[A_Index].Value[1], " `t`r`n`v`f")                        ;Trim()的作用就是把“abc: haha”中haha的多余空白符消除
            value:=Trim(infos_temp[A_Index].Value[2], " `t`r`n`v`f")

            ;“Set-Cookie”是可以一次返回多条的。
            if (name="Set-Cookie")
                infos[name].=value . "`r`n"
            else
                infos[name]:=value
        }

        return, infos
    }

    /*
    在“GetAllResponseHeaders”中,“Set-Cookie”可能一次存在多个,比如“Set-Cookie:name=a; domain=xxx.com `r`n Set-Cookie:name=b; domain=www.xxx.com”。
    之后向服务器发送cookie的时候,会先验证domain,再验证path,两者都成功,再发送所有符合条件的cookies。
    domain的匹配方式是从字符串的尾部开始比较。
    path的匹配方式是从头开始逐字符串比较(例如/blog与/blog、/blogrool等等都匹配)。需要注意的是,path只在domain完成匹配后才比较。
    当下次访问“www.xxx.com”时,由于有2个符合条件的cookie,所以发送给服务器的cookie应该是“name=b; name=a”。
    当下次访问“xxx.com”时,由于只有1个符合条件的cookie,所以发送给服务器的cookie应该是“name=a”。
    规则是,path越详细,越靠前。domain越详细,越靠前(domain和path加起来就是网址了)。
    另外需要注意的是,“Set-Cookie”中没有domain或者path的话,则以当前url为准。
    如果要覆盖一个已有的cookie值,那么需要创建一个name、domain、path,完全相同的“Set-Cookie”(name就是“cookie:name=value; path=/”中的name)。
    当一个cookie存在,并且可选条件允许的话,该cookie的值会在接下来的每个请求中被发送至服务器。
    其值被存储在名为Cookie的HTTP消息头中,并且只包含了cookie的值,其它的选项全部被去除(expires,domain,path,secur​e全部没有了)。
    如果在指定的请求中有多个cookies,那么它们会被分号和空格分开,例如:(Cookie:value1 ; value2 ; name1=value1)
    在没有expires选项时,cookie的寿命仅限于单一的会话中。浏览器的关闭意味这一次会话的结束,所以会话cookie只存在于浏览器保持打开的状态之​下。
    如果expires选项设置了一个过去的时间点,那么这个cookie会被立即删除。
    最后一个选项是secure。不像其它选项,该选项只是一个标记并且没有其它的值。
    “http://my.oschina.net/hmj/blog/69638” 参考答案。
    要想做到完全如浏览器般自动管理cookies,每个链接发对应的cookie,难度颇大。模拟登录什么的,可以一步一步提取所需cookie再发送给服务器。​综合考虑,暂时不写自动管理。
    */
    管理cookie(cookie)
    {
        return
    }
}


附件
.zip  WinHttp.zip (大小: 11.65 KB / 下载: 168)
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
[+] 15用户表示感谢兔子
2015-06-04, 15 : 26
RE: 新库 winhttp 可以 下载到文件 下载到变量 向网站传数据 存取cookie 不会卡 不会死 支持超时 支持代理 速度快!!!!!!还有超级详细的例子!!!!!!
请问当正在下载的时候
如何判断下载文件已经下载的大小

我的GitHub地址
访问这个用户的网站 查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2015-06-28, 21 : 16 (这个帖子最后修改于: 2015-06-28 21 : 32 by June.)
RE: 新库 winhttp 可以 下载到文件 下载到变量 向网站传数据 存取cookie 不会卡 不会死 支持超时 支持代理 速度快!!!!!!还有超级详细的例子!!!!!!
@兔子
ADO.SaveToFile(FilePath,2) ;文件存在则覆盖
如不想覆盖文件怎么改?参数改成1也是覆盖的..
例如:
使用URLDownloadToFile
下载,新库.ahk (1MB)
下载,新库.ahk (5MB)
名字相同时,会覆盖,但是容量内容是不一样的。
下载时不覆盖,应该如何设置?
例如:
当名字相同时,不覆盖,而是添加一个序号。
新库.ahk 新库(1).ahk
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2015-06-28, 22 : 17
RE: 新库 winhttp 可以 下载到文件 下载到变量 向网站传数据 存取cookie 不会卡 不会死 支持超时 支持代理 速度快!!!!!!还有超级详细的例子!!!!!!
兔子已经超神了!

One for all, but man for himself

帮推广:AHK知乎专栏
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2015-07-14, 10 : 21
RE: 新库 winhttp 可以 下载到文件 下载到变量 向网站传数据 存取cookie 不会卡 不会死 支持超时 支持代理 速度快!!!!!!还有超级详细的例子!!!!!!
太赞了,正好有这方面的需求。谢谢。
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2015-08-08, 01 : 45 (这个帖子最后修改于: 2015-08-09 12 : 31 by tmplinshi.)
RE: 新库 winhttp 可以 下载到文件 下载到变量 向网站传数据 存取cookie 不会卡 不会死 支持超时 支持代理 速度快!!!!!!还有超级详细的例子!!!!!!
我要分享一个非常重要的说明——

WebRequest.WaitForResponse(超时秒数)
默认情况下,这个超时秒数并不是你设置为空就一直等待,设置 60 就等待 60 秒。而是要受限于默认的超时设置。

默认的超时设置为:
  • 解析超时: 0 秒
  • 连接超时: 60 秒
  • 发送超时: 30 秒
  • 接收超时: 30 秒

WaitForResponse 应该是指接收超时吧。所以呢,默认的话即使你设置 WaitForResponse(60) 实际上还是最多就等待 30 秒。。

默认值可以通过 WebRequest.SetTimeouts(解析超时, 连接超时, 发送超时, 接收超时) 来设置,详见 MSDN 的说明。比如把接收超时修改为 120 秒 —— WebRequest.SetTimeouts(0, 60000, 30000, 120000)

这点没有明白可把我害惨了。最近写的一个查询软件,经常查询失败。我原本以为是网站无响应,因为我没有设置超时,以为软件会一直等待(但是上面说了,“一直等待”会受限于​默认的最大超时)。后来仔细看抓包数据,看到每次都 30 秒超时返回。而用浏览器测试却正常,在 40 多秒的时候返回了结果,这才发觉是软件不对劲。

希望更多人知道这点说明,有空我还要再发几个帖子说明这一点。。另外@兔子 你也可以修改下代码,如果传递的超时超过了默认的 30 秒则调用一下 SetTimeouts。
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
[+] 1用户表示感谢tmplinshi
2015-09-11, 12 : 55 (这个帖子最后修改于: 2015-09-11 13 : 29 by 兔子.)
RE: 新库 winhttp 可以 下载到文件 下载到变量 向网站传数据 存取cookie 不会卡 不会死 支持超时 支持代理 速度快!!!!!!还有超级详细的例子!!!!!!
(2015-06-28 21 : 16)June 提到:  @兔子
ADO.SaveToFile(FilePath,2) ;文件存在则覆盖
如不想覆盖文件怎么改?参数改成1也是覆盖的..
例如:
使用URLDownloadToFile
下载,新库.ahk (1MB)
下载,新库.ahk (5MB)
名字相同时,会覆盖,但是容量内容是不一样的。
下载时不覆盖,应该如何设置?
例如:
当名字相同时,不覆盖,而是添加一个序号。
新库.ahk 新库(1).ahk
这个问题不应该直接改库,而应该在下载前,对文件是否存在进行检测。如果存在,则在参数“FilePath”中改名实现。

@tmplinshi

我说有时候怎么超时很诡异,原来是这样……

多谢分享,已经修改。
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
[+] 3用户表示感谢兔子
2015-10-08, 23 : 26
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
支持存取“Cookie”,可用于模拟登录状态。
这个貌似没有实现啊,我想知道怎么保存所需要的数据COOKIE数据可以从哪方面入手获取,有的时候需要通过获取到的值来进行修改呢
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2015-10-12, 09 : 46 (这个帖子最后修改于: 2015-10-12 10 : 00 by 兔子.)
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
(2015-10-08 23 : 26)qingyuan0o0 提到:  支持存取“Cookie”,可用于模拟登录状态。
这个貌似没有实现啊,我想知道怎么保存所需要的数据COOKIE数据可以从哪方面入手获取,有的时候需要通过获取到的值来进行修改呢
每次下载操作后,“ResponseHeaders”这个变量中存储的就是每次访问网址时,服务器返回的“ResponseHeaders”。当然,它已经被解析成对象了​,方便直接使用。
不需要的时候,可以不用管它。需要的时候,则在下载网址后,紧接着读取这个对象就行了。
例如“obj:=WinHttp.ResponseHeaders”,此时obj中就包含了刚才访问网址时服务器返回的所有“ResponseHeaders”。
于是“MsgBox, % obj["Content-type"]”,就得到了“Content-type”。
于是“MsgBox, % obj["Set-Cookie"]”,就得到了“Set-Cookie”。
上面的“Set-Cookie”,就是cookie。

代码: (全选)
;~ 所以整体来说就是
WinHttp.UrlDownloadToVar("http://www.baidu.com")
obj:=WinHttp.ResponseHeaders
MsgBox, % obj["Set-Cookie"]
MsgBox, 上面那个就是cookie
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
[+] 1用户表示感谢兔子
2015-10-19, 11 : 05 (这个帖子最后修改于: 2015-10-19 12 : 37 by zhangnew.)
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
兄弟,很棒!
访问这个用户的网站 查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2015-10-24, 12 : 16
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
(2015-10-12 09 : 46)兔子 提到:  
(2015-10-08 23 : 26)qingyuan0o0 提到:  支持存取“Cookie”,可用于模拟登录状态。
这个貌似没有实现啊,我想知道怎么保存所需要的数据COOKIE数据可以从哪方面入手获取,有的时候需要通过获取到的值来进行修改呢
每次下载操作后,“ResponseHeaders”这个变量中存储的就是每次访问网址时,服务器返回的“ResponseHeaders”。当然,它已经被解析成对象了​,方便直接使用。
不需要的时候,可以不用管它。需要的时候,则在下载网址后,紧接着读取这个对象就行了。
例如“obj:=WinHttp.ResponseHeaders”,此时obj中就包含了刚才访问网址时服务器返回的所有“ResponseHeaders”。
于是“MsgBox, % obj["Content-type"]”,就得到了“Content-type”。
于是“MsgBox, % obj["Set-Cookie"]”,就得到了“Set-Cookie”。
上面的“Set-Cookie”,就是cookie。

代码: (全选)
;~ 所以整体来说就是
WinHttp.UrlDownloadToVar("http://www.baidu.com")
obj:=WinHttp.ResponseHeaders
MsgBox, % obj["Set-Cookie"]
MsgBox, 上面那个就是cookie

那如果cookies的内容在接下来有变动,在登陆前和登录后内容值有增加这个情况直接替换就可以了吗?
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2015-10-25, 11 : 10
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
(2015-10-24 12 : 16)qingyuan0o0 提到:  
(2015-10-12 09 : 46)兔子 提到:  
(2015-10-08 23 : 26)qingyuan0o0 提到:  支持存取“Cookie”,可用于模拟登录状态。
这个貌似没有实现啊,我想知道怎么保存所需要的数据COOKIE数据可以从哪方面入手获取,有的时候需要通过获取到的值来进行修改呢
每次下载操作后,“ResponseHeaders”这个变量中存储的就是每次访问网址时,服务器返回的“ResponseHeaders”。当然,它已经被解析成对象了​,方便直接使用。
不需要的时候,可以不用管它。需要的时候,则在下载网址后,紧接着读取这个对象就行了。
例如“obj:=WinHttp.ResponseHeaders”,此时obj中就包含了刚才访问网址时服务器返回的所有“ResponseHeaders”。
于是“MsgBox, % obj["Content-type"]”,就得到了“Content-type”。
于是“MsgBox, % obj["Set-Cookie"]”,就得到了“Set-Cookie”。
上面的“Set-Cookie”,就是cookie。

代码: (全选)
;~ 所以整体来说就是
WinHttp.UrlDownloadToVar("http://www.baidu.com")
obj:=WinHttp.ResponseHeaders
MsgBox, % obj["Set-Cookie"]
MsgBox, 上面那个就是cookie

那如果cookies的内容在接下来有变动,在登陆前和登录后内容值有增加这个情况直接替换就可以了吗?
在你登录成功后,页面返回时也会返回cookie,把这个cookie存下来,之后的操作作为参数传进去。
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2015-11-26, 16 : 41
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
@兔子 能不能有下载的文件大小显示和进度显示?
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2016-01-13, 15 : 14
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
很强大,需要点时间来琢磨和消化
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2016-02-07, 20 : 37
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
太好用了!
极品AHK代码!
谢谢!
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2017-02-02, 11 : 23
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
请问如何知道网页上文件的格式呢?
比如我保存图片,我不知道网页上的是jpg还是png
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2017-02-28, 16 : 54
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
(2015-10-12 09 : 46)兔子 提到:  
(2015-10-08 23 : 26)qingyuan0o0 提到:  支持存取“Cookie”,可用于模拟登录状态。
这个貌似没有实现啊,我想知道怎么保存所需要的数据COOKIE数据可以从哪方面入手获取,有的时候需要通过获取到的值来进行修改呢
每次下载操作后,“ResponseHeaders”这个变量中存储的就是每次访问网址时,服务器返回的“ResponseHeaders”。当然,它已经被解析成对象了​,方便直接使用。
不需要的时候,可以不用管它。需要的时候,则在下载网址后,紧接着读取这个对象就行了。
例如“obj:=WinHttp.ResponseHeaders”,此时obj中就包含了刚才访问网址时服务器返回的所有“ResponseHeaders”。
于是“MsgBox, % obj["Content-type"]”,就得到了“Content-type”。
于是“MsgBox, % obj["Set-Cookie"]”,就得到了“Set-Cookie”。
上面的“Set-Cookie”,就是cookie。

代码: (全选)
;~ 所以整体来说就是
WinHttp.UrlDownloadToVar("http://www.baidu.com")
obj:=WinHttp.ResponseHeaders
MsgBox, % obj["Set-Cookie"]
MsgBox, 上面那个就是cookie

多个cookie是怎么获取的呢?验证码在cookies中,可以直接提取?试了很久,还是不太会用这个函数
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
2018-06-06, 00 : 08
RE: 9.12更新 新库 winhttp 可以 下载到文件 到变量 向网站传数据 存取cookie 获取状态码 不会卡 不会死 支持超时 支持代理 速度快!还有超级详细的例子!
(2015-03-26 10 : 07)兔子 提到:  
代码: (全选)
            if (k="Cookie")
            {
                WebRequest.SetRequestHeader("Cookie","tuzi")    ;先设置一个cookie,防止出错,msdn推荐这么做
                WebRequest.SetRequestHeader("Cookie",v)
            }
            WebRequest.SetRequestHeader(k,v)
这里会造成 cookie 被设置两次。应该加一个 else:
代码: (全选)
            else
                WebRequest.SetRequestHeader(k,v)
查找这个用户的全部帖子
表示感谢 引用并回复 移动视图置页面顶端
发表回复 


论坛跳转:


联系我们 | Autohotkey 中文站 | 回到顶部 | 回到正文区 | 精简(归档)模式 | RSS 聚合