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来 ...
随机推荐
- ZooKeeper学习第一期---Zookeeper简单介绍
一.分布式协调技术 在给大家介绍ZooKeeper之前先来给大家介绍一种技术——分布式协调技术.那么什么是分布式协调技术?那么我来告诉大家,其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同 ...
- 冲刺阶段 day 14
项目进展 经过这几个星期的努力,我们已经完成了我们的软件工程项目,经过多次测试,项目已经可以准确无误地运行. 存在问题 测试期间,未发现问题. 心得体会 在这几个星期的努力下,我们终于完成了我们预期的 ...
- 纠结于搞.Net待遇不高的同学入...
最近看到不少抱怨搞.net工资低的帖子.别的方向我不是太清楚,作为搞了近8年.Net信息系统开发的码农也想发表下自己的意见. 由于我的阅历和能力有限,首先想限定下本文的范围.这里说的“信息系统”主要包 ...
- XMPie部署与创建过程 - 快速指南
XMPie部署与创建过程 1PhotoShop.Indesign.VS2013关系.作用.使用 .1.1目的与过程 1. Photoshop负责导出cpkg文件. 1.1 动态性 如果你想要生成动态的 ...
- 手把手教你做一个原生js拖动滑块【兼容PC和移动端】
废话少说: 在PC端可以用mousedown来触发一个滑块滑动的效果,但在手机上,貌似无法识别这个事件,但手机上有touchstart事件,可以通过一系列"touch"事件来替代P ...
- hibernate学习笔记之一 hibernate简介
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架 hibernate可以自动生成SQL语句,自 ...
- _学生选课数据库SQL语句练习题
1. 查询Student表中的所有记录的Sname.Ssex和Class列. select Sname,Ssex,t.sclass from STUDENT t 2. 查询教师所有的单位即不重复的De ...
- 手打的笔记,java语法中的输入输出,语句,及注释。
手打的笔记: () 内的则为注意事项或者提示 public static void main (String[] args) ******(用一个方法)****{ int i = 10; int j ...
- Java项目——模拟电话薄联系人增删改查
该项目模拟了电话本记录联系人的业务功能,用来练习对数据库的增删改查等操作. 菜单类:Menu -- 用来封装主菜单和个选项的子菜单 Person类: Person--联系人的实体类 TelNoteRe ...
- NET中Application,Session,Cookie,ViewState,Cache,Hidden 缓存机制 .
Application 1. Application用来保存所有用户共用的信息 2. 在Asp时代,如果要保存的数据在应用程序生存期内不会或者很少发生改变,那么使用Ap ...