c#提取html中的正文方法二则

  文章的撰写一般是用编辑器来完成的,自然会产生大量的html标记。而前几天则有个需求,需要在首页显示一篇文章的部分章节,如下图:

这样的话,就存在一个问题,第一,需要控制显示的字数,如果只是简单的substring函数来截取字数的话,会把大量的html标记也弄进去;第二,要去除文章本身的样式,如果保持原文章的样式的话,如果文章的字体为大号,那明显会破坏这个界面的外观。因此鉴于以上两个问题,需要只提取文章的正文,过滤掉html标签。

  方法有很多,如果单纯从前台的javascript来实现也是可以,例如使用jquery中的Text()函数就可以做到只提取正文的功能,但是这样需要把整篇文章都发到客户端去处理也不是很好的事情。所以事情还是在后台做掉算了。在网上匆匆找了个用正则的方法过滤掉html,代码还算简单,也刚好满足处理要求。代码如下:

 

正则提取正文
1 /// <summary>
2 /// 去除HTML标记
3 /// </summary>
4 /// <param name="strHtml">包括HTML的源码 </param>
5 /// <returns>已经去除后的文字</returns>
6   public static string StripHTML(string strHtml)
7 {
8 string [] aryReg ={
9 @"<script[^>]*?>.*?</script>",
10
11 @"<(\/\s*)?!?((\w+:)?\w+)(\w+(\s*=?\s*(([""'])(\\[""'tbnr]|[^\7])*?\7|\w+)|.{0})|\s)*?(\/\s*)?>",
12 @"([\r\n])[\s]+",
13 @"&(quot|#34);",
14 @"&(amp|#38);",
15 @"&(lt|#60);",
16 @"&(gt|#62);",
17 @"&(nbsp|#160);",
18 @"&(iexcl|#161);",
19 @"&(cent|#162);",
20 @"&(pound|#163);",
21 @"&(copy|#169);",
22 @"&#(\d+);",
23 @"-->",
24 @"<!--.*\n"
25
26 };
27
28 string [] aryRep = {
29 "",
30 "",
31 "",
32 "\"",
33   "&",
34 "<",
35 ">",
36 " ",
37 "\xa1",//chr(161),
38   "\xa2",//chr(162),
39   "\xa3",//chr(163),
40   "\xa9",//chr(169),
41   "",
42 "\r\n",
43 ""
44 };
45
46 string newReg =aryReg[0];
47 string strOutput=strHtml;
48 for(int i = 0;i<aryReg.Length;i++)
49 {
50 Regex regex = new Regex(aryReg[i],RegexOptions.IgnoreCase );
51 strOutput = regex.Replace(strOutput,aryRep[i]);
52 }
53
54 strOutput.Replace("<","");
55 strOutput.Replace(">","");
56 strOutput.Replace("\r\n","");
57
58
59 return strOutput;
60 }
61

 

 

 

  今天有空,闲来无事,突然又觉得上面的代码有点还是做不到的,就是不能让部分html标签保留,例如我想保留br标签,想保留p标签,这个的话,上面的代码就不灵活了。于是花了些时间自己写了一段提取正文的代码,而且能设置保留某些标签。当然对比起正则来明显就不简洁了。主要思路是逐字分析,通过判断是否是“<"来确定是否是标签,判断”>“l来确定是否是正文。如果是进入了标签内,则在读取标签的名字,从而来判断是否要保留这个标签。代码也不难读,代码如下:

 

1 using System;
2 using System.Text;
3 namespace HtmlStrip
4 {
5 class MainClass
6 {
7 public static void Main (string[] args)
8 {
9 string str = "<div>abc</div><span>efg</span><br /><script>888</script><!--<PA>WW</PA-->oo";
10 //System.IO.StreamReader rd=new System.IO.StreamReader ("/home/lx/test.html");
11 //str=rd.ReadToEnd ();
12 HtmlParser t = new HtmlParser (str); //
13 t.KeepTag (new string[] { "br" }); //设置br标签不过虑
14 Console.Write (t.Text ());
15 }
16
17
18
19 }
20 class HtmlParser
21 {
22 private string[] htmlcode; //把html转为数组形式用于分析
23 private StringBuilder result = new StringBuilder (); //输出的结果
24 private int seek; //分析文本时候的指针位置
25 private string[] keepTag; //用于保存要保留的尖括号内容
26 private bool _inTag; //标记现在的指针是不是在尖括号内
27 private bool needContent = true; //是否要提取正文
28 private string tagName; //当前尖括号的名字
29 private string[] specialTag = new string[] { "script", "style", "!--" }; //特殊的尖括号内容,一般这些标签的正文是不要的
30
31 /// <summary>
32 /// 当指针进入尖括号内,就会触发这个属性。这里主要逻辑是提取尖括号里的标签名字
33 /// </summary>
34 public bool inTag {
35 get { return _inTag; }
36 set {
37 _inTag = value;
38 if (!value)
39 return;
40 bool ok = true;
41 tagName = "";
42 while (ok) {
43 string word = read ();
44 if (word != " " && word != ">") {
45 tagName += word;
46 } else if (word == " " && tagName.Length > 0) {
47 ok = false;
48 } else if (word == ">") {
49 ok = false;
50 inTag = false;
51 seek -= 1;
52 }
53 }
54 }
55 }
56 /// <summary>
57 /// 初始化类
58 /// </summary>
59 /// <param name="html">
60 /// 要分析的html代码
61 /// </param>
62 public HtmlParser (string html)
63 {
64 htmlcode = new string[html.Length];
65 for (int i = 0; i < html.Length; i++) {
66 htmlcode[i] = html[i].ToString ();
67 }
68 KeepTag (new string[] { });
69 }
70 /// <summary>
71 /// 设置要保存那些标签不要被过滤掉
72 /// </summary>
73 /// <param name="tags">
74 ///
75 /// </param>
76 public void KeepTag (string[] tags)
77 {
78 keepTag = tags;
79 }
80
81 /// <summary>
82 ///
83 /// </summary>
84 /// <returns>
85 /// 输出处理后的文本
86 /// </returns>
87 public string Text ()
88 {
89 int startTag = 0;
90 int endTag = 0;
91 while (seek < htmlcode.Length) {
92 string word = read ();
93 if (word.ToLower () == "<") {
94 startTag = seek;
95 inTag = true;
96 } else if (word.ToLower () == ">") {
97 endTag = seek;
98 inTag = false;
99 if (iskeepTag (tagName.Replace ("/", ""))) {
100 for (int i = startTag - 1; i < endTag; i++) {
101 result.Append (htmlcode[i].ToString ());
102 }
103 } else if (tagName.StartsWith ("!--")) {
104 bool ok = true;
105 while (ok) {
106 if (read () == "-") {
107 if (read () == "-") {
108 if (read () == ">") {
109 ok = false;
110 } else {
111 seek -= 1;
112 }
113 }
114 }
115 }
116 } else {
117 foreach (string str in specialTag) {
118 if (tagName == str) {
119 needContent = false;
120 break;
121 } else
122 needContent = true;
123 }
124 }
125 } else if (!inTag && needContent) {
126 result.Append (word);
127 }
128
129 }
130 return result.ToString ();
131 }
132 /// <summary>
133 /// 判断是否要保存这个标签
134 /// </summary>
135 /// <param name="tag">
136 /// A <see cref="System.String"/>
137 /// </param>
138 /// <returns>
139 /// A <see cref="System.Boolean"/>
140 /// </returns>
141 private bool iskeepTag (string tag)
142 {
143 foreach (string ta in keepTag) {
144 if (tag.ToLower () == ta.ToLower ()) {
145 return true;
146 }
147 }
148 return false;
149 }
150 private string read ()
151 {
152 return htmlcode[seek++];
153 }
154
155 }
156 }
157

 

上面的代码运行后的结果为:

abcefg<br />oo

------------------

基本达到了要求,当然代码是有缺陷的,在read()函数应该有个判断seek是否超出最大索引的判断。

posted @ 2010-12-18 23:16  看那边的人  阅读(1962)  评论(4编辑  收藏  举报