场景模拟
每天8点爬取今日发布的新闻和通知公告,将爬取后的信息保存到Excel文件中,将程序发布成windows服务,开机即可自动启动。
技术使用
1.每天8点定时执行任务,使用Quartz.Net
2.爬取数据采用HtmlAgility
3.Excel操作采用NPOI
4.将应用程序发布为Windows服务,使用Topshelf
5.日志记录Log4Net
思路
因为最后的输出形式为Windows服务,所以使用Topshelf进行打包
TopShelf使用链接
TopShelf概述
Topshelf是创建Windows服务的另一种方法,老外的一篇文章Create a .NET Windows Service in 5 steps with Topshelf通过5个步骤详细的介绍使用使用Topshelf创建Windows 服务。Topshelf是一个开源的跨平台的宿主服务框架,支持Windows和Mono,只需要几行代码就可以构建一个很方便使用的服务宿主。
不了解TopShelf的童鞋可以先百度或者根据LZ提供的链接看看TopShelf是什么以及如何使用。
在了解TopShelf为何物后,我们首先建立一个控制台项目,将我们所需要的组件一一安装。
Install-package Quartz.Net
Install-package Log4Net
Install-package HtmlAgility
Install-package TopShelf
第一步:配置Log4Net日志
新建Log4net.config配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<!--日志路径-->
<param name= "File" value= "D:\App_Log\servicelog\"/>
<!--是否是向文件中追加日志-->
<param name= "AppendToFile" value= "true"/>
<!--log保留天数-->
<param name= "MaxSizeRollBackups" value= "10"/>
<!--日志文件名是否是固定不变的-->
<param name= "StaticLogFileName" value= "false"/>
<!--日志文件名格式为:2008-08-31.log-->
<param name= "DatePattern" value= "yyyy-MM-dd".read.log""/>
<!--日志根据日期滚动-->
<param name= "RollingStyle" value= "Date"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n %loggername" />
</layout>
</appender>
<!-- 控制台前台显示日志 -->
<appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="ERROR" />
<foreColor value="Red, HighIntensity" />
</mapping>
<mapping>
<level value="Info" />
<foreColor value="Green" />
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%n%date{HH:mm:ss,fff} [%-5level] %m" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="Info" />
<param name="LevelMax" value="Fatal" />
</filter>
</appender>
<root>
<!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低) -->
<level value="all" />
<appender-ref ref="ColoredConsoleAppender"/>
<appender-ref ref="RollingLogFileAppender"/>
</root>
</log4net>
</configuration>
![img_ebd202975c4912411271ab8e6f18767a.png](https://i-blog.csdnimg.cn/blog_migrate/7d60a7a995b6ef2b04d378c913352328.png)
在Main方法种开始执行时,对Log4Net进行日志配置加载。
第二部:爬取信息
使用HtmlAgility,如果对此第三方库不是很理解的,可以参考LZ的爬虫系列文章,里面有对其讲解。
代码不做过多讲解,主要就是注意获取当天数据
public class JKNews
{
/// <summary>
/// 得到今日的新闻
/// </summary>
/// <returns></returns>
public static List<Notice> GetTodayNews()
{
string[] url =
{
"http://www.jit.edu.cn/myNews_list_out.aspx?infotype=1",
"http://www.jit.edu.cn/myNews_list_out.aspx?infotype=2"
};
var web = new HtmlWeb();
web.OverrideEncoding = Encoding.GetEncoding("gb2312");
List<Notice> noticeItems = new List<Notice>();
for (int i = 0; i < url.Length; i++)
{
var docWeb = web.Load(url[i]);
var listItems = docWeb.DocumentNode.SelectNodes("//*[@id=\"table_list\"]/li").ToList();
foreach (var item in listItems)
{
string href = item.SelectSingleNode("./a").Attributes["href"].Value;
string title = item.InnerText;
string remark = item.SelectSingleNode("./span[@class=\'puber\']").InnerText;
var splitArr = remark.Split(' ');
string dep = splitArr[0].TrimStart('[');
string time = splitArr[1].TrimEnd(']');
noticeItems.Add(new Notice()
{
Href = href,
Title = title,
Dep = dep,
Time = time
});
}
}
return noticeItems.Where(u => Convert.ToDateTime(u.Time).Date.Equals(DateTime.Now)).ToList();
}
}
比较俩日期相同
方法一:
System.Data.Entity.DbFunctions.DiffDays(cs.StartTime.Value,DateTime.Now) == 0只获取当天
方法二:
XX.StartTime.Value.Date.Equals(DateTime.Now.Date)//只获取当天
![img_14d472afbcba4e9e317fe231dc05bcbc.png](https://i-blog.csdnimg.cn/blog_migrate/d97cdc575282a0bf4631b4dfe7f80099.png)
第三步:使用QuartZ建立一个定时任务
1.建立一个 ToExcelJob 继承 IJob接口,实现方法
![img_7a3840826de2e1b3c66f624da522b0a8.png](https://i-blog.csdnimg.cn/blog_migrate/1c14dd60411d5fe82ff5daf78bc13e6b.png)
定时方法Exceute种分俩部分执行
第一部分:获取爬取后的数据
第二部分:数据保存到Excel文件
public class ToExcelJob : IJob
{
private static string excelPath = ConfigurationManager.AppSettings["ExcelPath"];
static readonly ILog Log = LogManager.GetLogger(typeof(ToExcelJob));
public void Execute(IJobExecutionContext context)
{
try
{
if (!Directory.Exists(excelPath))
{
Directory.CreateDirectory(excelPath);
}
var items = JKNews.GetTodayNews();
var excel = new Excel();
excel.CreateSheet("Sheet1");
int rowIndex = 0;
excel.WriteTitle(new string[] { "链接", "标题", "部门", "日期" }, 0, 0);
rowIndex++;
foreach (var item in items)
{
excel.CreateRow(0, rowIndex);
excel.WriteProperty<Notice>(item, 0, rowIndex, 0);
rowIndex++;
}
excel.SetColumnWidth(0, 0, new[] { 20, 30, 10, 10 });
string savePath = Path.Combine(excelPath, string.Format("{0}.xlsx", DateTime.Now.ToString("yyyy.MM.dd")));
excel.WriteFile(savePath);
}
catch (Exception ex)
{
Log.Error(ex.Message, ex);
}
}
}
第四步:定制TopShelf服务类,对开始和结束进行代码描述
构造函数中使用Quartz.Net 开始进行任务的创建
首先创建一个调度器
然后创建一个任务
然后创建一个触发器,这一步中我们设置了cron为每晚8点,具体使用的大家可以看cron语法
然后将任务与触发器添加到调度器中并执行
在构造函数中对当前任务(Job)进行初始化配置,然后开放俩个方法Start和Stop让便上层调用
public class ToExcelRunner
{
static readonly ILog _log = LogManager.GetLogger(typeof(ToExcelRunner));
private readonly IScheduler scheduler;
public ToExcelRunner()
{
// 创建一个调度器
scheduler = StdSchedulerFactory.GetDefaultScheduler();
//2、创建一个任务
IJobDetail job = JobBuilder.Create<ToExcelJob>().WithIdentity("job1", "group1").Build();
//3、创建一个触发器
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.WithCronSchedule("0 0 20 ? * *") //每天晚上8点执行
.Build();
//4 将任务与触发器添加到调度器中并执行
scheduler.ScheduleJob(job, trigger);
}
public void Start()
{
try
{
_log.Info("服务开启");
scheduler.Start();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
public void Stop()
{
_log.Info("服务结束");
scheduler.Shutdown(false);
}
}
第五步:TopShelf配置
s.ConstructUsing(name => new ToExcelRunner()); 服务使用ToExcelRunner
对服务的Started注册tc.Start()方法
对服务的Stopped注册tc.Stop()方法
使用日志记录,出错时候记录。
class Program
{
static readonly ILog _log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
FileInfo fi = new FileInfo(AppDomain.CurrentDomain.BaseDirectory + "\\log4net.config");
XmlConfigurator.ConfigureAndWatch(fi);
try
{
HostFactory.Run(x =>
{
x.Service<ToExcelRunner>(s =>
{
s.ConstructUsing(name => new ToExcelRunner());
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
});
x.RunAsLocalSystem();
x.SetDescription("每天晚上8点讲当日新闻保存为Excel");
x.SetDisplayName("新闻保存服务");
x.SetServiceName("新闻保存服务");
});
}
catch (Exception ex)
{
_log.Error(ex);
}
}
}
第六步:发布为windows服务
配置运行
没错,整个程序已经开发完了,接下来,只需要简单配置一下,即可以当服务来使用了。安装很方便:
安装:JKNoticeget.exe install
启动:JKNoticeget.exe start
卸载:JKNoticeget.exe uninstall
管理员身份进入,对应路径,注册服务
![img_616ac8480620d3544e7321724bd30269.png](https://i-blog.csdnimg.cn/blog_migrate/cc349a17ebd18af2f683d978243370e9.png)
![img_82379f6a2d894446ae8cdac5c70cdbbf.png](https://i-blog.csdnimg.cn/blog_migrate/9941b5c41988beac7b21989201788796.png)
![img_8f16499b558f1ca96883eb9e8de2fdb3.png](https://i-blog.csdnimg.cn/blog_migrate/0103192cf88a33fdf2e6e9d97f453d95.png)
代码链接 https://github.com/happlyfox/FoxCrawler/tree/master/%E5%AD%A6%E4%B9%A0%E7%A4%BA%E4%BE%8B/JKNoticeget