最近在做一些網頁信息采集的工作,說通俗點就是爬蟲工具,要監控頁面中某一部分內容是否發生變化。起初考慮用正則表達式去匹配網頁源碼,經過咨詢有經驗人士,推薦使用xpath去獲取頁面內容能獲得更好的效率。但是對於html這種寬松語法要求的語言來說,不可能100%地完全符合xml標准,那么就沒法使用xpath,說得更直接點就是:不能把html源碼直接加載到xmldocument中。為了使用xpath,只能對html內容進行轉換或者規范,於是就寫了這么一個方法。
該方法比較地偷懶,借助了開源工具htmlparser獲取html源碼中的所有節點,然后遍歷各個節點,轉換為對應的xmlnode。對於html中有未閉合的節點,在轉換后實際代碼會有一些差別,但是不影響xpath的使用(這也跟如何寫xpath的內容有關)。
實現方式如下,需引用htmlparser的dll

/// <summary>
/// 解析Xml文件的幫助類
/// </summary>
public class XMLHelper
{
/// <summary>
/// 有效名稱的正則表達式
/// </summary>
static string validName = @"^[^\$\/;""\!#\)\.]+$";
#region CovertHtmlToXml
/// <summary>
/// 轉換html源碼為xml格式
/// </summary>
/// <param name="html">html源碼</param>
/// <returns>xml字符串</returns>
/// <param name="TargetTag">需轉換的標記名</param>
public static string CovertHtmlToXml(string html, string targetTag)
{
try
{
XmlDocument doc = new XmlDocument();
XmlNode xmlDeclaration = doc.CreateXmlDeclaration("1.0", "utf-8", null);
doc.AppendChild(xmlDeclaration);
// 借助htmlparser解析html內容
Parser parser = Parser.CreateParser(html, "GBK");
// 篩選出指定的節點
TagNameFilter tnf = new TagNameFilter(targetTag);
NodeList nodes = parser.Parse(tnf);
// 創建根節點
XmlElement root = doc.CreateElement("Tags");
TagNode tagNode = null;
Hashtable ht = null;
XmlAttribute attr = null;
XmlElement parent = null;
for (int i = 0; i < nodes.Size(); i++)
{
tagNode = nodes[i] as TagNode;
parent = doc.CreateElement(tagNode.TagName);
// 添加屬性
ht = tagNode.Attributes;
foreach (DictionaryEntry ent in ht)
{
// 查看屬性名是否合法
if (Regex.IsMatch(ent.Key.ToString(), validName))
{
attr = doc.CreateAttribute(ent.Key.ToString());
attr.Value = ent.Value.ToString();
parent.Attributes.Append(attr);
}
}// end foreach (DictionaryEntry ent in ht)
AppendChild(tagNode, parent, doc);
root.AppendChild(parent);
}
doc.AppendChild(root);
return doc.OuterXml;
//throw new Exception("給定的html文本必須至少包含一個" + targetTag + "節點");
}
catch (Exception ex)
{
throw new Exception("轉換html內容出錯:" + ex.Message);
}
}
/// <summary>
/// 添加子節點
/// </summary>
/// <param name="tagNode">Html的父節點</param>
/// <param name="parent">Xml的父節點</param>
/// <param name="doc">Xml文檔對象</param>
private static void AppendChild(INode tagNode, XmlNode parent, XmlDocument doc)
{
INode node = null;
XmlNode xmlNode = null;
XmlAttribute attr = null;
Hashtable ht = null;
// 判斷是否包含子節點
if (tagNode.Children != null && tagNode.Children.Size() > 0)
{
for (int i = 0; i < tagNode.Children.Size(); i++)
{
node = tagNode.Children[i];
xmlNode = null;
attr = null;
ht = null;
// 如果是html標記節點
if (node is TagNode)
{
TagNode tn = node as TagNode;
if (Regex.IsMatch(tn.TagName, validName))
{
xmlNode = doc.CreateElement(tn.TagName);
// 添加屬性
ht = tn.Attributes;
foreach (DictionaryEntry ent in ht)
{
// 查看屬性名是否合法
if (Regex.IsMatch(ent.Key.ToString(), validName))
{
attr = doc.CreateAttribute(ent.Key.ToString());
attr.Value = ent.Value.ToString();
xmlNode.Attributes.Append(attr);
}
}
}
}
// 如果是文本節點
if (node is TextNode)
{
xmlNode = doc.CreateTextNode((node as TextNode).ToPlainTextString());
}
if (xmlNode != null)
{
parent.AppendChild(xmlNode);
AppendChild(node, xmlNode, doc);
}
}
}
}
#endregion
}