XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFENewsApplication

【WinUI】新闻软件

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFENewsApplication

添加CCTV新闻源

f4005c6
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

7 个文件 +260 -31
Modified XFENewsApplication.Core/Utilities/Helpers/ClimbHelper.cs +28 -1
@@ -1,4 +1,5 @@
1 using System.Text.Json;
1 using System;
2 using System.Text.Json;
2 3 using System.Text.RegularExpressions;
3 4 using XFEExtension.NetCore.StringExtension;
4 5 using XFEExtension.NetCore.XFETransform.JsonConverter;
@@ -142,6 +143,32 @@ public static class ClimbHelper
142 143 };
143 144 }
144 145
146 public static async Task<NewsResult> ClimbCCTVNews(CancellationToken cancellationToken, int skip = 1, string type = "news")
147 {
148 var resultList = new List<NewsSource>();
149 using var client = new HttpClient();
150 client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0");
151 QueryableJsonNode jsonNode = await client.GetStringAsync($"https://news.cctv.com/2019/07/gaiban/cmsdatainterface/page/{type}_{skip}.jsonp?cb={type}");
152 foreach (var node in jsonNode["data"]["list"]["package:list", "brief", "title", "id", "url"].PackageInListObject())
153 {
154 resultList.Add(new NewsSource
155 {
156 Title = Regex.Unescape(node["title"].ToString()),
157 ID = node["id"].ToString(),
158 Url = Regex.Unescape(node["url"].ToString()),
159 Abstract = Regex.Unescape(node["brief"].ToString()),
160 Source = "CCTV"
161 });
162 }
163 return new()
164 {
165 NewsSourceList = resultList,
166 Success = resultList.Count > 0,
167 Message = resultList.Count > 0 ? "Success" : "没有获取到数据",
168 Count = resultList.Count
169 };
170 }
171
145 172 public static async Task<NewsResult> ClimbWeiboHotSearch(CancellationToken cancellationToken)
146 173 {
147 174 int index = 1;
Modified XFENewsApplication.Test/Program.cs +74 -5
@@ -1,19 +1,88 @@
1 1 using System.Text.RegularExpressions;
2 2 using XFEExtension.NetCore.StringExtension;
3 3 using XFEExtension.NetCore.XFETransform.JsonConverter;
4 using XFENewsApplication.Core.Utilities.Helpers;
4 5
5 6 class Program
6 7 {
7 //[SMTest(1)]
8 public static async Task TestCCTVClimb(int index)
8 //[SMTest]
9 public static async Task TestMethod()
10 {
11 foreach (var result in (await ClimbHelper.ClimbMSNNews(new CancellationToken())).NewsSourceList)
12 {
13 Console.WriteLine(result.Title);
14 }
15 }
16
17 [SMTest(1, "china")]
18 [SMTest(2, "life")]
19 [SMTest(1, "law")]
20 [SMTest(1, "society")]
21 [SMTest(2, "ent")]
22 [SMTest(1, "tech")]
23 [SMTest(1, "world")]
24 [SMTest(2, "china")]
25 [SMTest(2, "law")]
26 [SMTest(3, "tech")]
27 [SMTest(3, "world")]
28 [SMTest(1, "life")]
29 [SMTest(3, "society")]
30 [SMTest(2, "world")]
31 [SMTest(3, "law")]
32 [SMTest(1, "ent")]
33 [SMTest(3, "china")]
34 [SMTest(3, "ent")]
35 [SMTest(2, "society")]
36 [SMTest(3, "life")]
37 [SMTest(2, "tech")]
38 public static async Task TestCCTVClimb(int index, string type)
9 39 {
10 40 using var client = new HttpClient();
11 41 client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0");
12 QueryableJsonNode jsonNode = await client.GetStringAsync($"https://news.cctv.com/2019/07/gaiban/cmsdatainterface/page/news_{index}.jsonp?cb=news");
42 QueryableJsonNode jsonNode = await client.GetStringAsync($"https://news.cctv.com/2019/07/gaiban/cmsdatainterface/page/{type}_{index}.jsonp?cb={type}");
43 var file = File.OpenWrite("CCTV.csv");
44 var writer = new StreamWriter(file);
45 file.Position = file.Length;
13 46 foreach (var node in jsonNode["data"]["list"]["package:list", "brief", "title", "url"].PackageInListObject())
14 47 {
15 Console.WriteLine($"标题:{node["title"]}({node["url"]})\n{node["brief"]}\n");
48 //Console.WriteLine($"标题:{node["title"]}({node["url"]})\n{node["brief"]}\n");
49 writer.WriteLine($"{node["title"]},{type}");
16 50 }
51 writer.Close();
52 }
53
54 [SMTest(1)]
55 [SMTest(2)]
56 [SMTest(3)]
57 public static async Task TestCCTVEconomyClimb(int index)
58 {
59 using var client = new HttpClient();
60 client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0");
61 QueryableJsonNode jsonNode = await client.GetStringAsync($"https://news.cctv.com/2019/07/gaiban/cmsdatainterface/page/economy_zixun_{index}.jsonp?cb=economy_zixun");
62 var file = File.OpenWrite("CCTV.csv");
63 var writer = new StreamWriter(file);
64 file.Position = file.Length;
65 foreach (var node in jsonNode["data"]["list"]["package:list", "title"].PackageInListObject())
66 {
67 writer.WriteLine($"{node["title"]},eco");
68 }
69 writer.Close();
70 }
71
72 [SMTest]
73 public static async Task TestCCTVMilitaryClimb()
74 {
75 using var client = new HttpClient();
76 client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0");
77 QueryableJsonNode jsonNode = await client.GetStringAsync("https://military.cctv.com/data/index.json");
78 var file = File.OpenWrite("CCTV.csv");
79 var writer = new StreamWriter(file);
80 file.Position = file.Length;
81 foreach (var node in jsonNode["rollData"]["package:list", "title"].PackageInListObject())
82 {
83 writer.WriteLine($"{Regex.Unescape(node["title"].ToString())},army");
84 }
85 writer.Close();
17 86 }
18 87
19 88 //[SMTest]
@@ -37,7 +106,7 @@ class Program
37 106 }
38 107 }
39 108
40 [SMTest]
109 //[SMTest]
41 110 public static async Task TestBilibiliHotSearch()
42 111 {
43 112 using var client = new HttpClient();
Modified XFENewsApplication.Test/XFENewsApplication.Test.csproj +4 -0
@@ -13,4 +13,8 @@
13 13 <PackageReference Include="XFEExtension.NetCore.XUnit" Version="2.0.4" />
14 14 </ItemGroup>
15 15
16 <ItemGroup>
17 <ProjectReference Include="..\XFENewsApplication.Core\XFENewsApplication.Core.csproj" />
18 </ItemGroup>
19
16 20 </Project>
Modified XFENewsApplication/Utilities/Helper/ArticleDisplayHelper.cs +39 -18
@@ -1,7 +1,9 @@
1 1 using HtmlAgilityPack;
2 2 using Microsoft.UI.Text;
3 3 using Microsoft.UI.Xaml.Documents;
4 using System.Net;
4 5 using System.Text.Json;
6 using System.Text.RegularExpressions;
5 7 using XFEExtension.NetCore.StringExtension;
6 8 using XFENewsApplication.Models;
7 9
@@ -9,7 +11,24 @@ namespace XFENewsApplication.Utilities.Helper;
9 11
10 12 public static class ArticleDisplayHelper
11 13 {
12 public static List<ArticlePart> ConvertToArticlePart(string newsDocument)
14 public static List<ArticlePart> ConvertCCTVToArticlePart(string newsDocument)
15 {
16 var htmlDocument = new HtmlDocument();
17 htmlDocument.LoadHtml(newsDocument);
18 var articleParts = new List<ArticlePart>
19 {
20 new TextArticlePart
21 {
22 FontSize = 40,
23 FontWeight = FontWeights.Bold,
24 Content = (htmlDocument.DocumentNode.SelectSingleNode("/html/body/div[@id = 'page_body']/div/div/div[@class = 'title_area']/h1") ?? htmlDocument.DocumentNode.SelectSingleNode("//div[@class = 'page_wrap']/div/div[@class = 'title_area']/h1")).InnerText.Replace("\t", string.Empty).Replace(" ", string.Empty)
25 }
26 };
27 AnalysisMSNDocument(articleParts, articleParts[0].Content, [], (htmlDocument.DocumentNode.SelectSingleNode("/html/body/div[@id = 'page_body']/div/div/div[@class = 'content_area']") ?? htmlDocument.DocumentNode.SelectSingleNode("//div[@class = 'page_wrap']/div/div[@class = 'text_area']")).ChildNodes, htmlDocument.DocumentNode);
28 return articleParts;
29 }
30
31 public static List<ArticlePart> ConvertMSNNewsToArticlePart(string newsDocument)
13 32 {
14 33 var articleParts = new List<ArticlePart>();
15 34 var imageDictionary = new Dictionary<string, string>();
@@ -89,7 +108,7 @@ public static class ArticleDisplayHelper
89 108 case "img":
90 109 articleParts.Add(new ImageArticlePart
91 110 {
92 Content = imageDictionary[node.GetAttributeValue("data-document-id", string.Empty).Replace("\"", string.Empty)],
111 Content = imageDictionary.Count > 0 ? imageDictionary[node.GetAttributeValue("data-document-id", string.Empty).Replace("\"", string.Empty)] : $"https:{node.GetAttributeValue("src", string.Empty)}",
93 112 From = title
94 113 });
95 114 break;
@@ -101,29 +120,37 @@ public static class ArticleDisplayHelper
101 120 case "p":
102 121 CheckLastArticlePart(articleParts, parentNode, new Run
103 122 {
104 Text = node.InnerText
123 Text = WebUtility.HtmlDecode(node.InnerText)
105 124 });
106 125 break;
107 126 case "span":
108 127 CheckLastArticlePart(articleParts, parentNode.ParentNode, new Run
109 128 {
110 Text = node.InnerText
129 Text = WebUtility.HtmlDecode(node.InnerText)
111 130 });
112 131 break;
113 132 case "strong":
114 133 var bold = new Bold();
115 134 bold.Inlines.Add(new Run
116 135 {
117 Text = node.InnerText
136 Text = WebUtility.HtmlDecode(node.InnerText)
118 137 });
119 138 CheckLastArticlePart(articleParts, parentNode.ParentNode, bold);
120 139 break;
140 case "em":
141 var italic = new Italic();
142 italic.Inlines.Add(new Run
143 {
144 Text = WebUtility.HtmlDecode(node.InnerText)
145 });
146 CheckLastArticlePart(articleParts, parentNode.ParentNode, italic);
147 break;
121 148 case "blockquote":
122 149 if (node.InnerText.IsNullOrWhiteSpace())
123 150 continue;
124 151 articleParts.Add(new QuoteArticlePart
125 152 {
126 Content = node.InnerText,
153 Content = WebUtility.HtmlDecode(node.InnerText),
127 154 FontSize = 36,
128 155 FontWeight = FontWeights.SemiBold
129 156 });
@@ -131,7 +158,7 @@ public static class ArticleDisplayHelper
131 158 case "h1":
132 159 articleParts.Add(new TitleArticlePart
133 160 {
134 Content = node.InnerText,
161 Content = WebUtility.HtmlDecode(node.InnerText),
135 162 FontSize = 68,
136 163 FontWeight = FontWeights.SemiBold
137 164 });
@@ -139,7 +166,7 @@ public static class ArticleDisplayHelper
139 166 case "h2":
140 167 articleParts.Add(new TitleArticlePart
141 168 {
142 Content = node.InnerText,
169 Content = WebUtility.HtmlDecode(node.InnerText),
143 170 FontSize = 48,
144 171 FontWeight = FontWeights.SemiBold
145 172 });
@@ -147,7 +174,7 @@ public static class ArticleDisplayHelper
147 174 case "h3":
148 175 articleParts.Add(new TitleArticlePart
149 176 {
150 Content = node.InnerText,
177 Content = WebUtility.HtmlDecode(node.InnerText),
151 178 FontSize = 36,
152 179 FontWeight = FontWeights.SemiBold
153 180 });
@@ -155,7 +182,7 @@ public static class ArticleDisplayHelper
155 182 case "h4":
156 183 articleParts.Add(new TitleArticlePart
157 184 {
158 Content = node.InnerText,
185 Content = WebUtility.HtmlDecode(node.InnerText),
159 186 FontSize = 28,
160 187 FontWeight = FontWeights.SemiBold
161 188 });
@@ -163,7 +190,7 @@ public static class ArticleDisplayHelper
163 190 case "h5":
164 191 articleParts.Add(new TitleArticlePart
165 192 {
166 Content = node.InnerText,
193 Content = WebUtility.HtmlDecode(node.InnerText),
167 194 FontSize = 20,
168 195 FontWeight = FontWeights.SemiBold
169 196 });
@@ -171,18 +198,12 @@ public static class ArticleDisplayHelper
171 198 case "h6":
172 199 articleParts.Add(new TitleArticlePart
173 200 {
174 Content = node.InnerText,
201 Content = WebUtility.HtmlDecode(node.InnerText),
175 202 FontSize = 16,
176 203 FontWeight = FontWeights.SemiBold
177 204 });
178 205 break;
179 206 default:
180 if (node.InnerText.IsNullOrWhiteSpace())
181 continue;
182 articleParts.Add(new TextArticlePart
183 {
184 Content = node.InnerText
185 });
186 207 break;
187 208 }
188 209 break;
Modified XFENewsApplication/ViewModels/NewsViewPageViewModel.cs +109 -7
@@ -114,12 +114,55 @@ public partial class NewsViewPageViewModel : ViewModelBase
114 114 }
115 115 else
116 116 {
117 skip = 0;
118 servedCardCount = 0;
119 lastCardCount = 0;
120 lastSelectedNews = 0;
117 121 await GetNews();
118 122 }
119 123 SelectedIndex = lastSelectedNews;
120 124 IsSideBarMoreContentLoadingVisible = true;
121 125 lastNewsSource = "MSN";
122 126 break;
127 case "CCTV":
128 if (NavigationViewService is not null)
129 {
130 NavigationViewService.ContentMargin = new(0, 24, 0, 0);
131 var stackPanel = new StackPanel
132 {
133 Orientation = Orientation.Horizontal,
134 Spacing = 24
135 };
136 stackPanel.Children.Add(new Image
137 {
138 Source = new BitmapImage(new("https://news.cctv.com/favicon.ico"))
139 });
140 stackPanel.Children.Add(new TextBlock
141 {
142 Text = "CCTV新闻",
143 FontSize = 28,
144 FontWeight = FontWeights.SemiBold
145 });
146 NavigationViewService.Header = stackPanel;
147 }
148 if (lastNewsList.Count > 0 && lastNewsSource == "CCTV")
149 {
150 newsList = lastNewsList;
151 LoadNewsSource(newsList);
152 NewsListViewService.ListView.ScrollIntoView(NewsList[lastSelectedNews], ScrollIntoViewAlignment.Leading);
153 }
154 else
155 {
156 skip = 0;
157 servedCardCount = 0;
158 lastCardCount = 0;
159 lastSelectedNews = 0;
160 await GetNews();
161 }
162 SelectedIndex = lastSelectedNews;
163 IsSideBarMoreContentLoadingVisible = true;
164 lastNewsSource = "CCTV";
165 break;
123 166 case "Favorite":
124 167 IsSideBarMoreContentLoadingVisible = false;
125 168 if (NavigationViewService is not null)
@@ -208,7 +251,17 @@ public partial class NewsViewPageViewModel : ViewModelBase
208 251 {
209 252 ContentList.Clear();
210 253 currentNewsId = newsSource.ID;
211 LoadContentList(ArticleDisplayHelper.ConvertToArticlePart(article.ArticleContent));
254 switch (NavigationParameterService.Parameter)
255 {
256 case "MSN":
257 LoadContentList(ArticleDisplayHelper.ConvertMSNNewsToArticlePart(article.ArticleContent));
258 break;
259 case "CCTV":
260 LoadContentList(ArticleDisplayHelper.ConvertCCTVToArticlePart(article.ArticleContent));
261 break;
262 default:
263 break;
264 }
212 265 }
213 266 }
214 267
@@ -236,7 +289,22 @@ public partial class NewsViewPageViewModel : ViewModelBase
236 289 currentNewsContent = result;
237 290 if (!CheckCurrentNewsSource(newsSource.ID))
238 291 return;
239 var articleParts = ArticleDisplayHelper.ConvertToArticlePart(result);
292 var articleParts = ArticleDisplayHelper.ConvertMSNNewsToArticlePart(result);
293 if (!CheckCurrentNewsSource(newsSource.ID))
294 return;
295 LoadContentList(articleParts);
296 HistoryManager.Visit(newsSource.Url, newsSource);
297 UserProfile.HistoryArticleList = [.. HistoryManager.Export()];
298 IsContentLoadingVisible = false;
299 return;
300 case "CCTV":
301 if (NavigationParameterService.Parameter == "CCTV")
302 lastSelectedNews = SelectedIndex;
303 result = await client.GetStringAsync(newsSource.Url);
304 currentNewsContent = result;
305 if (!CheckCurrentNewsSource(newsSource.ID))
306 return;
307 articleParts = ArticleDisplayHelper.ConvertCCTVToArticlePart(result);
240 308 if (!CheckCurrentNewsSource(newsSource.ID))
241 309 return;
242 310 LoadContentList(articleParts);
@@ -272,13 +340,9 @@ public partial class NewsViewPageViewModel : ViewModelBase
272 340 ContentList.Add(article);
273 341 }
274 342
275 [RelayCommand]
276 async Task GetNews()
343 private async Task GetMSNNews(CancellationTokenSource tokenSource)
277 344 {
278 isLoadingNews = true;
279 345 int totalCount = 0;
280 var tokenSource = new CancellationTokenSource();
281 TokenSource = tokenSource;
282 346 while (!tokenSource.IsCancellationRequested)
283 347 {
284 348 var result = await ClimbHelper.ClimbMSNNews(tokenSource.Token, skip, lastCardCount, servedCardCount);
@@ -304,6 +368,44 @@ public partial class NewsViewPageViewModel : ViewModelBase
304 368 MessageService?.ShowMessage(result.Message ?? "未知原因", "无法获取新闻", InfoBarSeverity.Warning);
305 369 }
306 370 }
371 }
372
373 private async Task GetCCTVNews(CancellationTokenSource tokenSource)
374 {
375 var result = await ClimbHelper.ClimbCCTVNews(tokenSource.Token, skip == 0 ? 1 : skip);
376 if (result.Success)
377 {
378 newsList.AddRange(result.NewsSourceList);
379 lastNewsList.AddRange(result.NewsSourceList);
380 if (!tokenSource.IsCancellationRequested)
381 {
382 LoadNewsSource(result.NewsSourceList);
383 skip++;
384 }
385 }
386 else
387 {
388 MessageService?.ShowMessage(result.Message ?? "未知原因", "无法获取新闻", InfoBarSeverity.Warning);
389 }
390 }
391
392 [RelayCommand]
393 async Task GetNews()
394 {
395 isLoadingNews = true;
396 var tokenSource = new CancellationTokenSource();
397 TokenSource = tokenSource;
398 switch (NavigationParameterService.Parameter)
399 {
400 case "MSN":
401 await GetMSNNews(tokenSource);
402 break;
403 case "CCTV":
404 await GetCCTVNews(tokenSource);
405 break;
406 default:
407 break;
408 }
307 409 isLoadingNews = false;
308 410 }
309 411
Modified XFENewsApplication/Views/AppShellPage.xaml +5 -0
@@ -41,6 +41,11 @@
41 41 <ImageIcon Source="https://www.msn.com/favicon.ico" Width="16" Height="16"/>
42 42 </NavigationViewItem.Icon>
43 43 </NavigationViewItem>
44 <NavigationViewItem Content="CCTV 新闻" add:NavigationAddition.NavigateTo="XFENewsApplication.Views.NewsViewPage" add:NavigationAddition.NavigateParameter="CCTV">
45 <NavigationViewItem.Icon>
46 <ImageIcon Source="https://news.cctv.com/favicon.ico" Width="16" Height="16"/>
47 </NavigationViewItem.Icon>
48 </NavigationViewItem>
44 49 <NavigationViewItem Content="微博 热搜" add:NavigationAddition.NavigateTo="XFENewsApplication.Views.HotSearchViewPage" add:NavigationAddition.NavigateParameter="Weibo">
45 50 <NavigationViewItem.Icon>
46 51 <ImageIcon Source="https://m.weibo.cn/favicon.ico" Width="16" Height="16"/>
Modified XFENewsApplication/Views/NewsViewPage.xaml.cs +1 -0
@@ -31,6 +31,7 @@ public sealed partial class NewsViewPage : Page
31 31 {
32 32 base.OnNavigatedFrom(e);
33 33 ViewModel.TokenSource?.Cancel();
34 ViewModel.SelectedIndex = -1;
34 35 if (ViewModel.NavigationViewService is not null)
35 36 ViewModel.NavigationViewService.ContentMargin = new(56, 24, 56, 0);
36 37 }