HD15X5Job.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. using HtmlAgilityPack;
  7. using Newtonsoft.Json.Linq;
  8. using Quartz;
  9. using SCC.Common;
  10. using SCC.Crawler.Tools;
  11. using SCC.Interface;
  12. using SCC.Models;
  13. namespace SCC.Crawler.DT
  14. {
  15. /// <summary>
  16. /// 数据爬取类
  17. /// 华东15选5
  18. /// </summary>
  19. [DisallowConcurrentExecution]
  20. [PersistJobDataAfterExecution]
  21. public class HD15X5Job : IJob
  22. {
  23. /// <summary>
  24. /// 构造函数
  25. /// </summary>
  26. public HD15X5Job()
  27. {
  28. log = new LogHelper();
  29. services = IOC.Resolve<IDTOpenCode>();
  30. email = IOC.Resolve<IEmail>();
  31. }
  32. /// <summary>
  33. /// 作业执行入口
  34. /// </summary>
  35. /// <param name="context">作业执行上下文</param>
  36. public void Execute(IJobExecutionContext context)
  37. {
  38. Config = CommonHelper.GetConfigFromDataMap(context.JobDetail.JobDataMap);
  39. //预设节假日不开奖
  40. if (Config.SkipDate.Contains(CommonHelper.SCCSysDateTime.ToString("yyyyMMdd"))) return;
  41. LatestItem = context.JobDetail.JobDataMap["LatestItem"] as OpenCode5DTModel;
  42. try
  43. {
  44. //服务启动时配置初始数据
  45. if (LatestItem == null)
  46. {
  47. LatestItem = services.GetOpenCode5DTLastItem(currentLottery);
  48. if (LatestItem == null)
  49. LatestItem = new OpenCode5DTModel
  50. {
  51. Term = CommonHelper.GenerateQiHaoYYYYQQQ(0),
  52. OpenTime = new DateTime(CommonHelper.SCCSysDateTime.Year, 1, 1)
  53. };
  54. }
  55. //程序时间第二天,程序根据配置检查是否昨天有开奖
  56. isGetData = false;
  57. if (CommonHelper.CheckDTIsNeedGetData(Config))
  58. {
  59. DoMainUrl();
  60. DoBackUrl();
  61. }
  62. if (!LatestItem.Term.ToString().StartsWith(CommonHelper.SCCSysDateTime.ToString("yyyy")))
  63. LatestItem = new OpenCode5DTModel
  64. {
  65. Term = CommonHelper.GenerateQiHaoYYYYQQQ(0),
  66. OpenTime = new DateTime(CommonHelper.SCCSysDateTime.Year, 1, 1)
  67. };
  68. //当今日开奖并且当前时间是晚上8点过后开始抓取
  69. if (CommonHelper.CheckTodayIsOpenDay(Config) && CommonHelper.SCCSysDateTime.Hour > 12)
  70. {
  71. DoMainUrl();
  72. DoBackUrl();
  73. }
  74. }
  75. catch (Exception ex)
  76. {
  77. log.Error(GetType(), string.Format("【{0}】抓取时发生错误,错误信息【{1}】", Config.Area + currentLottery, ex.Message));
  78. }
  79. //保存最新期号
  80. context.JobDetail.JobDataMap["LatestItem"] = LatestItem;
  81. }
  82. /// <summary>
  83. /// 通过主站点爬取开奖数据
  84. /// (江苏福彩网)
  85. /// </summary>
  86. private void DoMainUrl()
  87. {
  88. if (!string.IsNullOrEmpty(Config.MainUrl))
  89. {
  90. var OpenList = GetOpenListFromMainUrl(Config.MainUrl);
  91. if (OpenList.Count == 0) return; //无抓取数据
  92. var newestQiHao = OpenList.Max(w => w.Term).ToString();
  93. var startQiNum = Convert.ToInt32(LatestItem.Term.ToString().Substring(4)) + 1;
  94. var newestQiNum = Convert.ToInt32(newestQiHao.Substring(4));
  95. if (startQiNum > newestQiNum) return; //无最新数据
  96. //处理最新开奖数据
  97. var getQiHao = string.Empty;
  98. OpenCode5DTModel matchItem = null;
  99. for (var i = startQiNum; i <= newestQiNum; i++)
  100. {
  101. getQiHao = LatestItem.Term.ToString().Substring(0, 4) + i.ToString().PadLeft(3, '0');
  102. matchItem = OpenList.Where(R => R.Term.ToString() == getQiHao).FirstOrDefault();
  103. if (matchItem != null && OptimizeMainModel(ref matchItem) &&
  104. services.AddDTOpen5Code(currentLottery, matchItem))
  105. {
  106. //Do Success Log
  107. log.Info(GetType(), CommonHelper.GetJobMainLogInfo(Config, getQiHao));
  108. LatestItem = matchItem;
  109. isGetData = true;
  110. }
  111. }
  112. }
  113. }
  114. /// <summary>
  115. /// 获取主站开奖列表数据
  116. /// </summary>
  117. /// <param name="mainUrl">主站地址</param>
  118. /// <returns></returns>
  119. private List<OpenCode5DTModel> GetOpenListFromMainUrl(string mainUrl)
  120. {
  121. var result = new List<OpenCode5DTModel>();
  122. try
  123. {
  124. var postData = "PlayType=5&currentPage=1&pageSize=200";
  125. var htmlResource = NetHelper.GetUrlResponse(mainUrl, "POST", postData, Encoding.UTF8);
  126. if (htmlResource == null) return result;
  127. var obj = htmlResource.JsonToEntity<JObject>();
  128. var lotteryList = obj["LotteryNumberList"];
  129. OpenCode5DTModel model = null;
  130. string[] openCodeList = null;
  131. var issue = string.Empty;
  132. foreach (var item in lotteryList)
  133. {
  134. issue = item["Issue"].ToString();
  135. if (issue.StartsWith((CommonHelper.SCCSysDateTime.Year - 1).ToString())) break; //只抓取今年数据
  136. model = new OpenCode5DTModel();
  137. model.Term = Convert.ToInt64(issue);
  138. model.OpenTime = Convert.ToDateTime(item["LotteryDate"].ToString());
  139. openCodeList = item["BasicNumber"].ToString().Trim().Replace("[", string.Empty)
  140. .Replace("]", string.Empty).Replace("\r\n", string.Empty).Replace("\"", string.Empty)
  141. .Split(',');
  142. if (openCodeList.Length != 5) continue;
  143. model.OpenCode1 = Convert.ToInt32(openCodeList[0]);
  144. model.OpenCode2 = Convert.ToInt32(openCodeList[1]);
  145. model.OpenCode3 = Convert.ToInt32(openCodeList[2]);
  146. model.OpenCode4 = Convert.ToInt32(openCodeList[3]);
  147. model.OpenCode5 = Convert.ToInt32(openCodeList[4]);
  148. model.Spare = string.IsNullOrEmpty(item["PrizePool"].ToString().Replace("万元", string.Empty))
  149. ? "0"
  150. : item["PrizePool"].ToString().Replace("万元", string.Empty);
  151. result.Add(model);
  152. }
  153. var checkDataHelper = new CheckDataHelper();
  154. var dbdata = services.GetListS<OpenCode5DTModel>(currentLottery)
  155. .ToDictionary(w => w.Term.ToString(), w => w.GetCodeStr());
  156. checkDataHelper.CheckData(dbdata, result.ToDictionary(w => w.Term.ToString(), w => w.GetCodeStr()),
  157. Config.Area, currentLottery);
  158. result = result.OrderByDescending(S => S.Term).ToList();
  159. }
  160. catch (Exception ex)
  161. {
  162. log.Error(GetType(),
  163. string.Format("【{0}】通过主站点抓取开奖列表时发生错误,错误信息【{1}】", Config.Area + currentLottery, ex.Message));
  164. }
  165. return result;
  166. }
  167. /// <summary>
  168. /// 完善备用站点江苏体彩7位数开奖实体信息
  169. /// </summary>
  170. /// <param name="model"></param>
  171. private bool OptimizeBackModel(ref OpenCode5DTModel model, HtmlNode tr)
  172. {
  173. try
  174. {
  175. var entity = new KaijiangDetailsEntity();
  176. entity.KaiJiangItems = new List<Kaijiangitem>();
  177. var tds = tr.ChildNodes.Where(w => w.Name == "td").ToList();
  178. var xiaoshoue = tds[2].InnerText.Replace(",", "").Trim();
  179. var jiangchi = "0";
  180. var tedengjiang = new Kaijiangitem
  181. {
  182. Name = "特等奖",
  183. Total = tds[3].InnerText.Trim(),
  184. TotalMoney = tds[4].InnerText.Trim()
  185. };
  186. entity.KaiJiangItems.Add(tedengjiang);
  187. var yidengjiang = new Kaijiangitem
  188. {
  189. Name = "一等奖",
  190. Total = tds[5].InnerText.Trim(),
  191. TotalMoney = tds[6].InnerText.Trim()
  192. };
  193. entity.KaiJiangItems.Add(yidengjiang);
  194. var erdengjiang = new Kaijiangitem
  195. {
  196. Name = "二等奖",
  197. Total = tds[7].InnerText.Trim(),
  198. TotalMoney = tds[8].InnerText.Trim()
  199. };
  200. entity.KaiJiangItems.Add(erdengjiang);
  201. entity.Trje = xiaoshoue;
  202. entity.Gdje = jiangchi;
  203. model.Spare = entity.TryToJson();
  204. }
  205. catch (Exception ex)
  206. {
  207. log.Error(GetType(),
  208. string.Format("【{0}】通过备用站点优化开奖列表时发生错误,错误信息【{1}】", Config.Area + currentLottery, ex.Message));
  209. }
  210. return false;
  211. }
  212. /// <summary>
  213. /// 完善主站江苏体彩7位数开奖详情信息
  214. /// </summary>
  215. /// <param name="model"></param>
  216. private bool OptimizeMainModel(ref OpenCode5DTModel model)
  217. {
  218. var url = string.Format("http://www.jslottery.com/Lottery/LotteryDetails?PlayType=5&Issue={0}", model.Term);
  219. try
  220. {
  221. var entity = new KaijiangDetailsEntity();
  222. entity.KaiJiangItems = new List<Kaijiangitem>();
  223. var htmlResource = NetHelper.GetUrlResponse(url);
  224. if (!string.IsNullOrEmpty(htmlResource))
  225. {
  226. var doc = new HtmlDocument();
  227. doc.LoadHtml(htmlResource);
  228. var tables = doc.DocumentNode.SelectNodes("//table");
  229. if (tables.Count < 5) return false;
  230. var trs = tables[4].ChildNodes[0].ChildNodes.Where(S => S.Name.ToLower() == "tr").ToList();
  231. foreach (var tr in trs)
  232. {
  233. var tds = tr.ChildNodes.Where(S => S.Name.ToLower() == "td").ToList();
  234. if (tds.Count < 3) continue;
  235. if (tds[0].InnerText == "特等奖")
  236. {
  237. var tmp = new Kaijiangitem
  238. {
  239. Name = "特等奖",
  240. Total = tds[1].InnerText,
  241. TotalMoney = tds[2].InnerText
  242. };
  243. entity.KaiJiangItems.Add(tmp);
  244. // Level1Num = Convert.ToInt32(tds[1].InnerText);
  245. // Level1Money = Convert.ToInt32(tds[2].InnerText);
  246. }
  247. else if (tds[0].InnerText == "一等奖")
  248. {
  249. var tmp = new Kaijiangitem
  250. {
  251. Name = "一等奖",
  252. Total = tds[1].InnerText,
  253. TotalMoney = tds[2].InnerText
  254. };
  255. entity.KaiJiangItems.Add(tmp);
  256. }
  257. else if (tds[0].InnerText == "二等奖")
  258. {
  259. var tmp = new Kaijiangitem
  260. {
  261. Name = "二等奖",
  262. Total = tds[1].InnerText,
  263. TotalMoney = tds[2].InnerText
  264. };
  265. entity.KaiJiangItems.Add(tmp);
  266. }
  267. }
  268. var reg = new Regex(@"本期投注总额<span>([\d.,]*?)</span>");
  269. var match = reg.Match(htmlResource);
  270. if (match.Success)
  271. {
  272. entity.Trje = (Convert.ToDecimal(match.Result("$1").Replace(",", string.Empty)) * 10000)
  273. .ToString();
  274. }
  275. else
  276. {
  277. reg = new Regex(@"本期投注总额<span lang=""EN-US"">([\d.,]*?)</span>");
  278. match = reg.Match(htmlResource);
  279. if (match.Success)
  280. entity.Trje = (Convert.ToDecimal(match.Result("$1").Replace(",", string.Empty)) * 10000)
  281. .ToString();
  282. }
  283. entity.Gdje = (Convert.ToDecimal(model.Spare.Replace(",", string.Empty)) * 10000).ToString();
  284. model.Spare = entity.TryToJson();
  285. return true;
  286. }
  287. }
  288. catch (Exception ex)
  289. {
  290. log.Error(GetType(),
  291. string.Format("【{0}】通过主站点完善抓取开奖列表时发生错误,错误信息【{1}】", Config.Area + currentLottery, ex.Message));
  292. }
  293. return false;
  294. }
  295. /// <summary>
  296. /// 通过备用站点抓取开奖数据
  297. /// (百度乐彩)
  298. /// </summary>
  299. private void DoBackUrl()
  300. {
  301. if (!string.IsNullOrEmpty(Config.BackUrl))
  302. {
  303. var OpenList = GetOpenListFromBackUrl();
  304. if (OpenList.Count == 0) return; //无抓取数据
  305. var newestQiHao = OpenList.First().Term.ToString();
  306. var startQiNum = Convert.ToInt32(LatestItem.Term.ToString().Substring(4)) + 1;
  307. var newestQiNum = Convert.ToInt32(newestQiHao.Substring(4));
  308. if (startQiNum > newestQiNum) return; //无最新数据
  309. //处理最新开奖数据
  310. var getQiHao = string.Empty;
  311. OpenCode5DTModel matchItem = null;
  312. for (var i = startQiNum; i <= newestQiNum; i++)
  313. {
  314. getQiHao = LatestItem.Term.ToString().Substring(0, 4) + i.ToString().PadLeft(3, '0');
  315. matchItem = OpenList.Where(R => R.Term.ToString() == getQiHao).FirstOrDefault();
  316. if (matchItem != null && services.AddDTOpen5Code(currentLottery, matchItem))
  317. {
  318. //Do Success Log
  319. log.Info(GetType(), CommonHelper.GetJobBackLogInfo(Config, getQiHao));
  320. LatestItem = matchItem;
  321. isGetData = true;
  322. }
  323. }
  324. }
  325. }
  326. /// <summary>
  327. /// 获取备用站点开奖列表数据
  328. /// </summary>
  329. /// <param name="backUrl">备用站点</param>
  330. /// <returns></returns>
  331. private List<OpenCode5DTModel> GetOpenListFromBackUrl()
  332. {
  333. var result = new List<OpenCode5DTModel>();
  334. try
  335. {
  336. var htmlResource = NetHelper.GetUrlResponse(Config.BackUrl);
  337. if (htmlResource == null) return result;
  338. var doc = new HtmlDocument();
  339. doc.LoadHtml(htmlResource);
  340. var table = doc.DocumentNode.SelectSingleNode("//table");
  341. if (table == null) return result;
  342. var trs = table.ChildNodes.Where(node => node.Name == "tr").ToList();
  343. trs.RemoveAll(w => w.GetAttributeValue("class", "") != "");
  344. foreach (var item in trs)
  345. {
  346. var tds = item.ChildNodes.Where(w => w.Name == "td").ToList();
  347. ;
  348. var qihao = tds[0].InnerText.Trim();
  349. var kaijianghao = tds[1].InnerText.Trim();
  350. var opentime = tds[9].InnerText.Trim();
  351. var tmp = new OpenCode5DTModel
  352. {
  353. Term = long.Parse(qihao),
  354. OpenTime = DateTime.Parse(opentime)
  355. };
  356. tmp.OpenCode1 = int.Parse(kaijianghao.Substring(0, 2));
  357. tmp.OpenCode2 = int.Parse(kaijianghao.Substring(2, 2));
  358. tmp.OpenCode3 = int.Parse(kaijianghao.Substring(4, 2));
  359. tmp.OpenCode4 = int.Parse(kaijianghao.Substring(6, 2));
  360. tmp.OpenCode5 = int.Parse(kaijianghao.Substring(8, 2));
  361. OptimizeBackModel(ref tmp, item);
  362. result.Add(tmp);
  363. }
  364. var checkDataHelper = new CheckDataHelper();
  365. var dbdata = services.GetListS<OpenCode5DTModel>(currentLottery)
  366. .ToDictionary(w => w.Term.ToString(), w => w.GetCodeStr());
  367. checkDataHelper.CheckData(dbdata, result.ToDictionary(w => w.Term.ToString(), w => w.GetCodeStr()),
  368. Config.Area, currentLottery);
  369. result = result.OrderByDescending(S => S.Term).ToList();
  370. }
  371. catch (Exception ex)
  372. {
  373. log.Error(GetType(),
  374. string.Format("【{0}】通过备用站点抓取开奖列表时发生错误,错误信息【{1}】", Config.Area + currentLottery, ex.Message));
  375. }
  376. return result;
  377. }
  378. #region Attribute
  379. /// <summary>
  380. /// 配置信息
  381. /// </summary>
  382. private SCCConfig Config;
  383. /// <summary>
  384. /// 当天抓取的最新一期开奖记录
  385. /// </summary>
  386. private OpenCode5DTModel LatestItem;
  387. /// <summary>
  388. /// 当天抓取失败列表
  389. /// </summary>
  390. private List<string> FailedQiHaoList = null;
  391. /// <summary>
  392. /// 日志对象
  393. /// </summary>
  394. private readonly LogHelper log;
  395. /// <summary>
  396. /// 数据服务
  397. /// </summary>
  398. private readonly IDTOpenCode services;
  399. /// <summary>
  400. /// 当前彩种
  401. /// </summary>
  402. private SCCLottery currentLottery => SCCLottery.HD15X5;
  403. /// <summary>
  404. /// 邮件接口
  405. /// </summary>
  406. private IEmail email;
  407. /// <summary>
  408. /// 是否本次运行抓取到开奖数据
  409. /// </summary>
  410. private bool isGetData;
  411. #endregion
  412. }
  413. }