返回提交历史
Modified
XFENewsApplication.Core/Utilities/Helpers/ClimbHelper.cs
+28
-1
Modified
XFENewsApplication.Test/Program.cs
+74
-5
Modified
XFENewsApplication.Test/XFENewsApplication.Test.csproj
+4
-0
Modified
XFENewsApplication/Utilities/Helper/ArticleDisplayHelper.cs
+39
-18
Modified
XFENewsApplication/ViewModels/NewsViewPageViewModel.cs
+109
-7
Modified
XFENewsApplication/Views/AppShellPage.xaml
+5
-0
Modified
XFENewsApplication/Views/NewsViewPage.xaml.cs
+1
-0
XFEstudio/XFENewsApplication
添加CCTV新闻源
f4005c6
代码差异
7 个文件
+260
-31
@@ -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;
@@ -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();
@@ -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>
@@ -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;
@@ -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
@@ -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"/>
@@ -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
}