在S1死程群@kula的鼓勵下,我開始使用kula提供的api來操作那個傻逼的“鳥窩”網站(https://www.niaowo.me)。不過由於我自己在業余時間寫的程序都喜歡用C++和Windows API,因此我琢磨了幾天,真的讓我用C++給寫了出來。
我寫了一個HttpUtility庫來實現C++操作http/https服務的功能,這份代碼可以在這裡獲得:
使用的時候很簡單,只需要HttpRequest裡面填滿了參數,然後就可以用HttpQuery參數獲得一個HttpResponse類型,這個類型裡面寫滿了http服務器的返回值、返回內容和cookie等的數據。譬如說用來post來登陸鳥窩,然後拿到cookie之後查詢首頁的所有帖子,大概就可以這麼寫:
WString NestleGetSession(const WString& username, const WString& password, const WString& apiKey, const WString& apiSecret)
{
WString body=L"api_key="+apiKey+L"&api_secret="+apiSecret+L"&username="+username+L"&password="+password;
HttpRequest request;
HttpResponse response;
request.SetHost(L"https://www.niaowo.me/account/token/");
request.method=L"POST";
request.contentType=L"application/x-www-form-urlencoded";
request.SetBodyUtf8(body);
HttpQuery(request, response);
if(response.statusCode==200)
{
return response.cookie;
}
else
{
return L"";
}
}
WString NestleGetXml(const WString& path, const WString& cookie)
{
HttpRequest request;
HttpResponse response;
request.SetHost(L"https://www.niaowo.me"+path+L".xml");
request.method=L"GET";
request.cookie=cookie;
request.acceptTypes.Add(L"application/xml");
HttpQuery(request, response);
if(response.statusCode==200)
{
return response.GetBodyUtf8();
}
else
{
return L"";
}
}
於是我們終於獲得了一個保存在vl::WString的xml字符串了,那怎麼辦呢?這個時候需要出動IXMLDOMDocument2來解析我們的xml。只要裝了IE的計算機上都是有IXMLDOMDocument2的,而不裝IE的Windows PC是不存在的,因此我們總是可以大膽的使用。當然,用IXMLDOMDocument直接來遍歷什麼東西特別的慢,所以我們需要的是xpath。xpath對於xml就跟regex對於字符串一樣,可以直接查詢出我們要的東西。首先看一下如何操作IXMLDOMDocument2接口:
IXMLDOMNodeList* XmlQuery(IXMLDOMNode* pDom, const WString& xpath)
{
IXMLDOMNodeList* nodes=0;
BSTR xmlQuery=SysAllocString(xpath.Buffer());
if(xmlQuery)
{
HRESULT hr=pDom->selectNodes(xmlQuery, &nodes);
if(FAILED(hr))
{
nodes=0;
}
SysFreeString(xmlQuery);
}
return nodes;
}
WString XmlReadString(IXMLDOMNode* node)
{
WString result;
BSTR text=0;
HRESULT hr=node->get_text(&text);
if(SUCCEEDED(hr))
{
const wchar_t* candidateItem=text;
result=candidateItem;
SysFreeString(text);
}
return result;
}
void XmlReadMultipleStrings(IXMLDOMNodeList* textNodes, List<WString>& candidates, int max)
{
candidates.Clear();
while((int)candidates.Count()<max)
{
IXMLDOMNode* textNode=0;
HRESULT hr=textNodes->nextNode(&textNode);
if(hr==S_OK)
{
candidates.Add(XmlReadString(textNode));
textNode->Release();
}
else
{
break;
}
}
}
IXMLDOMDocument2* XmlLoad(const WString& content)
{
IXMLDOMDocument2* pDom=0;
HRESULT hr=CoCreateInstance(__uuidof(DOMDocument60), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDom));
if(SUCCEEDED(hr))
{
pDom->put_async(VARIANT_FALSE);
pDom->put_validateOnParse(VARIANT_FALSE);
pDom->put_resolveExternals(VARIANT_FALSE);
BSTR xmlContent=SysAllocString(content.Buffer());
if(xmlContent)
{
VARIANT_BOOL isSuccessful=0;
hr=pDom->loadXML(xmlContent, &isSuccessful);
if(!(SUCCEEDED(hr) && isSuccessful==VARIANT_TRUE))
{
pDom->Release();
pDom=0;
}
SysFreeString(xmlContent);
}
}
return pDom;
}
有了這幾個函數之後,我們就可以干下面的事情,譬如說從鳥窩首頁下載第一頁的所有topic的標題:
WString xml=NestleGetXml(L”/topics”, cookie);
IXMLDOMDocument2* pDom=XmlLoad(xml);
List<WString> titles;
IXMLNodeList* nodes=XmlQuery(pDom, L”/hash/topics/topic/title/text()”);
XmlReadMultipleStrings(nodes, titles, 100);
為什麼上面的xpath是hash/topics/topic/title/text()呢?因為這個xml的內容大概類似於:
<hash>
<topics>
<topic>
<title>TITLE</title>
…
剩下的大家就去看代碼吧。這個故事告訴我們,只要有一個合適的封裝,C++寫起這些本來應該讓C#來寫的東西也不是那麼的煩人的,啊哈哈哈哈。