HTML解析-第二版(C/C++)

时间:2022-08-29 21:27:15

背景:
基于某些不着边际想法,只为取得HTML页面上的所有“URL”和“文本”,其它的内容都不在关心之列。
问题:
对于“文本”搜索,如果搜索了除英文以外的语言还好说些,如果要搜索的内容是英文本,
那么就难以区分是“标记”还是“本文”了。对于“URL”的搜索,因为“标记”就是英文,
这样就绕回到“对于‘文本’搜索”。另外字母的大小写,被转义的字符,引号,尖括号,都得处理。
例如:
<a href="http://www.csdn.net" >csdn</a>
<script src="http://csdnimg.cn/xxxxxxxx.js" type="text/javascript"></script>
想要搜索“csdn”这个字符串,直接以字符串遍历的法能搜索到3个,其实呢只希望搜索到1个。
例如:
<a href="http://bbs.csdn.net" >论坛</a>
<a href="http://bbs.csdn.net" >论 坛</a>
<a href="http://bbs.csdn.net" >论 坛</a>
想要搜索“论坛”这个字符串,按语义上讲,希望在搜索时能搜到3个。
但直接以字符串遍历的法能搜到1个,原因在于加了“空格”后的字符串,
计算机不知道对于人来讲意思并没有变。

总结:
1:直接搜索特定字符串,不多了就是少了。
2:尝试过MS的COM库,功能强大且齐全,但耗费的资源也相当多。
3:耳熟能详的搜索引擎也跑过几个回合,因没有耐心翻遍所有网页只好放弃。

结论:
只能把HTML页面完整的解析完毕才能达找到想到的东西,尽管不是全部,但情况要好很多。

方法:
HTML语句结构是:<a href="http://www.csdn.net" >aaaa</a> 或 <link href="/favicon.ico" />
等等一连串类似的语句组成,并且只有嵌套没有循环(脚本只能算上面提到的“文本”)。
分界符(这个词本人自己的称呼)使用的是“ <>""''=空格 ”,把两个分界符之间的内容看作一个链表节点,
“标记”a与“标记”/a是“父”节点与“子”节点的关系,“标记”a与“标记”href是“兄弟”节点的关系。
这样的好处是不用关心“标记”含义,就可以把整个页面解析成一个二维链表。
纵向可以遍历“标记”和“文本”,横向可以找到“文本”对应用“URL”。
当然实际情况要复杂的多,种种异常情况都要考虑。如:转意字符,脚本中的括号对称验证等等,
最糟糕是碰到错误的语法,或者根本就不是HTML页面(这个就不属性本文说明范围了)。



//////////////////////////////以上内容于 2011-12-17 18:01 添加/////////////////////////////////////////


1:较“HTML解析-第一版(C/C++)” 减少了内存拷贝,速度相对提高很多。

2:代码在VS2008下测试通过。#define _UNICODE #define _WIN32_WINNT 0x0600

3:解析方法:类似于构建一个map表(STL模板库里的map不利于阅读,可以参考MFC类库的CMap),最终组成一个二维的单向链表。

4:CHtmlObject 类负责解析HTML“标记”和“属性”。


//////////////////////////////////////////////////////////////////////////////////////////CHtmlObject.h//////////////////////////////////////////////////////////////////////////

#pragma once

/*****************************************************************************************************************
created: 2011/12/03
author: hmm7e (hmm7e_z@126.com)

*****************************************************************************************************************/

class CHtmlObject
{
public:
//
static BOOL IsSpace(TCHAR tcLetter);
protected:
struct tagNode
{
LPCTSTR s_pszKey;
LPCTSTR s_pszValue;
struct tagNode * s_pstRight; //attribute of tag
struct tagNode * s_pstNext; //next tag
};
public:
CHtmlObject(void);
virtual ~CHtmlObject(void);
//
enum {CHARSET_UTF8,CHARSET_UNICODE,CHARSET_MULTIBYTE}TextCharset;
protected:
//
tagNode * InnerAllocNode();
void InnerFreeNode(tagNode * lpstNode);
void InnerLinkNextNode(tagNode * lpstNode);
void InnerLinkRightNode(tagNode * lpstTagNode,tagNode * lpstNode);
void InnerCleanupNode();
void InnerCleanupRightNode(tagNode * lpstNode);
public:
//
void AutoTakeSnapshot(PBYTE lpszString,UINT nStringLen);
void TakeSnapshot(PBYTE lpszString,UINT nStringLen,UINT nFromCharset );
void DeleteSnapshot();
//
void Parse();
private:
//
void InnerParse();
LPTSTR InnerSplitComment(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitTag(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitContent(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitText(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitScript(tagNode * lpstNode,LPTSTR lpszTagString);
LPTSTR InnerSplitStyle(tagNode * lpstNode,LPTSTR lpszTagString);

protected:
//
LPTSTR m_pszSnapshotBuffer;
UINT m_nSnapshotBufferLen;
UINT m_nSnapshotStringLen;
//
tagNode * m_pstHead;
tagNode * m_pstTail;

};

//////////////////////////////////////////////////////////////////////////////////////////CHtmlObject.h//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////CHtmlObject.cpp//////////////////////////////////////////////////////////////////////////#pragma once/*****************************************************************************************************************created: 2011/12/03author: hmm7e (hmm7e_z@126.com)*****************************************************************************************************************/#include "HtmlObject.h"//BOOL CHtmlObject::IsSpace(TCHAR tcLetter){	//以下字符在HTML标记里都算是空格。	return (tcLetter == _T(' ') || tcLetter == _T('\r') ||tcLetter == _T('\n') ||tcLetter == _T('\t') );}CHtmlObject::CHtmlObject(void){	m_pszSnapshotBuffer = NULL;	m_nSnapshotBufferLen = 0;	m_nSnapshotStringLen = 0;	m_pstHead = NULL;	m_pstTail = NULL;}CHtmlObject::~CHtmlObject(void){	DeleteSnapshot();}//CHtmlObject::tagNode * CHtmlObject::InnerAllocNode(){	CHtmlObject::tagNode * pstResult = new CHtmlObject::tagNode;	if( pstResult )	{		::ZeroMemory((LPVOID)pstResult,sizeof(CHtmlObject::tagNode));	}	return pstResult;}void CHtmlObject::InnerFreeNode(CHtmlObject::tagNode * lpstNode){	if( lpstNode )		delete lpstNode;}void CHtmlObject::InnerLinkNextNode(tagNode * lpstNode){	//链接到“尾”结点。	//1:如果没有“头”节点,那么表示链表是“空”的。	//2:如果已经存“头”节点,那么就链接新节点到“尾”节点,并重新记录“尾”节点指针。	if( m_pstHead == NULL )	{		m_pstHead = lpstNode;		m_pstTail = lpstNode;	}	else	{		m_pstTail->s_pstNext = lpstNode;		m_pstTail = lpstNode;	}#ifdef _DEBUG	if( lpstNode->s_pszKey )	{		::OutputDebugString(_T("--"));		::OutputDebugString(lpstNode->s_pszKey);		::OutputDebugString(_T("--\r\n"));	}	if( lpstNode->s_pszValue )	{		::OutputDebugString(_T("--"));		::OutputDebugString(lpstNode->s_pszValue);		::OutputDebugString(_T("--\r\n"));	}#endif //_DEBUG}void CHtmlObject::InnerLinkRightNode(tagNode * lpstTagNode,tagNode * lpstNode){	//链接到“属性”的“头”节点。	//1:把现有的“属性”链表,链接到当前新节点的下。	//2:把当前节点做为“头”节点保存。	lpstNode->s_pstRight = lpstTagNode->s_pstRight;	lpstTagNode->s_pstRight = lpstNode;#ifdef _DEBUG	if( lpstNode->s_pszKey )	{		::OutputDebugString(_T("-->"));		::OutputDebugString(lpstNode->s_pszKey);		::OutputDebugString(_T("<--\r\n"));	}	if( lpstNode->s_pszValue )	{		::OutputDebugString(_T("-->"));		::OutputDebugString(lpstNode->s_pszValue);		::OutputDebugString(_T("<--\r\n"));	}#endif //_DEBUG}void CHtmlObject::InnerCleanupNode(){	//循环清除所有节点。如果存在“属性”节点一并清除。	CHtmlObject::tagNode * pstPrev = NULL;	while( m_pstHead )	{		pstPrev = m_pstHead;		m_pstHead = m_pstHead->s_pstNext;		//first		InnerCleanupRightNode(pstPrev);		//second		InnerFreeNode(pstPrev);	}	m_pstHead = NULL;	m_pstTail = NULL;}void CHtmlObject::InnerCleanupRightNode(CHtmlObject::tagNode * lpstNode){	//循环清除所有“属性”节点。	CHtmlObject::tagNode * pstHead = lpstNode->s_pstRight;	CHtmlObject::tagNode * pstPrev = NULL;	while( pstHead )	{		pstPrev = pstHead;		pstHead = pstHead->s_pstRight;		InnerFreeNode(pstPrev);	}	pstHead = NULL;	pstPrev = NULL;}//void CHtmlObject::AutoTakeSnapshot(PBYTE lpszString,UINT nStringLen){		if( lpszString && nStringLen > 0)	{		//根据数据头自动判断是否需要转换数据到当前应程所使用的编码。		if( nStringLen >= 2 )		{			if( lpszString[0] == 0xFF && lpszString[1] == 0xFE ) // skip 0xFF,0xFE			{				TakeSnapshot(lpszString+2,nStringLen-2,CHtmlObject::CHARSET_UNICODE);			}			else if( lpszString[0] == 0xEF && lpszString[1] == 0xBB && lpszString[2] == 0xBF )// skip 0xEF,0xBB,0xBF			{				TakeSnapshot(lpszString+3,nStringLen-3,CHtmlObject::CHARSET_UTF8);			}			else			{				TakeSnapshot(lpszString,nStringLen,CHtmlObject::CHARSET_MULTIBYTE);			}		}		else		{			TakeSnapshot(lpszString,nStringLen,CHtmlObject::CHARSET_MULTIBYTE);		}	}}void CHtmlObject::TakeSnapshot(PBYTE lpszString,UINT nStringLen,UINT nFromCharset ){	//delete old snapshot	DeleteSnapshot();	if( lpszString && nStringLen > 0 )	{		//transform to TCHAR		if( CHtmlHelper::CHARSET_UTF8 == nFromCharset )		{#ifdef _UNICODE 			m_nSnapshotBufferLen = nStringLen;			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];			::memset((LPVOID)m_pszSnapshotBuffer,0,m_nSnapshotBufferLen*sizeof(TCHAR));			m_nSnapshotStringLen = ::MultiByteToWideChar(CP_UTF8,0,(LPCSTR)lpszString,nStringLen,m_pszSnapshotBuffer,m_nSnapshotBufferLen);#else			::OutputDebugString(_T("no support"));#endif //_UNICODE		}		else if( CHtmlHelper::CHARSET_UNICODE == nFromCharset )		{#ifdef _UNICODE 			m_nSnapshotBufferLen = nStringLen;			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];			::memset((LPVOID)m_pszSnapshotBuffer,0,m_nSnapshotBufferLen*sizeof(TCHAR));			::memcpy((LPVOID)m_pszSnapshotBuffer,lpszString,nStringLen);#else			m_nSnapshotBufferLen = nStringLen/2+1;			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];			::memset((LPVOID)m_pszSnapshotBuffer,0,m_nSnapshotBufferLen*sizeof(TCHAR));			m_nSnapshotStringLen = ::WideCharToMultiByte(CP_ACP,0,(LPWSTR)lpszString,nStringLen,(LPSTR)m_pszSnapshotBuffer,m_nSnapshotBufferLen,NULL,NULL);#endif //_UNICODE		}		else		{#ifdef _UNICODE 			m_nSnapshotBufferLen = nStringLen;			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];			::memset(m_pszSnapshotBuffer,0,m_nSnapshotBufferLen*sizeof(TCHAR));			m_nSnapshotStringLen = ::MultiByteToWideChar(CP_ACP,0,(LPCSTR)lpszString,nStringLen,m_pszSnapshotBuffer,m_nSnapshotBufferLen);#else			m_nSnapshotBufferLen = nStringLen;			m_pszSnapshotBuffer = new TCHAR[m_nSnapshotBufferLen];			::memset((LPVOID)m_pszSnapshotBuffer,0,m_nSnapshotBufferLen*sizeof(TCHAR));			::memcpy((LPVOID)m_pszSnapshotBuffer,lpszString,nStringLen);#endif //_UNICODE		}	}}void CHtmlObject::DeleteSnapshot(){	//先清除树型表。	InnerCleanupNode();	if( m_pszSnapshotBuffer )		delete []m_pszSnapshotBuffer;	m_pszSnapshotBuffer = NULL;	m_nSnapshotBufferLen = 0;	m_nSnapshotStringLen = 0;}//void CHtmlObject::Parse(){#ifdef _AFX	CString strTrace;	strTrace.Format(_T("CHtmlObject::Parse() --begin-->(%d)\r\n"),::GetTickCount());	::OutputDebugString(strTrace);#endif //_AFX	InnerParse();#ifdef _AFX	strTrace.Format(_T("CHtmlObject::Parse() --end-->(%d)\r\n"),::GetTickCount());	::OutputDebugString(strTrace);#endif //_AFX}//void CHtmlObject::InnerParse(){	LPTSTR pszFind = m_pszSnapshotBuffer;	//跳过所有“空格”	while( *pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind) )	{		//下一个字符		pszFind++;	}	//直到碰到'\0'就退出	do	{		// 不是“\0”,并且第一个字符为“<”则置换为“\0”,否则什么也不做。		//这么写的原因就在于InnerSplitContent()返回后 “<”可能已经被置换成“\0”。		if( *pszFind != _T('\0') && *pszFind == _T('<') )		{			//把“<”置换为“\0”,做为结尾。			*pszFind = _T('\0');			//下一个字符。			pszFind++;		}		// 不是“\0”		if( *pszFind != _T('\0') )		{			//是否为注释			if( *pszFind == _T('!') )			{				//申请一个点节。				tagNode *pstNode = InnerAllocNode();				//解析注释,返回的是注释后面的内容。				pszFind = InnerSplitComment(pstNode,pszFind);				//链接到“链表”。(下)				InnerLinkNextNode(pstNode);			}			else			{				//申请一个点节。				tagNode *pstNode = InnerAllocNode();				//解析tag,返回的是tag后面的内容。				pszFind = InnerSplitTag(pstNode,pszFind);				//解析content返回的是content后面的内容。				pszFind = InnerSplitContent(pstNode,pszFind);				//链接到“链表”。(下)				InnerLinkNextNode(pstNode);			}		}	}while( *pszFind!= _T('\0') );}LPTSTR CHtmlObject::InnerSplitComment(CHtmlObject::tagNode * lpstNode,LPTSTR lpszTagString){	LPTSTR pszFind = lpszTagString;	//指向注释开头(已经跳过“<”字符)	lpstNode->s_pszKey = pszFind;	//如果为 <!-- *** -->	if( ::_tcsnicmp(pszFind+1,_T("--"),2) == 0 )	{		//跳过注释标记“头”,开始查找。		pszFind += 3;		//查找到注释结尾,并给结尾加“\0”。		while( ::_tcsnicmp(pszFind,_T("-->"),3) != 0 )		{			//下一个字符			pszFind++;		}		//不是“\0”		if( *pszFind != _T('\0') )		{			//把“>”置换为“\0”,做为注释结尾			*(pszFind+2) = _T('\0');			//指向新的节点或内容。			pszFind += 3;		}	}	//否则为 <! *** >	else	{		//查找到注释结尾,并给结尾加“\0”。		while( *pszFind != _T('\0') && *pszFind != _T('>') )		{			//下一个字符			pszFind++;		}		//不能是“\0”		if( *pszFind != _T('\0') )		{			//把“>”置换为“\0”,做为注释结尾。			*pszFind = _T('\0');			//指向新的节点或内容。			pszFind++;		}	}	//找到一个“<”	while( *pszFind != _T('\0') && *pszFind != _T('<') )	{		//下一个字符		pszFind++;	}	return pszFind;}LPTSTR CHtmlObject::InnerSplitTag(CHtmlObject::tagNode * lpstNode,LPTSTR lpszTagString){	LPTSTR pszFind = lpszTagString;	//指向开头(已经跳过“<”字符)	lpstNode->s_pszKey = pszFind;	//查找tag结尾,并给结尾加“\0”。	while( *pszFind != _T('\0') && *pszFind != _T('>') && !CHtmlObject::IsSpace(*pszFind) )	{		//下一个字符		pszFind++;	}	//不是“\0”	if( *pszFind != _T('\0') )	{		if( *pszFind == _T('>') )		{			//把“>”置换为“\0”,做为注释结尾。			*pszFind = _T('\0');			//指向新的节点或内容。			pszFind++;			//此tag没有属性,什么也不做了。						}		else		{			//把“space,\r,\n,\t ”置换为“\0”,做为注释结尾。			*pszFind = _T('\0');			//指向新的节点或内容。			pszFind++;			//如果不是结束标记,表示此tag有“属性”还需要解析“属性”。			if( *lpstNode->s_pszKey != _T('/') )			{				//跳过所有“空格”,找到第一个属性。				while( *pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind) )				{					//下一个字符					pszFind++;				}				//循环分析“属性”。				while( *pszFind != _T('\0') && *pszFind != _T('<') && *pszFind != _T('>') )				{					//例:					// key="value" key=value					//跳过空格					while( *pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind) )					{						//下一个字符						pszFind++;					}					//不是“\0”					if( *pszFind != _T('\0') )					{						//申请一个点节。						tagNode *pstAttributeNode = InnerAllocNode();						//指向“属性”Key。						pstAttributeNode->s_pszKey = pszFind;						//查找key的末尾.						while( *pszFind != _T('\0') && *pszFind != _T('=') && *pszFind != _T('>') )						{							//下一个字符							pszFind++;						}						//不是“\0”						if( *pszFind != _T('\0') )						{							if( *pszFind == _T('>') )							{								//把“>”置换为“\0”,做为结尾。								*pszFind = _T('\0');								//指向新的节点或内容。								pszFind++;								//链接到“链表”(右)。								InnerLinkRightNode(lpstNode,pstAttributeNode);								//已经碰到“>”,需要跳出。								break;							}							else							{								//把“=”置换为“\0”,做为结尾。								*pszFind = _T('\0');								//指向新的节点或内容。								pszFind++;																//不是“\0”								if( *pszFind != _T('\0') )								{									if( *pszFind == _T('"') )									{										//跳过“"”										pszFind++;										//指向“属性”key的Value。										pstAttributeNode->s_pszValue = pszFind;										//查找Value的末尾.										while( *pszFind != _T('\0') && *pszFind != _T('\"') && *pszFind != _T('>') )										{											//下一个字符											pszFind++;										}										//不是“\0”										if( *pszFind != _T('\0') )										{											//把“",> ”置换为“\0”,做为结尾。											*pszFind = _T('\0');											//指向新的节点或内容。											pszFind++;										}									}									else if( *pszFind == _T('\'') )									{										//跳过“'”										pszFind++;										//指向“属性”key的Value。										pstAttributeNode->s_pszValue = pszFind;										//查找Value的末尾.										while( *pszFind != _T('\0') && *pszFind != _T('\'') && *pszFind != _T('>') )										{											//下一个字符											pszFind++;										}										//不是“\0”										if( *pszFind != _T('\0') )										{											//把“",<space> ”置换为“\0”,做为结尾。											*pszFind = _T('\0');											//指向新的节点或内容。											pszFind++;										}									}									else									{										//指向“属性”key的Value。										pstAttributeNode->s_pszValue = pszFind;										//查找Value的末尾.										while( *pszFind != _T('\0') && *pszFind != _T(' ') && *pszFind != _T('>') )										{											//下一个字符											pszFind++;										}										//不是“\0”										if( *pszFind != _T('\0') )										{											//把“ ”置换为“\0”,做为结尾。											*pszFind = _T('\0');											//指向新的节点或内容。											pszFind++;										}									}																	}							}													}											//链接到“链表”(右)。						InnerLinkRightNode(lpstNode,pstAttributeNode);					}				} 								//跳过这个无用的字符。				if( *pszFind == _T('>') )				{					//指向新的节点或内容。					pszFind++;				}			}		}	}	return pszFind;}LPTSTR CHtmlObject::InnerSplitContent(CHtmlObject::tagNode * lpstNode,LPTSTR lpszTagString){	LPTSTR pszFind = lpszTagString;		if( ::_tcsnicmp(lpstNode->s_pszKey,_T("script"),6) == 0 )	{		pszFind = InnerSplitScript(lpstNode,pszFind);	}	else if( ::_tcsnicmp(lpstNode->s_pszKey,_T("style"),5) == 0 )	{		pszFind = InnerSplitStyle(lpstNode,pszFind);	}	else	{		pszFind = InnerSplitText(lpstNode,pszFind);	}	return pszFind;}LPTSTR CHtmlObject::InnerSplitText(CHtmlObject::tagNode * lpstNode,LPTSTR lpszTagString){	LPTSTR pszFind = lpszTagString;			//跳过所有“空格”	while( *pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind) )	{		//下一个字符		pszFind++;	}		//如果 _T('<') 表示没有文本。	if( *pszFind != _T('<') )	{		//指向可见文本。		lpstNode->s_pszValue = pszFind;		//查找文本结尾。		while( *pszFind != _T('\0') && *pszFind != _T('<') && !CHtmlObject::IsSpace(*pszFind) )		{			//下一个字符			pszFind++;		}		//不是“\0”		if( *pszFind != _T('\0') )		{			if( *pszFind == _T('<') )			{				//把“<”置换为“\0”,做为结尾。				*pszFind = _T('\0');				//指向新的节点或内容。				pszFind++;			}			else			{				//把“space,\r,\n,\t,”置换为“\0”,做为结尾。				*pszFind = _T('\0');				//指向新的节点或内容。				pszFind++;				//找到一个“<”				while( *pszFind != _T('\0') && *pszFind != _T('<') )				{					//下一个字符					pszFind++;				}			}		}	}	return pszFind;}LPTSTR CHtmlObject::InnerSplitScript(tagNode * lpstNode,LPTSTR lpszTagString){	LPTSTR pszFind = lpszTagString;#define  SCRIPT_MARK_MAX			1024	UINT nMarkIndex = 0;	TCHAR szMark[SCRIPT_MARK_MAX] = {_T('\0')}; //max 1024	//跳过所有“空格”	while( *pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind) )	{		//下一个字符		pszFind++;	}	if( *pszFind != _T('\0') && *pszFind != _T('<') )	{		//指向可见文本。		lpstNode->s_pszValue = pszFind;		while( *pszFind != _T('\0') )		{			//如果字符被“',"”包围则为字符串,这期间不计算注释。			if( szMark[nMarkIndex] != _T('\'') && szMark[nMarkIndex] != _T('\"') )			{				//如果是// abc 则跳过。				if( ::_tcsnicmp(pszFind,_T("//"),2) == 0   )				{					//跳过注释“头”。					pszFind +=2;					//查找注释“尾”。					while( *pszFind != _T('\0') && *pszFind != _T('\n') )					{						pszFind++;					}					//跳过注释“尾”。					if( *pszFind != _T('\0')  )						pszFind++;				}				//如果是/* abc */则跳过。				else if( ::_tcsnicmp(pszFind,_T("/*"),2) == 0   )				{					//跳过注释“头”。					pszFind +=2;					//查找注释“尾”。					while( ::_tcsnicmp(pszFind,_T("*/"),2) != 0 )					{						pszFind++;					}					//跳过注释“尾”。					if( *pszFind != _T('\0')  )						pszFind +=2;				}			}			if( *pszFind == _T('\\') && 				( *(pszFind+1) == _T('\\') || 				*(pszFind+1) == _T('(') || *(pszFind+1) == _T(')') || 				*(pszFind+1) == _T('[') || *(pszFind+1) == _T(']') || 				*(pszFind+1) == _T('{') || *(pszFind+1) == _T('}') || 				*(pszFind+1) == _T('\'') || 				*(pszFind+1) == _T('\"') ) )			{				//转意字符				pszFind+=2;			}			else if( *pszFind == _T('{') || *pszFind == _T('(') || *pszFind == _T('[') ||  (*pszFind == _T('\'') || *pszFind == _T('\"')) )			{				if( szMark[nMarkIndex] != _T('\'') && szMark[nMarkIndex] != _T('\"') )				{					if( nMarkIndex < SCRIPT_MARK_MAX )					{						if( nMarkIndex == 0 && szMark[nMarkIndex] == _T('\0')  )							szMark[nMarkIndex] = *pszFind;						else							szMark[++nMarkIndex] = *pszFind;					}				}				else if( szMark[nMarkIndex] == *pszFind )				{					if( nMarkIndex >0  )						szMark[nMarkIndex--] = _T('\0');					else						szMark[nMarkIndex] = _T('\0');				}				pszFind++;			}			else if( *pszFind == _T('}') ) 			{				if( szMark[nMarkIndex] == _T('{') )				{					if( nMarkIndex >0  )						szMark[nMarkIndex--] = _T('\0');					else						szMark[nMarkIndex] = _T('\0');				}				pszFind++;			}			else if( *pszFind == _T(')') )			{				if( szMark[nMarkIndex] == _T('(') )				{					if( nMarkIndex >0  )						szMark[nMarkIndex--] = _T('\0');					else						szMark[nMarkIndex] = _T('\0');				}				pszFind++;			}			else if( *pszFind == _T(']') ) 			{				if( szMark[nMarkIndex] == _T('[') )				{					if( nMarkIndex >0  )						szMark[nMarkIndex--] = _T('\0');					else						szMark[nMarkIndex] = _T('\0');				}				pszFind++;			}			else if( *pszFind == _T('<') && szMark[0] == _T('\0') )  //nMarkIndex == 0 &&			{				//把“<”置换为“\0”,做为结尾。				*pszFind = _T('\0');				//指向新的节点或内容。				pszFind++;				break;			}			else			{				pszFind++;			}		}	}	return pszFind;}LPTSTR CHtmlObject::InnerSplitStyle(CHtmlObject::tagNode * lpstNode,LPTSTR lpszTagString){	LPTSTR pszFind = lpszTagString;#define  STYLE_MARK_MAX			1024	UINT nMarkIndex = 0;	TCHAR szMark[STYLE_MARK_MAX] = {_T('\0')}; //max 1024	//跳过所有“空格”	while( *pszFind != _T('\0') && CHtmlObject::IsSpace(*pszFind) )	{		//下一个字符		pszFind++;	}	if( *pszFind != _T('\0') && *pszFind != _T('<') )	{		//指向可见文本。		lpstNode->s_pszValue = pszFind;		while( *pszFind != _T('\0') )		{			//如果字符被“(,',"”包围则为字符串,这期间不计算注释。			if( szMark[nMarkIndex] != _T('(') && szMark[nMarkIndex] != _T('\'') && szMark[nMarkIndex] != _T('\"') )			{				//如果是/* abc */则跳过。				if( ::_tcsnicmp(pszFind,_T("/*"),2) == 0   )				{					//跳过注释“头”,查找注释“尾”。					pszFind +=2;					while( ::_tcsnicmp(pszFind,_T("*/"),2) != 0 )					{						pszFind++;					}					//跳过注释“尾”。					if( *pszFind != _T('\0')  )						pszFind +=2;				}			}			if( *pszFind == _T('{') || *pszFind == _T('(') || *pszFind == _T('[') ||  (*pszFind == _T('\'') || *pszFind == _T('\"')) )			{				if( szMark[nMarkIndex] != _T('\'') && szMark[nMarkIndex] != _T('\"') )				{					if( nMarkIndex < STYLE_MARK_MAX )					{						if( nMarkIndex == 0 && szMark[nMarkIndex] == _T('\0')  )							szMark[nMarkIndex] = *pszFind;						else							szMark[++nMarkIndex] = *pszFind;					}				}				else if( szMark[nMarkIndex] == *pszFind )				{					if( nMarkIndex >0  )						szMark[nMarkIndex--] = _T('\0');					else						szMark[nMarkIndex] = _T('\0');				}				pszFind++;			}			else if( *pszFind == _T('}') ) 			{				if( szMark[nMarkIndex] == _T('{') )				{					if( nMarkIndex >0  )						szMark[nMarkIndex--] = _T('\0');					else						szMark[nMarkIndex] = _T('\0');				}				pszFind++;			}			else if( *pszFind == _T(')') )			{				if( szMark[nMarkIndex] == _T('(') )				{					if( nMarkIndex >0  )						szMark[nMarkIndex--] = _T('\0');					else						szMark[nMarkIndex] = _T('\0');				}				pszFind++;			}			else if( *pszFind == _T(']') ) 			{				if( szMark[nMarkIndex] == _T('[') )				{					if( nMarkIndex >0  )						szMark[nMarkIndex--] = _T('\0');					else						szMark[nMarkIndex] = _T('\0');				}				pszFind++;			}			else if( *pszFind == _T('<') && szMark[0] == _T('\0') )  //nMarkIndex == 0 &&			{				//把“<”置换为“\0”,做为结尾。				*pszFind = _T('\0');				//指向新的节点或内容。				pszFind++;				break;			}			else			{				pszFind++;			}		}	}	return pszFind;}//////////////////////////////////////////////////////////////////////////////////////////CHtmlObject.cpp//////////////////////////////////////////////////////////////////////////
5:CHtmlHelper做为派生类,负责读取解析后的“标记”和“属性”。这里只写了两个方法,实际应用请自行添加。

//////////////////////////////////////////////////////////////////////////////////////////CHtmlHelper.h//////////////////////////////////////////////////////////////////////////

#pragma once

/*****************************************************************************************************************
created: 2011/12/03
author: hmm7e (hmm7e_z@126.com)

*****************************************************************************************************************/

#include "HtmlObject.h"

class CHtmlHelper:public CHtmlObject
{
public:
CHtmlHelper(void);
virtual ~CHtmlHelper(void);
public:
//
LPCTSTR GetFirstLink();
LPCTSTR GetNextLink();
LPCTSTR GetFirstContent();
LPCTSTR GetNextContent();
LPCTSTR SearchText(LPCTSTR lpszText);
protected:
//
CHtmlObject::tagNode * m_pstCur;
};



//////////////////////////////////////////////////////////////////////////////////////////CHtmlHelper.h//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////CHtmlHelper.cpp//////////////////////////////////////////////////////////////////////////#pragma once/*****************************************************************************************************************created: 2011/12/03author: hmm7e (hmm7e_z@126.com)*****************************************************************************************************************/#include "HtmlHelper.h"#pragma warning(disable: 4996) CHtmlHelper::CHtmlHelper(){}CHtmlHelper::~CHtmlHelper(){}//LPCTSTR CHtmlHelper::GetFirstLink(){	LPCTSTR pszResult = NULL;	m_pstCur = m_pstHead;	while( m_pstCur && !pszResult )	{		if( 0 != ::_tcsnicmp(m_pstCur->s_pszKey,_T("script"),6) &&			0 != ::_tcsnicmp(m_pstCur->s_pszKey,_T("style"),5) )		{			CHtmlObject::tagNode * pstAttributeCur = m_pstCur->s_pstRight;			while( pstAttributeCur )			{				if( 0 == ::_tcsnicmp(pstAttributeCur->s_pszKey,_T("href"),4) ||					0 == ::_tcsnicmp(pstAttributeCur->s_pszKey,_T("src"),3) )				{					//return					pszResult = pstAttributeCur->s_pszValue;					break ;				}				else				{					pstAttributeCur = pstAttributeCur->s_pstRight;				}			}		}		m_pstCur = m_pstCur->s_pstNext;	}	return pszResult;}LPCTSTR CHtmlHelper::GetNextLink(){	LPCTSTR pszResult = NULL;	while( m_pstCur && !pszResult )	{		if( 0 != ::_tcsnicmp(m_pstCur->s_pszKey,_T("script"),6) &&			0 != ::_tcsnicmp(m_pstCur->s_pszKey,_T("style"),5) )		{			CHtmlObject::tagNode * pstAttributeCur = m_pstCur->s_pstRight;			while( pstAttributeCur )			{				if( 0 == ::_tcsnicmp(pstAttributeCur->s_pszKey,_T("href"),4) ||					0 == ::_tcsnicmp(pstAttributeCur->s_pszKey,_T("src"),3) )				{					//return					pszResult = pstAttributeCur->s_pszValue;					break ;				}				else				{					pstAttributeCur = pstAttributeCur->s_pstRight;				}			}		}		m_pstCur = m_pstCur->s_pstNext;	}	return pszResult;}LPCTSTR CHtmlHelper::GetFirstContent(){	LPCTSTR pszResult = NULL;	m_pstCur = m_pstHead;	while( m_pstCur && !pszResult )	{		if( 0 != ::_tcsnicmp(m_pstCur->s_pszKey,_T("script"),6) &&			0 != ::_tcsnicmp(m_pstCur->s_pszKey,_T("style"),5) )		{			if( m_pstCur->s_pszValue )				pszResult = m_pstCur->s_pszValue;		}		m_pstCur = m_pstCur->s_pstNext;	}	return pszResult;}LPCTSTR CHtmlHelper::GetNextContent(){	LPCTSTR pszResult = NULL;	while( m_pstCur && !pszResult )	{		if( 0 != ::_tcsnicmp(m_pstCur->s_pszKey,_T("script"),6) &&			0 != ::_tcsnicmp(m_pstCur->s_pszKey,_T("style"),5) )		{			if( m_pstCur->s_pszValue )				pszResult = m_pstCur->s_pszValue;		}		m_pstCur = m_pstCur->s_pstNext;	}	return pszResult;}//LPCTSTR CHtmlHelper::SearchText(LPCTSTR lpszText){	LPCTSTR pszResult = NULL;	CHtmlObject::tagNode *pstCur = m_pstHead;	while( pstCur && !pszResult)	{		if( 0 != ::_tcsnicmp(pstCur->s_pszKey,_T("script"),6) &&			0 != ::_tcsnicmp(pstCur->s_pszKey,_T("style"),5) )		{			if( pstCur->s_pszValue )			{				if( (NULL != ::StrStrI(pstCur->s_pszValue,lpszText)) )					pszResult = pstCur->s_pszValue;			}		}		pstCur = pstCur->s_pstNext;	}	return pszResult;}#pragma warning(default: 4996) //////////////////////////////////////////////////////////////////////////////////////////CHtmlHelper.cpp//////////////////////////////////////////////////////////////////////////