标题:XMLHTTPRequest 中文乱码的出来方法

-------------------------------------------------------------------------------------------------------------------------------

时间:2011/12/20 13:04:03

-------------------------------------------------------------------------------------------------------------------------------

内容:

 XMLHTTPRequest

评论 发表评论
大家经常需要将网站上面的数据下载或导入到Excel中进行汇总分析,例如股票数据等,这样可以充分利用Excel强大的数据分析功能。在 Excel中下载网页数据的方法有很多种,包括WebBrowser控件、控制IE、Web查询、API函数,这里主要介绍XML库的 XMLHTTPRequest对象。

使用XMLHTTPRequest对象的好处是不需要打开浏览器而在后台下载数据,这样可以保持干净的界面,另外能够很方便的使用POST方法递交查询表单的数据,同时XMLHTTPRequest对象可以处理各种类型的数据,例如javascript数据、json数据、xml数据以及各种图像数据等,特别是对于一些Web服务,使用XMLHTTPRequest是很好的选择。但XMLHTTPRequest对象一定程度上依赖于网页的源代码,如果源代码发生变化,处理数据的方法也需要相应地做出改变。

在前面的文章中已经介绍过XMLHTTPRequest对象的方法和属性,这里主要介绍使用XMLHTTPRequest对象的一般流程,以新浪的天气预报查询做为例子。


1. 首先分析网页源代码,获得所需要递交的数据,这个步骤很关键,需要了解部分HTML和脚本知识
查看网页源代码时,主要是分析要递交的表单(Form元素)包含哪些Input元素、处理表单的URL地址、表单的递交方法。
例如,打开新浪天气的网页http://php.weather.sina.com.cn/widget/search_middle.php,可以看到下面这个输入框,这就是一个查询表单。

在源代码中搜索“搜索城市天气”,可以找到这个form元素的代码:

<form name="city" method="get" action="http://php.weather.sina.com.cn/search.php" style="">
 
<span>搜索城市天气:</span><input type="text" name="city" value="输入国别、地名、拼音、电
 
话区号均可" onclick="javascript:if(this.value=='输入国别、地名、拼音、电话区号均可')this.value='';" class="city"
 
style="width:220px;" /> <input type="hidden" name="f" value="1" /><input type="hidden" name="dpc" value="1" /><input
 
type="submit" class="cityBtn" value="搜索" style="width:50px;" />
</form>
根据这个代码,从form元素的action属性可以得到处理表单的网址是“http://php.weather.sina.com.cn /search.php”,从method属性可以得到数据递交的方法是“get”,根据其中input元素的name和value属性可以知道需要递交的数据一共有三个,其中city可以使用地名、拼音和电话区号,服务器端可以自动处理。

city=东莞
f=1
dpc=1
2. 创建HTTPRequest对象
使用前绑定的话需要引用XML库,如图:


Dim httpRequest As MSXML2.XMLHTTP30
Set httpRequest = New MSXML2.XMLHTTP30
或者使用后绑定:

Dim httpRequest As Object
Set httpRequest = CreateObject("MSXML2.XMLHTTP")
3. 使用XMLHTTPRequest对象建立与指定的网页URL的连接
根据前面获得的Input元素和递交方法,Open方法使用的参数有些不同。
首先将需要递交的数据用符号&连接起来,不限制顺序,例如“city=东莞&f=1&dpc=1”和“dpc=1&city=东莞&f=1”均可。

如果使用GET方法,将这个数据和表单处理网址用符号?连接起来。例如:

httpRequest.Open "GET", "http://php.weather.sina.com.cn/search.php?city=东莞&amp;f=1&amp;dpc=1", False
如果使用POST方法,则在send方法中发送递交数据。

httpRequest.Open "GET", "http://php.weather.sina.com.cn/search.php", False
httpRequest.Send "city=东莞&amp;f=1&amp;dpc=1"
4. 设置HTTP的头部信息
一般来说只需要设置Content-Type的头部信息就可以了。对于一般的网页内容,设置Content-Type为“text/html”,也可以省略。例如:

httpRequest.setRequestHeader "Content-Type", "text/html"
某些情况下,需要设置为“application/x-www-form-urlencoded”,例如:

httpRequest.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
在实时更新数据的时候,有时每次查询会返回第一次查询后保存在缓存中的数据,可以设置HTTP的If-Modified-Since头部信息,这样就保证每次下载的都是最新的数据。例如:

httpRequest.setRequestHeader "If-Modified-Since", Format(Now, "[$-F800]dddd, mmmm dd, yyyy") &amp; " GMT"
5. 发送信息
如果是GET方法,发送空字符串,例如

httpRequest.Send ""
或者

httpRequest.Send
如果使用POST方法,则发生递交的数据,例如:

httpRequest.Send "city=东莞&amp;f=1&amp;dpc=1"
6. 处理返回的内容
根据XMLHTTPRequest对象的Status属性判断是否访问成功,如果成功则处理HTTPRequest对象的返回内容。

If httpRequest.Status = 200 Then
txtContent = httpRequest.responseText
' ...
End If
HTTPRequest对象返回的信息由三个属性表示:responseText、responseXML和responseBody。

这里需要注意中文网页的内容,如果返回的网页源代码的编码不是UTF-8的话,由responseText属性表示返回的内容中,中文部分将会是乱码,这时需要处理responseBody属性来获得正确的内容。

网上有些编码转换的函数(例如下面这个bytes2BSTR函数)在英文操作系统上不能使用。

Function bytes2BSTR(arrBytes) '编码转换
Dim strReturn As String
Dim ThisCharCode As String
Dim NextCharCode As String
Dim i As Long
 
strReturn = ""
arrBytes = CStr(arrBytes)
For i = 1 To LenB(arrBytes)
ThisCharCode = AscB(MidB(arrBytes, i, 1))
If ThisCharCode &lt; &amp;H80 Then
strReturn = strReturn &amp; Chr(ThisCharCode)
Else
NextCharCode = AscB(MidB(arrBytes, i + 1, 1))
strReturn = strReturn &amp; Chr(CLng(ThisCharCode) * &amp;H100 + CInt(NextCharCode))
i = i + 1
End If
Next i
bytes2BSTR = strReturn
End Function
在英文版操作系统中,如果Office是中文的话,可以直接使用下面的StrConv方法来转换。
StrConv(httpRequest.responseBody, vbUnicode)

上面这个StrConv方法在英文版Office中不能正确使用。比较安全的方法是使用API函数MultiByteToWideChar,这样可以正确转换编码。

Public Declare Function MultiByteToWideChar Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, ByRef lpMultiByteStr As Any, ByVal cchMultiByte As Long, ByVal lpWideCharStr As Long, ByVal cchWideChar As Long) As Long
Sub GetWeather(strCity As String)
Dim httpRequest As MSXML2.XMLHTTP30
Dim txtContent As String
Dim ReturnByte() As Byte
Dim lngBufferSize As Long
Dim strBuffer As String
Dim lngResult As Long
 
If Trim(strCity) = "" Then Exit Sub
strQuery = "http://php.weather.sina.com.cn/search.php?" &amp; "city=" &amp; strCity &amp; "&amp;f=1&amp;dpc=1"
Set httpRequest = New MSXML2.XMLHTTP30
httpRequest.Open "GET", strQuery, False
httpRequest.setRequestHeader "Content-Type", "text/html"
httpRequest.send ""
If httpRequest.Status = 200 Then
ReturnByte = httpRequest.responseBody
lngBufferSize = (UBound(ReturnByte) + 1) * 2
strBuffer = String$(lngBufferSize, vbNullChar)
lngResult = MultiByteToWideChar(936, 0, ReturnByte(0), lngBufferSize / 2, StrPtr(strBuffer), lngBufferSize)
txtContent = Left(strBuffer, lngResult)
另外,如果返回的内容是有效的格式,例如RSS Feed或者一些Web服务等,还可以使用XML库的DOMDocument对象来解析HTTPRequest对象的responseXML属性内容。

Set objXMLHTTP = New MSXML2.XMLHTTP30
objXMLHTTP.Open "GET", strFeedURL, False
objXMLHTTP.setRequestHeader "Content-type", "text/html"
objXMLHTTP.send
If objXMLHTTP.Status = 200 Then
Set objXML = New MSXML2.DOMDocument30
objXML.async = False
bRet = objXML.Load(objXMLHTTP.responseXML)
7. 如果有网页内容需要翻页的话,可以根据前面获取的内容判断总页数,然后重复使用Open和Send方法来获取所有的数据
8. 注销httpRequest对象

 XMLHTTPRequest

评论 发表评论
大家经常需要将网站上面的数据下载或导入到Excel中进行汇总分析,例如股票数据等,这样可以充分利用Excel强大的数据分析功能。在 Excel中下载网页数据的方法有很多种,包括WebBrowser控件、控制IE、Web查询、API函数,这里主要介绍XML库的 XMLHTTPRequest对象。

使用XMLHTTPRequest对象的好处是不需要打开浏览器而在后台下载数据,这样可以保持干净的界面,另外能够很方便的使用POST方法递交查询表单的数据,同时XMLHTTPRequest对象可以处理各种类型的数据,例如javascript数据、json数据、xml数据以及各种图像数据等,特别是对于一些Web服务,使用XMLHTTPRequest是很好的选择。但XMLHTTPRequest对象一定程度上依赖于网页的源代码,如果源代码发生变化,处理数据的方法也需要相应地做出改变。

在前面的文章中已经介绍过XMLHTTPRequest对象的方法和属性,这里主要介绍使用XMLHTTPRequest对象的一般流程,以新浪的天气预报查询做为例子。


1. 首先分析网页源代码,获得所需要递交的数据,这个步骤很关键,需要了解部分HTML和脚本知识
查看网页源代码时,主要是分析要递交的表单(Form元素)包含哪些Input元素、处理表单的URL地址、表单的递交方法。
例如,打开新浪天气的网页http://php.weather.sina.com.cn/widget/search_middle.php,可以看到下面这个输入框,这就是一个查询表单。

在源代码中搜索“搜索城市天气”,可以找到这个form元素的代码:

<form name="city" method="get" action="http://php.weather.sina.com.cn/search.php" style="">
 
<span>搜索城市天气:</span><input type="text" name="city" value="输入国别、地名、拼音、电
 
话区号均可" onclick="javascript:if(this.value=='输入国别、地名、拼音、电话区号均可')this.value='';" class="city"
 
style="width:220px;" /> <input type="hidden" name="f" value="1" /><input type="hidden" name="dpc" value="1" /><input
 
type="submit" class="cityBtn" value="搜索" style="width:50px;" />
</form>
根据这个代码,从form元素的action属性可以得到处理表单的网址是“http://php.weather.sina.com.cn /search.php”,从method属性可以得到数据递交的方法是“get”,根据其中input元素的name和value属性可以知道需要递交的数据一共有三个,其中city可以使用地名、拼音和电话区号,服务器端可以自动处理。

city=东莞
f=1
dpc=1
2. 创建HTTPRequest对象
使用前绑定的话需要引用XML库,如图:


Dim httpRequest As MSXML2.XMLHTTP30
Set httpRequest = New MSXML2.XMLHTTP30
或者使用后绑定:

Dim httpRequest As Object
Set httpRequest = CreateObject("MSXML2.XMLHTTP")
3. 使用XMLHTTPRequest对象建立与指定的网页URL的连接
根据前面获得的Input元素和递交方法,Open方法使用的参数有些不同。
首先将需要递交的数据用符号&连接起来,不限制顺序,例如“city=东莞&f=1&dpc=1”和“dpc=1&city=东莞&f=1”均可。

如果使用GET方法,将这个数据和表单处理网址用符号?连接起来。例如:

httpRequest.Open "GET", "http://php.weather.sina.com.cn/search.php?city=东莞&amp;f=1&amp;dpc=1", False
如果使用POST方法,则在send方法中发送递交数据。

httpRequest.Open "GET", "http://php.weather.sina.com.cn/search.php", False
httpRequest.Send "city=东莞&amp;f=1&amp;dpc=1"
4. 设置HTTP的头部信息
一般来说只需要设置Content-Type的头部信息就可以了。对于一般的网页内容,设置Content-Type为“text/html”,也可以省略。例如:

httpRequest.setRequestHeader "Content-Type", "text/html"
某些情况下,需要设置为“application/x-www-form-urlencoded”,例如:

httpRequest.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
在实时更新数据的时候,有时每次查询会返回第一次查询后保存在缓存中的数据,可以设置HTTP的If-Modified-Since头部信息,这样就保证每次下载的都是最新的数据。例如:

httpRequest.setRequestHeader "If-Modified-Since", Format(Now, "[$-F800]dddd, mmmm dd, yyyy") &amp; " GMT"
5. 发送信息
如果是GET方法,发送空字符串,例如

httpRequest.Send ""
或者

httpRequest.Send
如果使用POST方法,则发生递交的数据,例如:

httpRequest.Send "city=东莞&amp;f=1&amp;dpc=1"
6. 处理返回的内容
根据XMLHTTPRequest对象的Status属性判断是否访问成功,如果成功则处理HTTPRequest对象的返回内容。

If httpRequest.Status = 200 Then
txtContent = httpRequest.responseText
' ...
End If
HTTPRequest对象返回的信息由三个属性表示:responseText、responseXML和responseBody。

这里需要注意中文网页的内容,如果返回的网页源代码的编码不是UTF-8的话,由responseText属性表示返回的内容中,中文部分将会是乱码,这时需要处理responseBody属性来获得正确的内容。

网上有些编码转换的函数(例如下面这个bytes2BSTR函数)在英文操作系统上不能使用。

Function bytes2BSTR(arrBytes) '编码转换
Dim strReturn As String
Dim ThisCharCode As String
Dim NextCharCode As String
Dim i As Long
 
strReturn = ""
arrBytes = CStr(arrBytes)
For i = 1 To LenB(arrBytes)
ThisCharCode = AscB(MidB(arrBytes, i, 1))
If ThisCharCode &lt; &amp;H80 Then
strReturn = strReturn &amp; Chr(ThisCharCode)
Else
NextCharCode = AscB(MidB(arrBytes, i + 1, 1))
strReturn = strReturn &amp; Chr(CLng(ThisCharCode) * &amp;H100 + CInt(NextCharCode))
i = i + 1
End If
Next i
bytes2BSTR = strReturn
End Function
在英文版操作系统中,如果Office是中文的话,可以直接使用下面的StrConv方法来转换。
StrConv(httpRequest.responseBody, vbUnicode)

上面这个StrConv方法在英文版Office中不能正确使用。比较安全的方法是使用API函数MultiByteToWideChar,这样可以正确转换编码。

Public Declare Function MultiByteToWideChar Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, ByRef lpMultiByteStr As Any, ByVal cchMultiByte As Long, ByVal lpWideCharStr As Long, ByVal cchWideChar As Long) As Long
Sub GetWeather(strCity As String)
Dim httpRequest As MSXML2.XMLHTTP30
Dim txtContent As String
Dim ReturnByte() As Byte
Dim lngBufferSize As Long
Dim strBuffer As String
Dim lngResult As Long
 
If Trim(strCity) = "" Then Exit Sub
strQuery = "http://php.weather.sina.com.cn/search.php?" &amp; "city=" &amp; strCity &amp; "&amp;f=1&amp;dpc=1"
Set httpRequest = New MSXML2.XMLHTTP30
httpRequest.Open "GET", strQuery, False
httpRequest.setRequestHeader "Content-Type", "text/html"
httpRequest.send ""
If httpRequest.Status = 200 Then
ReturnByte = httpRequest.responseBody
lngBufferSize = (UBound(ReturnByte) + 1) * 2
strBuffer = String$(lngBufferSize, vbNullChar)
lngResult = MultiByteToWideChar(936, 0, ReturnByte(0), lngBufferSize / 2, StrPtr(strBuffer), lngBufferSize)
txtContent = Left(strBuffer, lngResult)
另外,如果返回的内容是有效的格式,例如RSS Feed或者一些Web服务等,还可以使用XML库的DOMDocument对象来解析HTTPRequest对象的responseXML属性内容。

Set objXMLHTTP = New MSXML2.XMLHTTP30
objXMLHTTP.Open "GET", strFeedURL, False
objXMLHTTP.setRequestHeader "Content-type", "text/html"
objXMLHTTP.send
If objXMLHTTP.Status = 200 Then
Set objXML = New MSXML2.DOMDocument30
objXML.async = False
bRet = objXML.Load(objXMLHTTP.responseXML)
7. 如果有网页内容需要翻页的话,可以根据前面获取的内容判断总页数,然后重复使用Open和Send方法来获取所有的数据
8. 注销httpRequest对象