C# 解析html —— 将html转为XHTML,然后利用Xml解析
呵呵,由于正则不熟,所以另谋出路——利用XML去解析html。
要想将抓取到的数据(直接抓取到的是byte[]) 转为XML文档(即XMLDocument对象),有两个要点:
一、判断编码(http头 charset 在某些网站上是不准确的)
我利用的是 第三方的一开源项目 去判断编码的,效果还不错:链接 。
二、将html转为XHTML
我利用的是 : SgmlReaderDll.dll ,微软提供的,虽然不是100%的准确,但是足以满足 轻量级的商业需求 。
核心代码如下:
public class XHtmlTools
{
private const string RegBody = @"<body[\s\S]*?>(?<body>[\s\S]*)</body>"; /// <summary>
/// 获取xml文档
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public XmlDocument GetXmlDocument(byte[] html)
{
StringBuilder XMLHEAD = new StringBuilder();
XMLHEAD.Append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
XMLHEAD.Append("<!DOCTYPE ARTICLE[");
XMLHEAD.Append("<!ENTITY nbsp \" \"><!ENTITY iexcl \"¡\"><!ENTITY cent \"¢\"><!ENTITY pound \"£\"><!ENTITY curren \"¤\"><!ENTITY yen \"¥\">");
XMLHEAD.Append("<!ENTITY brvbar \"¦\"><!ENTITY sect \"§\"><!ENTITY uml \"¨\"><!ENTITY copy \"©\"><!ENTITY ordf \"ª\"><!ENTITY laquo \"«\">");
XMLHEAD.Append("<!ENTITY not \"¬\"><!ENTITY shy \"-\"><!ENTITY reg \"®\"><!ENTITY macr \"¯\"><!ENTITY deg \"°\"><!ENTITY plusmn \"±\">");
XMLHEAD.Append("<!ENTITY sup2 \"²\"><!ENTITY sup3 \"³\"><!ENTITY acute \"´\"><!ENTITY micro \"µ\"><!ENTITY para \"¶\"><!ENTITY middot \"·\">");
XMLHEAD.Append("<!ENTITY cedil \"¸\"><!ENTITY sup1 \"¹\"><!ENTITY ordm \"º\"><!ENTITY raquo \"»\"><!ENTITY frac14 \"¼\"><!ENTITY frac12 \"½\">");
XMLHEAD.Append("<!ENTITY frac34 \"¾\"><!ENTITY iquest \"¿\"><!ENTITY times \"×\"><!ENTITY divide \"÷\"><!ENTITY Agrave \"À\"><!ENTITY Aacute \"Á\">");
XMLHEAD.Append("<!ENTITY Acirc \"Â\"><!ENTITY Atilde \"Ã\"><!ENTITY Auml \"Ä\"><!ENTITY Aring \"Å\"><!ENTITY AElig \"Æ\"><!ENTITY Ccedil \"Ç\">");
XMLHEAD.Append("<!ENTITY Egrave \"È\"><!ENTITY Eacute \"É\"><!ENTITY Ecirc \"Ê\"><!ENTITY Euml \"Ë\"><!ENTITY Igrave \"Ì\"><!ENTITY Iacute \"Í\">");
XMLHEAD.Append("<!ENTITY Icirc \"Î\"><!ENTITY Iuml \"Ï\"><!ENTITY ETH \"Ð\"><!ENTITY Ntilde \"Ñ\"><!ENTITY Ograve \"Ò\"><!ENTITY Oacute \"Ó\">");
XMLHEAD.Append("<!ENTITY Ocirc \"Ô\"><!ENTITY Otilde \"Õ\"><!ENTITY Ouml \"Ö\"><!ENTITY Oslash \"Ø\"><!ENTITY Ugrave \"Ù\"><!ENTITY Uacute \"Ú\">");
XMLHEAD.Append("<!ENTITY Ucirc \"Û\"><!ENTITY Uuml \"Ü\"><!ENTITY Yacute \"Ý\"><!ENTITY THORN \"Þ\"><!ENTITY szlig \"ß\"><!ENTITY agrave \"à\">");
XMLHEAD.Append("<!ENTITY aacute \"á\"><!ENTITY acirc \"â\"><!ENTITY atilde \"ã\"><!ENTITY auml \"ä\"><!ENTITY aring \"å\"><!ENTITY aelig \"æ\">");
XMLHEAD.Append("<!ENTITY ccedil \"ç\"><!ENTITY egrave \"è\"><!ENTITY eacute \"é\"><!ENTITY ecirc \"ê\"><!ENTITY euml \"ë\"><!ENTITY igrave \"ì\">");
XMLHEAD.Append("<!ENTITY iacute \"í\"><!ENTITY icirc \"î\"><!ENTITY iuml \"ï\"><!ENTITY eth \"ð\"><!ENTITY ntilde \"ñ\"><!ENTITY ograve \"ò\">");
XMLHEAD.Append("<!ENTITY oacute \"ó\"><!ENTITY ocirc \"ô\"><!ENTITY otilde \"õ\"><!ENTITY ouml \"ö\"><!ENTITY oslash \"ø\"><!ENTITY ugrave \"ù\">");
XMLHEAD.Append("<!ENTITY uacute \"ú\"><!ENTITY ucirc \"û\"><!ENTITY uuml \"ü\"><!ENTITY yacute \"ý\"><!ENTITY thorn \"þ\"><!ENTITY yuml \"ÿ\">");
XMLHEAD.Append("<!ENTITY lsquo \"‘\"><!ENTITY rsquo \"’\"><!ENTITY ldquo \"“\"><!ENTITY rdquo \"”\"><!ENTITY sbquo \"'\"><!ENTITY mdash \"—\">");
XMLHEAD.Append("<!ENTITY Prime \"′\"><!ENTITY hellip \"…\">");
XMLHEAD.Append("]>"); if (html == null)
return null; string xml = Convert(html); if (string.IsNullOrEmpty(xml))
return null; try
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = null;
xmlDoc.LoadXml(string.Format("{0}{1}", XMLHEAD.ToString(), xml)); return xmlDoc;
}
catch (XmlException)
{
return null;
}
} /// <summary>
/// 将html转为xml
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public string Convert(byte[] html)
{
string xml = string.Empty;
try
{
using (HtmlReader reader = new HtmlReader(GetString(html)))
{
StringBuilder sb = new StringBuilder(); using (HtmlWriter writer = new HtmlWriter(sb))
{
while (!reader.EOF)
{
writer.WriteNode(reader, true);
}
} xml = sb.ToString();
}
}
catch (Exception)
{
} Match match = Regex.Match(xml, RegBody, RegexOptions.IgnoreCase);
if (match.Success)
{
xml = match.Value;
} if (string.IsNullOrEmpty(xml))
{
xml = "<body></body>";
} return xml;
} /// <summary>
/// 解析编码并获得字符串
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public string GetString(byte[] buffer)
{
string result = string.Empty; if (buffer == null)
return result; using (MemoryStream msTemp = new MemoryStream(buffer))
{
if (msTemp.Length > )
{
msTemp.Seek(, SeekOrigin.Begin);
int DetLen = ;
byte[] DetectBuff = new byte[]; UniversalDetector det = new UniversalDetector(null);
while ((DetLen = msTemp.Read(DetectBuff, , DetectBuff.Length)) > && !det.IsDone())
{
det.HandleData(DetectBuff, , DetectBuff.Length);
}
det.DataEnd();
if (det.GetDetectedCharset() != null)
{
try
{
result = System.Text.Encoding.GetEncoding(det.GetDetectedCharset()).GetString(buffer);
}
catch (ArgumentException)
{
}
}
}
} return result;
} } public class HtmlReader : Sgml.SgmlReader
{
public HtmlReader(TextReader reader)
: base()
{
base.InputStream = reader;
base.DocType = "HTML";
}
public HtmlReader(string content)
: base()
{
base.InputStream = new StringReader(System.Web.HttpUtility.HtmlDecode(content));
base.DocType = "HTML";
} public override bool Read()
{
bool status = false;
try
{
status = base.Read();
if (status)
{
if (base.NodeType == XmlNodeType.Element
&& (string.Compare(base.Name, "head", true) ==
|| string.Compare(base.Name, "script", true) == ))
{
base.Skip();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return status;
}
} public class HtmlWriter : XmlTextWriter
{
private char[] chArrFilter = new char[] { '\'', '=', '?', '\"', '.', ';', ':', ')', '(', ' ', ' ' }; public HtmlWriter(TextWriter writer)
: base(writer)
{
} public HtmlWriter(StringBuilder builder)
: base(new StringWriter(builder))
{
} public HtmlWriter(Stream stream, Encoding enc)
: base(stream, enc)
{ } public override void WriteCData(string text)
{
// base.WriteCData(text);
} public override void WriteComment(string text)
{ } public override void WriteWhitespace(string ws)
{
if (ws.IndexOf("\r\n") > - || ws.IndexOf("\t") > -)
{
return;
} if (ws != " ")
{
// 处理空白字符
base.WriteWhitespace(ws);
}
} public override void WriteStartElement(string prefix, string localName, string ns)
{
if (localName != "")
{
int index = localName.LastIndexOf(':'); if (index > -)
{
// 防止带有前缀
localName = localName.Substring(index + );
} localName = string.Join("", localName.Split(chArrFilter)).ToLower(); base.WriteStartElement("", localName, "");
}
} public override void WriteAttributes(XmlReader reader, bool defattr)
{
if ((reader.NodeType == XmlNodeType.Element) || (reader.NodeType == XmlNodeType.XmlDeclaration))
{
if (reader.MoveToFirstAttribute())
{
this.WriteAttributes(reader, defattr);
reader.MoveToElement();
}
}
else if (reader.NodeType == XmlNodeType.Attribute)
{
string localName = "";
string value = "";
do
{
localName = reader.LocalName.ToLower(); // 单过滤
if (localName != "xml:space" && (localName.LastIndexOf(':') > - || localName.StartsWith("xml")))
{
// 防止带有前缀
continue;
} localName = string.Join("", localName.Split(chArrFilter)); if (localName == "")
{
continue;
} this.WriteStartAttribute("", localName, ""); while (reader.ReadAttributeValue())
{
// if (reader.NodeType == XmlNodeType.EntityReference)
// {
// this.WriteEntityRef(reader.Name);
// continue;
// } value = reader.Value; if (value == "")
{
continue;
} this.WriteString(value); // this.WriteRawString(reader.Value);
// this.WriteAttributeString(localName, reader.Value);
} this.WriteEndAttribute(); // ===========================================
//string attributeLocalName = reader.LocalName;
//while (reader.ReadAttributeValue())
//{
// string str = reader.Name;
//} //string strValue = reader.Value;
//attributeLocalName = reader.Name; //// 过滤无效的属性
//if (attributeLocalName == "" || strValue == "")
//{
// attributeLocalName = attributeLocalName.TrimStart(new char[] { '\'', '=', '?', '\"', '.' }).ToLower();
// this.WriteAttributeString(attributeLocalName, strValue);
//} } while (reader.MoveToNextAttribute());
}
} }
上述源码及DLL : http://files.cnblogs.com/08shiyan/XHtmlTools.zip
下面再说一下解析XML,我利用的XPath:
XPath 和 jQuery所支持的选择器有一定的相似之处,借助jQuery所支持的选择器去理解XPath会更容易一些。
http://www.cnblogs.com/08shiyan/archive/2013/05/02/3055078.html
续:
由 imfunny 分享的 HtmlAgilityPack,开源的力量很强大!
HtmlAgilityPack 里的部分类 的元属性截图
支持多个 .NET 版本
HtmlAgilityPack地址:http://htmlagilitypack.codeplex.com/
C# 解析html —— 将html转为XHTML,然后利用Xml解析的更多相关文章
- 多级xml解析方案
package com.people.xmlToSql; import java.io.File; import java.io.IOException; import java.io.StringW ...
- Duilib源码分析(三)XML解析器—CMarkup
上一节介绍了控件构造器CDialogBuilder,接下来将分析其XML解析器CMarkup: CMarkup:xml解析器,目前内置支持三种编码格式:UTF8.UNICODE.ASNI,默认为UTF ...
- 网络热恋之XML解析
XML 可扩展标记语言 用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言 易读性高,编码手写难度小,数据量大 NSXMLPars ...
- XML解析方案
在iOS中,解析XML的手段有很多 苹果原生 NSXMLParser:SAX方式解析,使用简单 第三方框架 libxml2:纯C语言,默认包含在iOS SDK中,同时支持DOM和SAX方式解析 GDa ...
- iOS开发——网络篇——JSON和XML,NSJSONSerialization ,NSXMLParser(XML解析器),NSXMLParserDelegate,MJExtension (字典转模型),GDataXML(三方框架解析XML)
一.JSON 1.JSON简介什么是JSONJSON是一种轻量级的数据格式,一般用于数据交互服务器返回给客户端的数据,一般都是JSON格式或者XML格式(文件下载除外) JSON的格式很像OC中的字典 ...
- IOS 网络浅析-(五 xml解析)
XML 可扩展标记语言 用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言 易读性高,编码手写难度小,数据量大 NSXMLPars ...
- 网络数据的XML解析
网络应用中的数据解析,因为最近的应用,无论是Android的和ios平台的,一直用也是建议用的都是Json解析, xml解析都有点被遗忘了. 然后最近自己在做着玩一个ios的小应用,涉及网络数据的抓取 ...
- XML解析技术研究(一)
摘要:XML作为过去十年中出现的最流行的技术之一,得到了广泛的应用,而其中XML解析技术是XML应用的关键.本文介绍了XML解析技术的研究动向,分析和比较了4种XML解析技术的优劣,并归纳总结了应 ...
- XML概念定义以及如何定义xml文件编写约束条件java解析xml DTD XML Schema JAXP java xml解析 dom4j 解析 xpath dom sax
本文主要涉及:xml概念描述,xml的约束文件,dtd,xsd文件的定义使用,如何在xml中引用xsd文件,如何使用java解析xml,解析xml方式dom sax,dom4j解析xml文件 XML来 ...
随机推荐
- Java设计模式5:原型模式
原型模式 原型模式属于对象的创建模式,通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的用意. 原型模式结构 原型模式要求对象实现一个 ...
- (转)Babel-现在开始使用 ES6
在 2 月 20 号 ECMAScript 第六版就正式推出了,这门语言一直保持稳定快速的发展而且新功能也在慢慢被现在主流的 JavaScript 引擎所接受.不过要想在浏览器端或者 Node 端直接 ...
- Java多线程系列--“JUC锁”05之 非公平锁
概要 前面两章分析了"公平锁的获取和释放机制",这一章开始对“非公平锁”的获取锁/释放锁的过程进行分析.内容包括:参考代码获取非公平锁(基于JDK1.7.0_40)释放非公平锁(基 ...
- java提高篇(二五)-----HashTable
在java中与有两个类都提供了一个多种用途的hashTable机制,他们都可以将可以key和value结合起来构成键值对通过put(key,value)方法保存起来,然后通过get(key ...
- java中基本类型和包装类型实践经验
至今,小菜用java快两年了,有些事,也该有个总结. 基本类型和包装类型的概念在本文不作赘述. 如果这两种类型直接使用,倒没什么值得讨论的,无非就是自动装箱拆箱,java可以让你感觉不到他们的存在,但 ...
- 线程池ThreadPool知识碎片和使用经验速记
ThreadPool(线程池)大概的工作原理是,初始时线程池中创建了一些线程,当应用程序需要使用线程池中的线程进行工作,线程池将会分配一个线程,之后到来的请求,线程池都会尽量使用池中已有的这个线程进行 ...
- 内存角度探寻C++面向对象 之 继承、多态
一,简单继承: #include <iostream> class TableTennisPlayer { private: int id; public: TableTennisPlay ...
- UISwitch
UISwitch *noticeSwtich = [[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 51, 31)]; // noticeSwtich. ...
- Linux初学 - 文件夹及文件操作
创建文件夹 mkdir 移动文件夹 mv dir1 dir2 复制文件夹 cp 删除文件夹 rm 创建文件 touch 编辑文件内容 vi /vim 查看文件内容 cat 追加文件内容 echo 复制 ...
- Java EE开发平台随手记3——Mybatis扩展2
忙里偷闲,继续上周的话题,记录Mybatis的扩展. 扩展5:设置默认的返回结果类型 大家知道,在Mybatis的sql-mapper配置文件中,我们需要给<select>元素添加resu ...