一个C++解析HTML的库

HTTP协议使用广泛,相应的,C++在这块需求也开始增加。一个好的解析库可以达到事半功倍的效果,在此贴出我的解析库的代码,方便新手朋友们使用。

hHttpParse.h

#ifndef __H_HTML_PARSE_H__
#define __H_HTML_PARSE_H__

#pragma once

#include <Windows.h>
#include <WinInet.h>
#include <string>
#include <cstdio>

class hHtmlParse {
	std::string data;
	int p;
public:
	//构造函数,传入HTML代码
	hHtmlParse (std::string& data);
	//获取网页的编码方式
	bool GetCharset (std::string& s);
	//设置当前解析位置
	bool SetPos (const char* find);
	//设置当前解析位置(反向查找目标位置)
	bool SetPos_LastOf (const char* find);
	//查找是否存在目标位置,不会更新当前位置
	bool find (const char* find);
	//匹配一串字符串,使用sscanf_s获取
	bool MatchString (const char* match, std::string& s);
	//获取当前电脑IP地址
	static bool GetLocalIp (std::string& ip);
	//查询某地址或某域名信息
	static bool GetAddrMessage (const wchar_t* addr, std::string& data);
	//关键函数,获取lpURL指向的地址的HTML代码,并存入data中
	static bool UrlGetHtml (LPCWSTR lpURL, std::string& data);
};

#endif //__H_HTML_PARSE_H__


hHttpParse.cpp

#include "hHtmlParse.h"

#pragma comment(lib, "WinInet.lib")

hHtmlParse::hHtmlParse (std::string& data) {
	this->data = data;
	this->p = 0;
}

bool hHtmlParse::GetCharset (std::string& s) {
	this->SetPos ("charset=");
	return this->MatchString ("%*[\"]%[^\"]", s);
}

bool hHtmlParse::SetPos (const char* find) {
	int t = this->data.find (find, p);
	if (-1 == t) return false;
	this->p = t + strlen (find);
	return true;
}

bool hHtmlParse::SetPos_LastOf (const char* find) {
	int t = this->data.rfind (find);
	if (-1 == t) return false;
	this->p = t + strlen (find);
	return true;
}

bool hHtmlParse::find (const char* find) {
	int t = this->data.find (find, p);
	return t != -1;
}

bool hHtmlParse::MatchString (const char* match, std::string& s) {
	return sscanf_s (&data.c_str () [p], match, const_cast<char*>(s.c_str ()), s.capacity ()) > 0;
}

bool hHtmlParse::GetLocalIp (std::string& ip) {
	std::string page, match;
	page.resize (512);
	match.resize(16);
	if (!hHtmlParse::UrlGetHtml (L"http://1111.ip138.com/ic.asp", page)) return false;
	hHtmlParse hp (page);
	hp.SetPos ("<center>");
	hp.MatchString ("%*[^0-9]%[0-9.]", match);
	ip.clear ();
	ip = match.c_str ();
	return true;
}

bool hHtmlParse::GetAddrMessage (const wchar_t* addr, std::string& data) {
	data.clear ();
	std::wstring link = L"http://www.ip138.com/ips138.asp?ip=";
	std::string page, match;
	link += addr;
	page.resize (16384);
	match.resize (64);
	if (!hHtmlParse::UrlGetHtml (link.c_str (), page)) return false;
	hHtmlParse hp (page);
	hp.SetPos_LastOf ("<table");
	hp.SetPos ("<td");
	hp.SetPos ("<td");
	bool b = true;
	if (hp.find (">>")) {
		b = false;
		hp.SetPos (">>");
		hp.MatchString ("%*[^0-9]%[0-9.]", match);
		data = match.c_str ();
	}
	hp.SetPos ("<ul");
	while (hp.find ("<li")) {
		hp.SetPos (":");
		hp.MatchString ("%[^<]", match);
		if (b) b = false; else data += "\n";
		data += match.c_str ();
	}
	return true;
}

bool hHtmlParse::UrlGetHtml (LPCWSTR lpURL, std::string& data) {
	HINTERNET hSession = InternetOpenW (L"EmotionSniffer", NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE);
	if (!hSession) return FALSE;
	HINTERNET hFile = InternetOpenUrlW (hSession, lpURL, NULL, NULL, INTERNET_FLAG_RELOAD, NULL);
	if (!hFile) {
		InternetCloseHandle (hSession); return FALSE;
	}
	DWORD dwW = 0, dwR = 0;
	int capacity = data.capacity ();
	do {
		dwW += dwR;
		if (dwW != 0 && dwR == 0) break;
		if (dwW + 1024 >= capacity) data.resize (capacity *= 2);
	} while (InternetReadFile (hFile, (LPVOID) (data.c_str () + dwW), 1024, &dwR));
	const_cast<char*>(data.c_str ()) [dwW] = '\0';
	InternetCloseHandle (hFile);
	InternetCloseHandle (hSession);
	return TRUE;
}

简要说明下使用方法。首先是封装的三个静态函数, GetLocalIp 和 GetAddrMessage 这俩是通过调用 www.ip138.com 动态查询获取的结果,调用这个库实现。使用这个库时可以参照上面两个函数的代码; UrlGetHtml 是通过 Windows 的 Internet API 实现从 URL 指定的地址下载网页。

重点说说这个库的使用方法,我就说说 hHtmlParse::GetLocalIp 则函数的实现,方法大多类似。

1、字符串定义

std::string page, match;
page.resize (512);
match.resize (16);

其中 page 用作保存 HTML 代码, match 用作保存匹配的字符串,也就是网页中需要获取的数据。由于访问的数据不大,所以 page 设置 512 字节足够。这儿也可以不用设置大小的, UrlGetHtml 实现的比较智能,可以自动扩展大小。设置一个大小只是可以减少内存 I/O ,提高执行速度。另外这儿也给 match 设置一个大小。对于IP地址来说,16字节足够了。

2、获取网页HTML代码

if (!hHtmlParse::UrlGetHtml (L"http://1111.ip138.com/ic.asp", page)) return false;
这句话的意思就是下载网页并将网页代码保存在 page 中,如果执行失败则返回。

3、创建解析对象

hHtmlParse hp (page);
这句代码用于创建一个解析对象,传入的数据为网页HTML字符串。
这里我们看看ip138的网页代码:

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=gb2312">
<title> 您的IP地址 </title>
</head>
<body style="margin:0px"><center>您的IP是:[123.144.*.*] 来自:XX市 联通</center></body></html>
地址和位置打码了,大家看得懂就行了。我们先来分析分析,需要获取地址的代码前面有一个 <center> 是吧?那就把位置设置在这儿吧。。。
4、设置当前解析位置

hp.SetPos ("<center>");
在这个解析库内部维护着一个字符指针,假如说网页前面的都解析过了,需要解析后面的,那就在解析时给字符指针赋值,然后下次解析时从字符指针位置开始解析,既方便网页处理,也提高解析速度,一举两得。这行代码的意思就是将内部维护的地址放在 <center> 这儿,下次调用时直接从这儿开始了。

5、获取匹配字符串,也就是IP地址

hp.MatchString ("%*[^0-9]%[0-9.]", match);
这儿就是获取匹配字符串的代码了 ,第一个参数为sscanf函数需要的那个字符串,match返回匹配的结果。简要说说这个字符串的意思,需要深究的自行bing。

%*[^0-9]   %*表示跳过,不匹配,[]表示匹配的内容,^表示非,0-9表示那十个数字。连起来的意思就是跳过所有不是0-9的字符。

%[0-9.]   %表示匹配,0-9.表示匹配的内容为那十个数字和小数点,一直到非0-9或小数点为止。

代码到这儿就已经获取了匹配的ip了,接下来的内容就是简单的处理了。

6、简单处理并返回

ip.clear ();
ip = match.c_str ();
return true;

由于匹配是通过 const_cast<char*>(s.c_str()) 赋值(这行代码可以在 hHtmlParse::MatchString 函数中找到),所以,在执行步骤6这段代码前,调用 match.length() 实际上返回的是不固定值,虽然不固定但是有数据。所以,假如非得在 const_cast<char*>(s.c_str()) 之后获取字符串长度,只能用lstrlen。

这几行代码大家应该都懂,简要说说第二行,意思是调用 std::string 的重载函数 operator=(char*) ,这样可以刷新 std::string 的长度,执行第二行代码之后,ip中的数据可以直接调用 length() 来获取长度了。

使用步骤描述完毕,同学们如果需要解析其他网页在相应地方修改就行了。另外, SetPos 和 MatchString 并不是只能调用一次的,只要找网页方便,并且保证对象内部维护的指针还没到末尾,就可以重复调用这些函数了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值