c#第五次作业

原创 2015年07月08日 21:12:37

(1)能够在自己电脑运行此程序。
    (2)测试如下三个不同的网页的提取效果,并把结果截图发布到作业博文中: 
             
http://blog.csdn.net/quailquailquail/article/details/45821703
             
http://user.qzone.qq.com/303727350/blog/1430870007
             
http://www.cnblogs.com/jasondan/p/4145305.html
    (3)自己在完成作业过程中的感受、体会、克服困难的过程。



// Author: StanZhai 翟士丹(jasondan325@163.com). All rights reserved. See License.md in the project root for license information.
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace StanSoft
{
    /// <summary>
    /// 文章正文数据模型
    /// </summary>
    public class Article
    {
        /// <summary>
        /// 文章标题
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// 正文文本
        /// </summary>
        public string Content { get; set; }
        /// <summary>
        /// 带标签正文
        /// </summary>
        public string ContentWithTags { get; set; }
        /// <summary>
        /// 文章发布时间
        /// </summary>
        public DateTime PublishDate { get; set; }
    }

    /// <summary>
    /// 解析Html页面的文章正文内容,基于文本密度的HTML正文提取类
    /// Date:   2012/12/30
    /// Update: 
    ///     2013/7/10   优化文章头部分析算法,优化
    ///     2014/4/25   添加Html代码中注释过滤的正则
    ///         
    /// </summary>
    public class Html2Article
    {
        #region 参数设置

        // 正则表达式过滤:正则表达式,要替换成的文本
        private static readonly string[][] Filters =
        {
            new[] { @"(?is)<script.*?>.*?</script>", "" },
            new[] { @"(?is)<style.*?>.*?</style>", "" },
            new[] { @"(?is)<!--.*?-->", "" },    // 过滤Html代码中的注释
            // 针对链接密集型的网站的处理,主要是门户类的网站,降低链接干扰
            new[] { @"(?is)</a>", "</a>\n"}                 
        };

        private static bool _appendMode = false;
        /// <summary>
        /// 是否使用追加模式,默认为false
        /// 使用追加模式后,会将符合过滤条件的所有文本提取出来
        /// </summary>
        public static bool AppendMode
        {
            get { return _appendMode; }
            set { _appendMode = value; }
        }

        private static int _depth = 6;
        /// <summary>
        /// 按行分析的深度,默认为6
        /// </summary>
        public static int Depth
        {
            get { return _depth; }
            set { _depth = value; }
        }

        private static int _limitCount = 180;
        /// <summary>
        /// 字符限定数,当分析的文本数量达到限定数则认为进入正文内容
        /// 默认180个字符数
        /// </summary>
        public static int LimitCount
        {
            get { return _limitCount; }
            set { _limitCount = value; }
        }

        // 确定文章正文头部时,向上查找,连续的空行到达_headEmptyLines,则停止查找
        private static int _headEmptyLines = 2;
        // 用于确定文章结束的字符数
        private static int _endLimitCharCount = 20;

        #endregion

        /// <summary>
        /// 从给定的Html原始文本中获取正文信息
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        public static Article GetArticle(string html)
        {
            // 如果换行符的数量小于10,则认为html为压缩后的html
            // 由于处理算法是按照行进行处理,需要为html标签添加换行符,便于处理
            if (html.Count(c => c == '\n') < 10)
            {
                html = html.Replace(">", ">\n");
            }

            // 获取html,body标签内容
            string body = "";
            string bodyFilter = @"(?is)<body.*?</body>";
            Match m = Regex.Match(html, bodyFilter);
            if (m.Success)
            {
                body = m.ToString();
            }
            // 过滤样式,脚本等不相干标签
            foreach (var filter in Filters)
            {
                body = Regex.Replace(body, filter[0], filter[1]);
            }
            // 标签规整化处理,将标签属性格式化处理到同一行
            // 处理形如以下的标签:
            //  <a 
            //   href='http://www.baidu.com'
            //   class='test'
            // 处理后为
            //  <a href='http://www.baidu.com' class='test'>
            body = Regex.Replace(body, @"(<[^<>]+)\s*\n\s*", FormatTag);

            string content;
            string contentWithTags;
            GetContent(body, out content, out contentWithTags);

            Article article = new Article
            {
                Title = GetTitle(html),
                PublishDate = GetPublishDate(body),
                Content = content,
                ContentWithTags = contentWithTags
            };

            return article;
        }

        /// <summary>
        /// 格式化标签,剔除匹配标签中的回车符
        /// </summary>
        /// <param name="match"></param>
        /// <returns></returns>
        private static string FormatTag(Match match)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var ch in match.Value)
            {
                if (ch == '\r' || ch == '\n')
                {
                    continue;
                }
                sb.Append(ch);
            }
            return sb.ToString();
        }

        /// <summary>
        /// 获取时间
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        private static string GetTitle(string html)
        {
            string titleFilter = @"<title>[\s\S]*?</title>";
            string h1Filter = @"<h1.*?>.*?</h1>";
            string clearFilter = @"<.*?>";

            string title = "";
            Match match = Regex.Match(html, titleFilter, RegexOptions.IgnoreCase);
            if (match.Success)
            {
                title = Regex.Replace(match.Groups[0].Value, clearFilter, "");
            }

            // 正文的标题一般在h1中,比title中的标题更干净
            match = Regex.Match(html, h1Filter, RegexOptions.IgnoreCase);
            if (match.Success)
            {
                string h1 = Regex.Replace(match.Groups[0].Value, clearFilter, "");
                if (!String.IsNullOrEmpty(h1) && title.StartsWith(h1))
                {
                    title = h1;
                }
            }
            return title;
        }

        /// <summary>
        /// 获取文章发布日期
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        private static DateTime GetPublishDate(string html)
        {
            // 过滤html标签,防止标签对日期提取产生影响
            string text = Regex.Replace(html, "(?is)<.*?>", "");
            Match match = Regex.Match(
                text,
                @"((\d{4}|\d{2})(\-|\/)\d{1,2}\3\d{1,2})(\s?\d{2}:\d{2})?|(\d{4}年\d{1,2}月\d{1,2}日)(\s?\d{2}:\d{2})?",
                RegexOptions.IgnoreCase);

            DateTime result = new DateTime(1900, 1, 1);
            if (match.Success)
            {
                try
                {
                    string dateStr = "";
                    for (int i = 0; i < match.Groups.Count; i++)
                    {
                        dateStr = match.Groups[i].Value;
                        if (!String.IsNullOrEmpty(dateStr))
                        {
                            break;
                        }
                    }
                    // 对中文日期的处理
                    if (dateStr.Contains("年"))
                    {
                        StringBuilder sb = new StringBuilder();
                        foreach (var ch in dateStr)
                        {
                            if (ch == '年' || ch == '月')
                            {
                                sb.Append("/");
                                continue;
                            }
                            if (ch == '日')
                            {
                                sb.Append(' ');
                                continue;
                            }
                            sb.Append(ch);
                        }
                        dateStr = sb.ToString();
                    }
                    result = Convert.ToDateTime(dateStr);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
                if (result.Year < 1900)
                {
                    result = new DateTime(1900, 1, 1);
                }
            }
            return result;
        }

        /// <summary>
        /// 从body标签文本中分析正文内容
        /// </summary>
        /// <param name="bodyText">只过滤了script和style标签的body文本内容</param>
        /// <param name="content">返回文本正文,不包含标签</param>
        /// <param name="contentWithTags">返回文本正文包含标签</param>
        private static void GetContent(string bodyText, out string content, out string contentWithTags)
        {
            string[] orgLines = null;   // 保存原始内容,按行存储
            string[] lines = null;      // 保存干净的文本内容,不包含标签

            orgLines = bodyText.Split('\n');
            lines = new string[orgLines.Length];
            // 去除每行的空白字符,剔除标签
            for (int i = 0; i < orgLines.Length; i++)
            {
                string lineInfo = orgLines[i];
                // 处理回车,使用[crlf]做为回车标记符,最后统一处理
                lineInfo = Regex.Replace(lineInfo, "(?is)</p>|<br.*?/>", "[crlf]");
                lines[i] = Regex.Replace(lineInfo, "(?is)<.*?>", "").Trim();
            }

            StringBuilder sb = new StringBuilder();
            StringBuilder orgSb = new StringBuilder();

            StringBuilder sbDebug = new StringBuilder();
            for (int i = 0; i < lines.Length; i++)
            {
                sbDebug.Append(i);
                sbDebug.Append(',');
                sbDebug.Append(lines[i].Length);
                sbDebug.Append("\r\n");
            }
            File.WriteAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data.txt"), lines, Encoding.Default);
            File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "stat.csv"), sbDebug.ToString());

            int preTextLen = 0;         // 记录上一次统计的字符数量
            int startPos = -1;          // 记录文章正文的起始位置
            for (int i = 0; i < lines.Length - _depth; i++)
            {
                int len = 0;
                for (int j = 0; j < _depth; j++)
                {
                    len += lines[i + j].Length;
                }

                if (startPos == -1)     // 还没有找到文章起始位置,需要判断起始位置
                {
                    if (preTextLen > _limitCount && len > 0)    // 如果上次查找的文本数量超过了限定字数,且当前行数字符数不为0,则认为是开始位置
                    {
                        // 查找文章起始位置, 如果向上查找,发现2行连续的空行则认为是头部
                        int emptyCount = 0;
                        for (int j = i - 1; j > 0; j--)
                        {
                            if (String.IsNullOrEmpty(lines[j]))
                            {
                                emptyCount++;
                            }
                            else
                            {
                                emptyCount = 0;
                            }
                            if (emptyCount == _headEmptyLines)
                            {
                                startPos = j + _headEmptyLines;
                                break;
                            }
                        }
                        // 如果没有定位到文章头,则以当前查找位置作为文章头
                        if (startPos == -1)
                        {
                            startPos = i;
                        }
                        // 填充发现的文章起始部分
                        for (int j = startPos; j <= i; j++)
                        {
                            sb.Append(lines[j]);
                            orgSb.Append(orgLines[j]);
                        }
                    }
                }
                else
                {
                    //if (len == 0 && preTextLen == 0)    // 当前长度为0,且上一个长度也为0,则认为已经结束
                    if (len <= _endLimitCharCount && preTextLen < _endLimitCharCount)    // 当前长度为0,且上一个长度也为0,则认为已经结束
                    {
                        if (!_appendMode)
                        {
                            break;
                        }
                        startPos = -1;
                    }
                    sb.Append(lines[i]);
                    orgSb.Append(orgLines[i]);
                }
                preTextLen = len;
            }

            string result = sb.ToString();
            // 处理回车符,更好的将文本格式化输出
            content = result.Replace("[crlf]", Environment.NewLine);
            content = System.Web.HttpUtility.HtmlDecode(content);
            // 输出带标签文本
            contentWithTags = orgSb.ToString();
        }
    }
}


C#第五次作业——正文提取

正文提取软件-Html2Artical 下载了提取正文软件Html2Artical后,运行VS里面的例程,打开软件界面: 输入老师要求的第一个网址:http://blog.csdn.n...

【c#第五次作业】测试 Html2Article 并添加保存功能

这是源码下载地址: http://www.51aspx.com/code/Html2Article 用vs打开后需要  右击Demo →设为启动项目 添加了 一个按钮和s...

C#第五次作业:开源项目“网页正文提取”的理解

运行结果截图: 提取http://blog.csdn.net/quailquailquail/article/details/45821703结果截图:  提取http:/...
  • WMCTG
  • WMCTG
  • 2015年07月08日 08:08
  • 295

C#第五次作业 提取网页正文

利用网上下载的程序提取指定网址的正文 网址1: http://blog.csdn.net/quailquailquail/article/details/45821703 ...

《C#程序设计》 第五次作业:开源项目“网页正文提取”的理解

一、打开运行(ctrl+r)->输入cmd->确定->输入netstat -ano 结果检测到 :80端口被system 占用,如图所示 打开进程发现确实被 PID为 4 的引用程序占用了(sys...
  • Ajixk
  • Ajixk
  • 2015年06月03日 14:08
  • 647

c#第五次作业---正文提取

一:要求如下:     (1)能够在自己电脑运行此程序。     (2)测试如下三个不同的网页的提取效果,并把结果截图发布到作业博文中:               http:/...

《C#程序设计》第五次作业:开源项目“网页正文提取”的案例分析

这周内容,是为了回应我们第五次作业(《C#程序设计》第五次作业:开源项目“网页正文提取”的理解 )。     大家可以直接从网盘下载讲义全文(word版本下载链接)。 【目的】 1. 掌握较...
  • xydy_
  • xydy_
  • 2015年06月12日 14:29
  • 362

C#第五次作业:开源项目“网页正文提取”的理解

信管1121,201211671117,聂双燕 第一部分:作业要求(完整要求见作业要求) 目标2:在目标1的基础上,增加保存正文文本到文本文件的功能(90分)。 第二部分:程序说明及代码 1...

2016年第五次VB作业

  • 2016年01月07日 08:53
  • 111KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:c#第五次作业
举报原因:
原因补充:

(最多只允许输入30个字)