HTML 转文本及HTML内容提取(C#)

时间:2023-03-10 02:59:04
HTML 转文本及HTML内容提取(C#)
  1. //1、HTML直接转文本
  2. //使用方法
  3. HtmlToText convert = new HtmlToText();
  4. textBox2.Text = convert.Convert(textBox1.Text);
  5. //代码
  6. /// <summary>
  7. /// Converts HTML to plain text.
  8. /// </summary>
  9. class HtmlToText
  10. {
  11. // Static data tables
  12. protected static Dictionary<string, string> _tags;
  13. protected static HashSet<string> _ignoreTags;
  14. // Instance variables
  15. protected TextBuilder _text;
  16. protected string _html;
  17. protected int _pos;
  18. // Static constructor (one time only)
  19. static HtmlToText()
  20. {
  21. _tags = new Dictionary<string, string>();
  22. _tags.Add("address", "\n");
  23. _tags.Add("blockquote", "\n");
  24. _tags.Add("div", "\n");
  25. _tags.Add("dl", "\n");
  26. _tags.Add("fieldset", "\n");
  27. _tags.Add("form", "\n");
  28. _tags.Add("h1", "\n");
  29. _tags.Add("/h1", "\n");
  30. _tags.Add("h2", "\n");
  31. _tags.Add("/h2", "\n");
  32. _tags.Add("h3", "\n");
  33. _tags.Add("/h3", "\n");
  34. _tags.Add("h4", "\n");
  35. _tags.Add("/h4", "\n");
  36. _tags.Add("h5", "\n");
  37. _tags.Add("/h5", "\n");
  38. _tags.Add("h6", "\n");
  39. _tags.Add("/h6", "\n");
  40. _tags.Add("p", "\n");
  41. _tags.Add("/p", "\n");
  42. _tags.Add("table", "\n");
  43. _tags.Add("/table", "\n");
  44. _tags.Add("ul", "\n");
  45. _tags.Add("/ul", "\n");
  46. _tags.Add("ol", "\n");
  47. _tags.Add("/ol", "\n");
  48. _tags.Add("/li", "\n");
  49. _tags.Add("br", "\n");
  50. _tags.Add("/td", "\t");
  51. _tags.Add("/tr", "\n");
  52. _tags.Add("/pre", "\n");
  53. _ignoreTags = new HashSet<string>();
  54. _ignoreTags.Add("script");
  55. _ignoreTags.Add("noscript");
  56. _ignoreTags.Add("style");
  57. _ignoreTags.Add("object");
  58. }
  59. /// <summary>
  60. /// Converts the given HTML to plain text and returns the result.
  61. /// </summary>
  62. /// <param name="html">HTML to be converted</param>
  63. /// <returns>Resulting plain text</returns>
  64. public string Convert(string html)
  65. {
  66. // Initialize state variables
  67. _text = new TextBuilder();
  68. _html = html;
  69. _pos = 0;
  70. // Process input
  71. while (!EndOfText)
  72. {
  73. if (Peek() == '<')
  74. {
  75. // HTML tag
  76. bool selfClosing;
  77. string tag = ParseTag(out selfClosing);
  78. // Handle special tag cases
  79. if (tag == "body")
  80. {
  81. // Discard content before <body>
  82. _text.Clear();
  83. }
  84. else if (tag == "/body")
  85. {
  86. // Discard content after </body>
  87. _pos = _html.Length;
  88. }
  89. else if (tag == "pre")
  90. {
  91. // Enter preformatted mode
  92. _text.Preformatted = true;
  93. EatWhitespaceToNextLine();
  94. }
  95. else if (tag == "/pre")
  96. {
  97. // Exit preformatted mode
  98. _text.Preformatted = false;
  99. }
  100. string value;
  101. if (_tags.TryGetValue(tag, out value))
  102. _text.Write(value);
  103. if (_ignoreTags.Contains(tag))
  104. EatInnerContent(tag);
  105. }
  106. else if (Char.IsWhiteSpace(Peek()))
  107. {
  108. // Whitespace (treat all as space)
  109. _text.Write(_text.Preformatted ? Peek() : ' ');
  110. MoveAhead();
  111. }
  112. else
  113. {
  114. // Other text
  115. _text.Write(Peek());
  116. MoveAhead();
  117. }
  118. }
  119. // Return result
  120. return HttpUtility.HtmlDecode(_text.ToString());
  121. }
  122. // Eats all characters that are part of the current tag
  123. // and returns information about that tag
  124. protected string ParseTag(out bool selfClosing)
  125. {
  126. string tag = String.Empty;
  127. selfClosing = false;
  128. if (Peek() == '<')
  129. {
  130. MoveAhead();
  131. // Parse tag name
  132. EatWhitespace();
  133. int start = _pos;
  134. if (Peek() == '/')
  135. MoveAhead();
  136. while (!EndOfText && !Char.IsWhiteSpace(Peek()) &&
  137. Peek() != '/' && Peek() != '>')
  138. MoveAhead();
  139. tag = _html.Substring(start, _pos - start).ToLower();
  140. // Parse rest of tag
  141. while (!EndOfText && Peek() != '>')
  142. {
  143. if (Peek() == '"' || Peek() == '\'')
  144. EatQuotedValue();
  145. else
  146. {
  147. if (Peek() == '/')
  148. selfClosing = true;
  149. MoveAhead();
  150. }
  151. }
  152. MoveAhead();
  153. }
  154. return tag;
  155. }
  156. // Consumes inner content from the current tag
  157. protected void EatInnerContent(string tag)
  158. {
  159. string endTag = "/" + tag;
  160. while (!EndOfText)
  161. {
  162. if (Peek() == '<')
  163. {
  164. // Consume a tag
  165. bool selfClosing;
  166. if (ParseTag(out selfClosing) == endTag)
  167. return;
  168. // Use recursion to consume nested tags
  169. if (!selfClosing && !tag.StartsWith("/"))
  170. EatInnerContent(tag);
  171. }
  172. else MoveAhead();
  173. }
  174. }
  175. // Returns true if the current position is at the end of
  176. // the string
  177. protected bool EndOfText
  178. {
  179. get { return (_pos >= _html.Length); }
  180. }
  181. // Safely returns the character at the current position
  182. protected char Peek()
  183. {
  184. return (_pos < _html.Length) ? _html[_pos] : (char)0;
  185. }
  186. // Safely advances to current position to the next character
  187. protected void MoveAhead()
  188. {
  189. _pos = Math.Min(_pos + 1, _html.Length);
  190. }
  191. // Moves the current position to the next non-whitespace
  192. // character.
  193. protected void EatWhitespace()
  194. {
  195. while (Char.IsWhiteSpace(Peek()))
  196. MoveAhead();
  197. }
  198. // Moves the current position to the next non-whitespace
  199. // character or the start of the next line, whichever
  200. // comes first
  201. protected void EatWhitespaceToNextLine()
  202. {
  203. while (Char.IsWhiteSpace(Peek()))
  204. {
  205. char c = Peek();
  206. MoveAhead();
  207. if (c == '\n')
  208. break;
  209. }
  210. }
  211. // Moves the current position past a quoted value
  212. protected void EatQuotedValue()
  213. {
  214. char c = Peek();
  215. if (c == '"' || c == '\'')
  216. {
  217. // Opening quote
  218. MoveAhead();
  219. // Find end of value
  220. int start = _pos;
  221. _pos = _html.IndexOfAny(new char[] { c, '\r', '\n' }, _pos);
  222. if (_pos < 0)
  223. _pos = _html.Length;
  224. else
  225. MoveAhead();    // Closing quote
  226. }
  227. }
  228. /// <summary>
  229. /// A StringBuilder class that helps eliminate excess whitespace.
  230. /// </summary>
  231. protected class TextBuilder
  232. {
  233. private StringBuilder _text;
  234. private StringBuilder _currLine;
  235. private int _emptyLines;
  236. private bool _preformatted;
  237. // Construction
  238. public TextBuilder()
  239. {
  240. _text = new StringBuilder();
  241. _currLine = new StringBuilder();
  242. _emptyLines = 0;
  243. _preformatted = false;
  244. }
  245. /// <summary>
  246. /// Normally, extra whitespace characters are discarded.
  247. /// If this property is set to true, they are passed
  248. /// through unchanged.
  249. /// </summary>
  250. public bool Preformatted
  251. {
  252. get
  253. {
  254. return _preformatted;
  255. }
  256. set
  257. {
  258. if (value)
  259. {
  260. // Clear line buffer if changing to
  261. // preformatted mode
  262. if (_currLine.Length > 0)
  263. FlushCurrLine();
  264. _emptyLines = 0;
  265. }
  266. _preformatted = value;
  267. }
  268. }
  269. /// <summary>
  270. /// Clears all current text.
  271. /// </summary>
  272. public void Clear()
  273. {
  274. _text.Length = 0;
  275. _currLine.Length = 0;
  276. _emptyLines = 0;
  277. }
  278. /// <summary>
  279. /// Writes the given string to the output buffer.
  280. /// </summary>
  281. /// <param name="s"></param>
  282. public void Write(string s)
  283. {
  284. foreach (char c in s)
  285. Write(c);
  286. }
  287. /// <summary>
  288. /// Writes the given character to the output buffer.
  289. /// </summary>
  290. /// <param name="c">Character to write</param>
  291. public void Write(char c)
  292. {
  293. if (_preformatted)
  294. {
  295. // Write preformatted character
  296. _text.Append(c);
  297. }
  298. else
  299. {
  300. if (c == '\r')
  301. {
  302. // Ignore carriage returns. We'll process
  303. // '\n' if it comes next
  304. }
  305. else if (c == '\n')
  306. {
  307. // Flush current line
  308. FlushCurrLine();
  309. }
  310. else if (Char.IsWhiteSpace(c))
  311. {
  312. // Write single space character
  313. int len = _currLine.Length;
  314. if (len == 0 || !Char.IsWhiteSpace(_currLine[len - 1]))
  315. _currLine.Append(' ');
  316. }
  317. else
  318. {
  319. // Add character to current line
  320. _currLine.Append(c);
  321. }
  322. }
  323. }
  324. // Appends the current line to output buffer
  325. protected void FlushCurrLine()
  326. {
  327. // Get current line
  328. string line = _currLine.ToString().Trim();
  329. // Determine if line contains non-space characters
  330. string tmp = line.Replace(" ", String.Empty);
  331. if (tmp.Length == 0)
  332. {
  333. // An empty line
  334. _emptyLines++;
  335. if (_emptyLines < 2 && _text.Length > 0)
  336. _text.AppendLine(line);
  337. }
  338. else
  339. {
  340. // A non-empty line
  341. _emptyLines = 0;
  342. _text.AppendLine(line);
  343. }
  344. // Reset current line
  345. _currLine.Length = 0;
  346. }
  347. /// <summary>
  348. /// Returns the current output as a string.
  349. /// </summary>
  350. public override string ToString()
  351. {
  352. if (_currLine.Length > 0)
  353. FlushCurrLine();
  354. return _text.ToString();
  355. }
  356. }
  357. }
  358. //2、提取html的正文 类
  359. using System;
  360. using System.Text;
  361. namespace HtmlStrip
  362. {
  363. class MainClass
  364. {
  365. public static void Main (string[] args)
  366. {
  367. string str = "<div>abc</div><span>efg</span><br /><script>888</script><!--<PA>WW</PA-->oo";
  368. //System.IO.StreamReader rd=new System.IO.StreamReader ("/home/lx/test.html");
  369. //str=rd.ReadToEnd ();
  370. HtmlParser t = new HtmlParser (str); //
  371. t.KeepTag (new string[] { "br" }); //设置br标签不过虑
  372. Console.Write (t.Text ());
  373. }
  374. }
  375. class HtmlParser
  376. {
  377. private string[] htmlcode; //把html转为数组形式用于分析
  378. private StringBuilder result = new StringBuilder ();  //输出的结果
  379. private int seek; //分析文本时候的指针位置
  380. private string[] keepTag;  //用于保存要保留的尖括号内容
  381. private bool _inTag;  //标记现在的指针是不是在尖括号内
  382. private bool needContent = true;  //是否要提取正文
  383. private string tagName;  //当前尖括号的名字
  384. private string[] specialTag = new string[] { "script", "style", "!--" };  //特殊的尖括号内容,一般这些标签的正文是不要的
  385. /// <summary>
  386. /// 当指针进入尖括号内,就会触发这个属性。这里主要逻辑是提取尖括号里的标签名字
  387. /// </summary>
  388. public bool inTag {
  389. get { return _inTag; }
  390. set {
  391. _inTag = value;
  392. if (!value)
  393. return;
  394. bool ok = true;
  395. tagName = "";
  396. while (ok) {
  397. string word = read ();
  398. if (word != " " && word != ">") {
  399. tagName += word;
  400. } else if (word == " " && tagName.Length > 0) {
  401. ok = false;
  402. } else if (word == ">") {
  403. ok = false;
  404. inTag = false;
  405. seek -= 1;
  406. }
  407. }
  408. }
  409. }
  410. /// <summary>
  411. /// 初始化类
  412. /// </summary>
  413. /// <param name="html">
  414. ///  要分析的html代码
  415. /// </param>
  416. public HtmlParser (string html)
  417. {
  418. htmlcode = new string[html.Length];
  419. for (int i = 0; i < html.Length; i++) {
  420. htmlcode[i] = html[i].ToString ();
  421. }
  422. KeepTag (new string[] {  });
  423. }
  424. /// <summary>
  425. /// 设置要保存那些标签不要被过滤掉
  426. /// </summary>
  427. /// <param name="tags">
  428. ///
  429. /// </param>
  430. public void KeepTag (string[] tags)
  431. {
  432. keepTag = tags;
  433. }
  434. /// <summary>
  435. ///
  436. /// </summary>
  437. /// <returns>
  438. /// 输出处理后的文本
  439. /// </returns>
  440. public string Text ()
  441. {
  442. int startTag = 0;
  443. int endTag = 0;
  444. while (seek < htmlcode.Length) {
  445. string word = read ();
  446. if (word.ToLower () == "<") {
  447. startTag = seek;
  448. inTag = true;
  449. } else if (word.ToLower () == ">") {
  450. endTag = seek;
  451. inTag = false;
  452. if (iskeepTag (tagName.Replace ("/", ""))) {
  453. for (int i = startTag - 1; i < endTag; i++) {
  454. result.Append (htmlcode[i].ToString ());
  455. }
  456. } else if (tagName.StartsWith ("!--")) {
  457. bool ok = true;
  458. while (ok) {
  459. if (read () == "-") {
  460. if (read () == "-") {
  461. if (read () == ">") {
  462. ok = false;
  463. } else {
  464. seek -= 1;
  465. }
  466. }
  467. }
  468. }
  469. } else {
  470. foreach (string str in specialTag) {
  471. if (tagName == str) {
  472. needContent = false;
  473. break;
  474. } else
  475. needContent = true;
  476. }
  477. }
  478. } else if (!inTag && needContent) {
  479. result.Append (word);
  480. }
  481. }
  482. return result.ToString ();
  483. }
  484. /// <summary>
  485. /// 判断是否要保存这个标签
  486. /// </summary>
  487. /// <param name="tag">
  488. /// A <see cref="System.String"/>
  489. /// </param>
  490. /// <returns>
  491. /// A <see cref="System.Boolean"/>
  492. /// </returns>
  493. private bool iskeepTag (string tag)
  494. {
  495. foreach (string ta in keepTag) {
  496. if (tag.ToLower () == ta.ToLower ()) {
  497. return true;
  498. }
  499. }
  500. return false;
  501. }
  502. private string read ()
  503. {
  504. return htmlcode[seek++];
  505. }
  506. }
  507. }

引文原址:http://blog.****.net/cjh200102/article/details/6824895