BJKL8Job.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using HtmlAgilityPack;
  5. using Quartz;
  6. using SCC.Common;
  7. using SCC.Crawler.Tools;
  8. using SCC.Interface;
  9. using SCC.Models;
  10. using SCC.Services;
  11. namespace SCC.Crawler.GP
  12. {
  13. /// <summary>
  14. /// 北京快乐8
  15. /// </summary>
  16. [DisallowConcurrentExecution]
  17. [PersistJobDataAfterExecution]
  18. public class BJKL8Job : IJob
  19. {
  20. /// <summary>
  21. /// 构造函数
  22. /// </summary>
  23. public BJKL8Job()
  24. {
  25. log = new LogHelper();
  26. services = IOC.Resolve<IOpen21Code>();
  27. email = IOC.Resolve<IEmail>();
  28. }
  29. /// <summary>
  30. /// 作业执行入口
  31. /// </summary>
  32. /// <param name="context">作业执行上下文</param>
  33. public void Execute(IJobExecutionContext context)
  34. {
  35. Config = CommonHelper.GetConfigFromDataMap(context.JobDetail.JobDataMap);
  36. //预设节假日不开奖
  37. if (Config.SkipDate.Contains(CommonHelper.SCCSysDateTime.ToString("yyyyMMdd"))) return;
  38. LatestItem = context.JobDetail.JobDataMap["LatestItem"] as OpenCode21Model;
  39. try
  40. {
  41. //服务启动时配置初始数据
  42. if (LatestItem == null) LatestItem = services.GetLastItem(currentLottery);
  43. //第一次启动服务或最新开奖记录为昨日滴,则自检昨天开奖数据是否抓取完毕(否则插入邮件数据),并重置当天期号和失败列表
  44. if (LatestItem == null || LatestItem.OpenTime.ToString("yyyyMMdd") !=
  45. CommonHelper.SCCSysDateTime.ToString("yyyyMMdd"))
  46. {
  47. CheckingYesterdayTheLotteryData();
  48. LatestItem = GenerateTodayFirstItem();
  49. }
  50. //当最新开奖记录不是今天最后一期,执行当天作业
  51. if (!CheckLatestItemIsTodayLastItem())
  52. {
  53. DoTodayJobByMainUrl();
  54. DoTodayJobByBackUrl();
  55. }
  56. }
  57. catch (Exception ex)
  58. {
  59. log.Error(GetType(), string.Format("【{0}】抓取时发生错误,错误信息【{1}】", Config.Area + currentLottery, ex.Message));
  60. }
  61. //保存最新期号和失败期号列表
  62. context.JobDetail.JobDataMap["LatestItem"] = LatestItem;
  63. }
  64. /// <summary>
  65. /// 自检昨天开奖数据
  66. /// </summary>
  67. private void CheckingYesterdayTheLotteryData()
  68. {
  69. if (Config.SkipDate.Contains(CommonHelper.SCCSysDateTime.AddDays(-1).ToString("yyyyMMdd")))
  70. return; //如果昨日设定不开奖则不自检昨日开奖数据
  71. //从数据库中获取昨天数据抓取失败列表
  72. FailedQiHaoList = services.GetYesterdayFailQQQList(currentLottery, GenerateYesterdayQiHaoList());
  73. if (FailedQiHaoList.Count > 0)
  74. {
  75. DoYesterdayFailedListByMainUrl();
  76. DoYesterdayFailedListByBackUrl();
  77. }
  78. }
  79. /// <summary>
  80. /// 通过主站点抓取开奖数据
  81. /// (百度彩票)
  82. /// </summary>
  83. private void DoTodayJobByMainUrl()
  84. {
  85. if (!string.IsNullOrEmpty(Config.MainUrl))
  86. {
  87. var OpenList = GetDocByMainUrl();
  88. if (OpenList.Count == 0) return; //无抓取数据
  89. var startQiNum = LatestItem.Term + 1;
  90. var newestQiNum = OpenList.Max(w => w.Term);
  91. if (startQiNum > newestQiNum) return; //无最新数据
  92. //处理最新开奖数据
  93. for (var i = startQiNum; i <= newestQiNum; i++)
  94. {
  95. var matchItem = OpenList.Where(R => R.Term == i).FirstOrDefault();
  96. if (matchItem != null && services.AddOpen21Code(currentLottery, matchItem))
  97. {
  98. //处理成功写入日志
  99. log.Info(GetType(), CommonHelper.GetJobMainLogInfo(Config, i.ToString()));
  100. LatestItem = matchItem;
  101. }
  102. }
  103. }
  104. }
  105. /// <summary>
  106. /// 通过主站抓取错误期号列表中每一个期号
  107. /// (百度彩票)
  108. /// </summary>
  109. private void DoYesterdayFailedListByMainUrl()
  110. {
  111. var OpenList = GetDocByMainUrl();
  112. if (OpenList.Count == 0) return;//无抓取数据
  113. foreach (OpenCode21Model model in OpenList)
  114. {
  115. if (services.AddOpen21Code(currentLottery, model))
  116. {
  117. //处理成功写入日志
  118. log.Info(typeof(BJPK10Job), CommonHelper.GetJobBackLogInfo(Config, model.Term.ToString()));
  119. LatestItem = model;
  120. }
  121. }
  122. }
  123. /// <summary>
  124. /// 通过备用站点抓取开奖数据
  125. /// (北京福彩官网)
  126. /// </summary>
  127. private void DoTodayJobByBackUrl()
  128. {
  129. if (!string.IsNullOrEmpty(Config.BackUrl))
  130. {
  131. var OpenList = GetOpenListFromBackUrl(Config.BackUrl);
  132. if (OpenList.Count == 0) return; //无抓取数据
  133. var startQiNum = LatestItem.Term + 1;
  134. var newestQiNum = OpenList.Max(w => w.Term);
  135. var total = OpenList.Count;
  136. if (startQiNum > newestQiNum) return; //无最新数据
  137. //处理最新开奖数据
  138. for (var i = startQiNum; i <= newestQiNum; i++)
  139. {
  140. var matchItem = OpenList.Where(R => R.Term == i).FirstOrDefault();
  141. var step = 0;
  142. var nowQiHao = Convert.ToInt32(i);
  143. while (matchItem == null)
  144. if (step <= total)
  145. {
  146. nowQiHao++;
  147. step++;
  148. matchItem = OpenList.Where(R => R.Term.ToString() == nowQiHao.ToString()).FirstOrDefault();
  149. }
  150. else
  151. {
  152. matchItem = null;
  153. break;
  154. }
  155. if (matchItem != null && services.AddOpen21Code(currentLottery, matchItem))
  156. {
  157. //处理成功写入日志
  158. log.Info(GetType(), CommonHelper.GetJobBackLogInfo(Config, i.ToString()));
  159. LatestItem = matchItem;
  160. }
  161. }
  162. }
  163. }
  164. /// <summary>
  165. /// 通过备用地址抓取错误期号列表中每一个期号
  166. /// (北京福彩官网)
  167. /// </summary>
  168. private void DoYesterdayFailedListByBackUrl()
  169. {
  170. if (!string.IsNullOrEmpty(Config.BackUrl))
  171. {
  172. var OpenList = GetOpenListFromBackUrl(Config.BackUrl);
  173. if (OpenList.Count == 0) return;//无抓取数据
  174. foreach (OpenCode21Model model in OpenList)
  175. {
  176. if (services.AddOpen21Code(currentLottery, model))
  177. {
  178. //处理成功写入日志
  179. log.Info(typeof(BJPK10Job), CommonHelper.GetJobBackLogInfo(Config, model.Term.ToString()));
  180. LatestItem = model;
  181. }
  182. }
  183. }
  184. }
  185. /// <summary>
  186. /// 抓取备用站点开奖数据
  187. /// 由于北京福彩官网分页30条数据一页,而此彩种每天开179期,如果去跑6页,负荷太大,只有通过主站点来保证数据完整性
  188. /// </summary>
  189. /// <param name="url">备用站点</param>
  190. /// <returns></returns>
  191. private List<OpenCode21Model> GetOpenListFromBackUrl(string url, bool IsToday = true)
  192. {
  193. var result = new List<OpenCode21Model>();
  194. try
  195. {
  196. var HtmlResource = string.Empty;
  197. HtmlResource = NetHelper.GetUrlResponse(url);
  198. if (HtmlResource == null) return result;
  199. var doc = new HtmlDocument();
  200. doc.LoadHtml(HtmlResource);
  201. var table = doc.DocumentNode.SelectNodes("//table");
  202. if (table == null || table.Count < 2) return result;
  203. var trs = table[1].ChildNodes.Where(R => R.Name.ToLower() == "tr").ToList();
  204. if (trs.Count < 2) return result;
  205. List<HtmlNode> tds = null;
  206. OpenCode21Model model = null;
  207. string[] openCodeList = null;
  208. var openTime = string.Empty;
  209. var todayDateString = CommonHelper.SCCSysDateTime.ToString("yyyy-MM-dd");
  210. for (var i = 1; i < trs.Count; i++) //第一行是表头
  211. {
  212. tds = trs[i].ChildNodes.Where(R => R.Name.ToLower() == "td").ToList();
  213. if (tds.Count < 4) continue;
  214. openTime = tds[3].InnerText.Trim();
  215. model = new OpenCode21Model();
  216. model.Term = Convert.ToInt64(tds[0].InnerText.Trim());
  217. model.OpenCode21 = Convert.ToInt32(tds[2].InnerText.Trim()); //飞盘
  218. openCodeList = tds[1].InnerText.Trim().Split(',');
  219. if (openCodeList.Length < 20) continue;
  220. model.OpenCode1 = Convert.ToInt32(openCodeList[0]);
  221. model.OpenCode2 = Convert.ToInt32(openCodeList[1]);
  222. model.OpenCode3 = Convert.ToInt32(openCodeList[2]);
  223. model.OpenCode4 = Convert.ToInt32(openCodeList[3]);
  224. model.OpenCode5 = Convert.ToInt32(openCodeList[4]);
  225. model.OpenCode6 = Convert.ToInt32(openCodeList[5]);
  226. model.OpenCode7 = Convert.ToInt32(openCodeList[6]);
  227. model.OpenCode8 = Convert.ToInt32(openCodeList[7]);
  228. model.OpenCode9 = Convert.ToInt32(openCodeList[8]);
  229. model.OpenCode10 = Convert.ToInt32(openCodeList[9]);
  230. model.OpenCode11 = Convert.ToInt32(openCodeList[10]);
  231. model.OpenCode12 = Convert.ToInt32(openCodeList[11]);
  232. model.OpenCode13 = Convert.ToInt32(openCodeList[12]);
  233. model.OpenCode14 = Convert.ToInt32(openCodeList[13]);
  234. model.OpenCode15 = Convert.ToInt32(openCodeList[14]);
  235. model.OpenCode16 = Convert.ToInt32(openCodeList[15]);
  236. model.OpenCode17 = Convert.ToInt32(openCodeList[16]);
  237. model.OpenCode18 = Convert.ToInt32(openCodeList[17]);
  238. model.OpenCode19 = Convert.ToInt32(openCodeList[18]);
  239. model.OpenCode20 = Convert.ToInt32(openCodeList[19]);
  240. model.OpenTime = Convert.ToDateTime(openTime);
  241. if (!result.Contains(model))
  242. result.Add(model);
  243. }
  244. var checkDataHelper = new CheckDataHelper();
  245. var data = services.GetListIn(currentLottery, IsToday);
  246. if (data != null)
  247. {
  248. var dbdata = services.GetListIn(currentLottery, IsToday)
  249. .ToDictionary(w => w.Term.ToString(), w => w.GetCodeStr());
  250. checkDataHelper.CheckData(dbdata, result.ToDictionary(w => w.Term.ToString(), w => w.GetCodeStr()),
  251. Config.Area, currentLottery);
  252. }
  253. else
  254. {
  255. log.Warn(GetType(), $"{Config.LotteryName}没有最新数据!");
  256. }
  257. }
  258. catch (Exception ex)
  259. {
  260. log.Error(GetType(),
  261. string.Format("【{0}】通过备用站点抓取开奖列表时发生错误,错误信息【{1}】", Config.Area + currentLottery, ex.Message));
  262. }
  263. return result;
  264. }
  265. /// <summary>
  266. /// 从主要链接查询开奖号码 昨天/今天
  267. /// </summary>
  268. /// <param name="IsToday"></param>
  269. /// <returns></returns>
  270. private List<OpenCode21Model> GetDocByMainUrl(bool IsToday = true)
  271. {
  272. var list = new List<OpenCode21Model>();
  273. try
  274. {
  275. var time = DateTime.Now;
  276. var arg = new OpenCaiApiArg
  277. {
  278. code = EnumHelper.GetLotteryCode(SCCLottery.BeiJingKL8),
  279. date = time.ToString("yyyy-MM-dd")
  280. };
  281. if (!IsToday) arg.date = time.AddDays(-1).ToString("yyyy-MM-dd");
  282. var data = OpenCaiApiServices.GetOpenCaiApiData(arg);
  283. if (data == null) return list;
  284. if (data.data != null)
  285. {
  286. if (data.data.Count == 0) return list;
  287. for (var i = 0; i < data.data.Count; i++)
  288. {
  289. var tmp = new OpenCode21Model();
  290. var codeAry = data.data[i].GetOpenCodeStr().Split(',');
  291. if (codeAry.Length == 21)
  292. {
  293. tmp.Term = long.Parse(data.data[i].expect);
  294. tmp.OpenTime = DateTime.Parse(data.data[i].opentime);
  295. tmp.OpenCode1 = int.Parse(codeAry[0]);
  296. tmp.OpenCode2 = int.Parse(codeAry[1]);
  297. tmp.OpenCode3 = int.Parse(codeAry[2]);
  298. tmp.OpenCode4 = int.Parse(codeAry[3]);
  299. tmp.OpenCode5 = int.Parse(codeAry[4]);
  300. tmp.OpenCode6 = int.Parse(codeAry[5]);
  301. tmp.OpenCode7 = int.Parse(codeAry[6]);
  302. tmp.OpenCode8 = int.Parse(codeAry[7]);
  303. tmp.OpenCode9 = int.Parse(codeAry[8]);
  304. tmp.OpenCode10 = int.Parse(codeAry[9]);
  305. tmp.OpenCode11 = int.Parse(codeAry[10]);
  306. tmp.OpenCode12 = int.Parse(codeAry[11]);
  307. tmp.OpenCode13 = int.Parse(codeAry[12]);
  308. tmp.OpenCode14 = int.Parse(codeAry[13]);
  309. tmp.OpenCode15 = int.Parse(codeAry[14]);
  310. tmp.OpenCode16 = int.Parse(codeAry[15]);
  311. tmp.OpenCode17 = int.Parse(codeAry[16]);
  312. tmp.OpenCode18 = int.Parse(codeAry[17]);
  313. tmp.OpenCode19 = int.Parse(codeAry[18]);
  314. tmp.OpenCode20 = int.Parse(codeAry[19]);
  315. tmp.OpenCode21 = int.Parse(codeAry[20]);
  316. }
  317. list.Add(tmp);
  318. }
  319. var checkDataHelper = new CheckDataHelper();
  320. var dbdata = services.GetListIn(currentLottery, IsToday);
  321. if (dbdata == null) return list;
  322. var DATA = dbdata.ToDictionary(w => w.Term.ToString(), w => w.GetCodeStr());
  323. checkDataHelper.CheckData(DATA, list.ToDictionary(w => w.Term.ToString(), w => w.GetCodeStr()),
  324. Config.Area, currentLottery);
  325. }
  326. }
  327. catch (Exception ee)
  328. {
  329. log.Error(GetType(),
  330. string.Format("【{0}】通过主要站点抓取开奖列表时发生错误,错误信息【{1}】", Config.Area + currentLottery, ee.Message));
  331. }
  332. return list;
  333. }
  334. /// <summary>
  335. /// 生成今日第一期记录
  336. /// </summary>
  337. /// <returns></returns>
  338. private OpenCode21Model GenerateTodayFirstItem()
  339. {
  340. var item = new OpenCode21Model();
  341. var currentDateTime = CommonHelper.SCCSysDateTime;
  342. var datepart = currentDateTime - new DateTime(2018, 1, 1);
  343. var t = Config.SkipDate.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
  344. item.Term = (datepart.Days - t.Length) * Config.TimesPerDay + LastTermLastYear; //期号使用昨日最后一期期号,便于计算使用
  345. item.OpenTime = new DateTime(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day,
  346. Config.StartHour, Config.StartMinute, 0);
  347. return item;
  348. }
  349. /// <summary>
  350. /// 核实最新一期开奖记录是否为今日最后一期
  351. /// </summary>
  352. /// <param name="QiHao">期号</param>
  353. /// <returns></returns>
  354. private bool CheckLatestItemIsTodayLastItem()
  355. {
  356. if (LatestItem == null ||
  357. LatestItem.OpenTime.ToString("yyyyMMdd") != CommonHelper.SCCSysDateTime.ToString("yyyyMMdd"))
  358. return true;
  359. var firstItem = GenerateTodayFirstItem();
  360. if (LatestItem.Term == firstItem.Term + Config.TimesPerDay)
  361. return true;
  362. return false;
  363. }
  364. /// <summary>
  365. /// 获取今天第一期期号
  366. /// </summary>
  367. /// <returns></returns>
  368. private string GenerateTodayFirstQiHao()
  369. {
  370. var datepart = CommonHelper.SCCSysDateTime - new DateTime(2018, 1, 1);
  371. var t = Config.SkipDate.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
  372. var terms = (datepart.Days - t.Length) * Config.TimesPerDay + LastTermLastYear + 1;
  373. return terms.ToString();
  374. }
  375. /// <summary>
  376. /// 生成昨天的期号列表
  377. /// </summary>
  378. /// <returns></returns>
  379. private List<string> GenerateYesterdayQiHaoList()
  380. {
  381. var result = new List<string>();
  382. var todayFirstQiHao = Convert.ToInt64(GenerateTodayFirstQiHao());
  383. for (var i = Config.TimesPerDay; i > 0; i--) result.Add((todayFirstQiHao - i).ToString());
  384. return result;
  385. }
  386. /// <summary>
  387. /// 生成快乐8的开奖时间
  388. /// </summary>
  389. /// <param name="QiHao"></param>
  390. /// <returns></returns>
  391. private DateTime GenerateYesterdayKL8OpenTime(string QiHao)
  392. {
  393. var openDay = CommonHelper.SCCSysDateTime.AddDays(-1);
  394. var StartTime = new DateTime(openDay.Year, openDay.Month, openDay.Day, Config.StartHour, Config.StartMinute,
  395. 0);
  396. var t = (Convert.ToInt64(QiHao) - LastTermLastYear) % Config.TimesPerDay;
  397. if (t == 0)
  398. return StartTime.AddMinutes((Config.TimesPerDay - 1) * Config.Interval);
  399. return StartTime.AddMinutes((t - 1) * Config.Interval);
  400. }
  401. #region Attribute
  402. /// <summary>
  403. /// 配置信息
  404. /// </summary>
  405. private SCCConfig Config;
  406. /// <summary>
  407. /// 当天抓取的最新一期开奖记录
  408. /// </summary>
  409. private OpenCode21Model LatestItem;
  410. /// <summary>
  411. /// 当天抓取失败列表
  412. /// </summary>
  413. private List<string> FailedQiHaoList;
  414. /// <summary>
  415. /// 日志对象
  416. /// </summary>
  417. private readonly LogHelper log;
  418. /// <summary>
  419. /// 数据服务
  420. /// </summary>
  421. private readonly IOpen21Code services;
  422. /// <summary>
  423. /// 当前彩种
  424. /// </summary>
  425. private SCCLottery currentLottery => SCCLottery.BeiJingKL8;
  426. /// <summary>
  427. /// 邮件接口
  428. /// </summary>
  429. private IEmail email;
  430. /// <summary>
  431. /// 2016年最后一期期号
  432. /// </summary>
  433. private readonly long LastTermLastYear = 872705;
  434. #endregion
  435. }
  436. }