注意:本程序基于Deep Seek v4 Flash构建,可能存在尚未解决的问题
本程序从中新网("https://www.chinanews.com.cn/rss/scroll-news.xml","https://www.chinanews.com.cn/rss/edu.xml",可自行调整,详见源码)获取当日新闻,在格式化后推送至 http://localhost:36000/news (可自行调整,详见源码)
使用方法:
- 安装ClassIsland的PutIsland插件;
- 修改源码中所要获取的新闻地址(详见
https://www.chinanews.cn/rss );
- 使用适配C++11版本的g++编译器将本程序构建为exe可执行程序并放置在合理位置;
- 在ClassIsland的组件设置中添加
网络文本 组件并将 Token 设置为 news ;
- 添加ClassIsland自动化以实现自动获取新闻文本;
- 建议设置对应组件自动隐藏以减少不必要的影响.
以下为对应源码:
// 编译命令 g++ -std=c++11 PathToThis.cpp -o Program.exe -lwinhttp
#include <windows.h>
#include <winhttp.h>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <fstream>
#pragma comment(lib, "winhttp.lib")
// 新闻条目结构体
struct NewsItem {
std::string title;
std::string desc;
};
// ---------- 1. HTTP GET 获取 RSS ----------
std::string httpGet(const std::string& url) {
std::string result;
std::wstring wurl(url.begin(), url.end());
URL_COMPONENTS urlComp = { 0 };
urlComp.dwStructSize = sizeof(urlComp);
wchar_t hostName[512] = { 0 };
wchar_t urlPath[2048] = { 0 };
urlComp.lpszHostName = hostName;
urlComp.dwHostNameLength = 512;
urlComp.lpszUrlPath = urlPath;
urlComp.dwUrlPathLength = 2048;
if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &urlComp))
return result;
HINTERNET hSession = WinHttpOpen(L"RSS Reader/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0);
if (!hSession) return result;
HINTERNET hConnect = WinHttpConnect(hSession, hostName, urlComp.nPort, 0);
if (!hConnect) { WinHttpCloseHandle(hSession); return result; }
DWORD flags = (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", urlPath, NULL, NULL, NULL, flags);
if (!hRequest) { WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return result; }
BOOL bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
if (bResults) bResults = WinHttpReceiveResponse(hRequest, NULL);
if (bResults) {
DWORD bytesRead = 0;
char buffer[4096];
while (WinHttpReadData(hRequest, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) {
result.append(buffer, bytesRead);
}
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return result;
}
// ---------- 2. 简易 XML 解析 ----------
std::string extractTagContent(const std::string& xml, const std::string& tag) {
std::string open = "<" + tag + ">";
std::string close = "</" + tag + ">";
size_t start = xml.find(open);
if (start == std::string::npos) return "";
start += open.length();
size_t end = xml.find(close, start);
if (end == std::string::npos) return "";
std::string content = xml.substr(start, end - start);
size_t cstart = content.find("<![CDATA[");
if (cstart != std::string::npos) {
content = content.substr(cstart + 9);
size_t cend = content.find("]]>");
if (cend != std::string::npos) content = content.substr(0, cend);
}
return content;
}
std::vector<NewsItem> parseRSS(const std::string& xml) {
std::vector<NewsItem> items;
size_t pos = 0;
while (true) {
size_t itemStart = xml.find("<item>", pos);
if (itemStart == std::string::npos) break;
size_t itemEnd = xml.find("</item>", itemStart);
if (itemEnd == std::string::npos) break;
std::string itemXml = xml.substr(itemStart, itemEnd - itemStart + 7);
std::string title = extractTagContent(itemXml, "title");
std::string desc = extractTagContent(itemXml, "description");
if (!title.empty()) {
items.push_back({title, desc});
}
pos = itemEnd + 7;
}
return items;
}
// 判断是否为标准空白字符
bool isSpaceChar(unsigned char c) {
return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f';
}
// 强力清洗函数:剥离首尾的控制字符、半角空格以及【全角空格】
void trimString(std::string& str) {
// 循环剥离前端
while (!str.empty()) {
unsigned char c1 = str.front();
// 1. 检查普通空白
if (isSpaceChar(c1)) {
str.erase(str.begin());
continue;
}
// 2. 检查全角空格 (全角空格在 UTF-8 为 E3 80 80,在 GBK 为 A1 A1)
if (str.size() >= 3 && (unsigned char)str[0] == 0xE3 && (unsigned char)str[1] == 0x80 && (unsigned char)str[2] == 0x80) {
str.erase(0, 3);
continue;
}
if (str.size() >= 2 && (unsigned char)str[0] == 0xA1 && (unsigned char)str[1] == 0xA1) {
str.erase(0, 2);
continue;
}
break; // 既不是半角也不是全角空白,停止
}
// 循环剥离后端
while (!str.empty()) {
unsigned char cLast = str.back();
if (isSpaceChar(cLast)) {
str.pop_back();
continue;
}
size_t len = str.size();
if (len >= 3 && (unsigned char)str[len-3] == 0xE3 && (unsigned char)str[len-2] == 0x80 && (unsigned char)str[len-1] == 0x80) {
str.pop_back(); str.pop_back(); str.pop_back();
continue;
}
if (len >= 2 && (unsigned char)str[len-2] == 0xA1 && (unsigned char)str[len-1] == 0xA1) {
str.pop_back(); str.pop_back();
continue;
}
break;
}
}
// ---------- 3. 格式化(严格清洗,完美贴合) ----------
std::string formatNews(const std::vector<NewsItem>& items) {
std::string result;
bool isFirst = true;
for (const auto& item : items) {
std::string clean_title = item.title;
std::string clean_desc = item.desc;
// 将新闻内部所有的换行符和制表符强制替换为空格,保证单行稳定
for (char& c : clean_title) { if (isSpaceChar((unsigned char)c)) c = ' '; }
for (char& c : clean_desc) { if (isSpaceChar((unsigned char)c)) c = ' '; }
// 调用强力清洗,把新闻内容和标题自带的全角/半角空格全部拔除
trimString(clean_title);
trimString(clean_desc);
// 如果描述内容和标题一模一样(很多新闻源会有这种情况),为了避免重复,直接清空描述
if (clean_desc == "【" + clean_title + "】" || clean_desc == clean_title) {
clean_desc = "";
}
// 只有在两条不同的新闻之间才插入 8 个空格作为分隔
if (!isFirst) {
result += " ";
}
isFirst = false;
// 紧凑拼接:【标题】后面直接连正文,中间没有任何缝隙
result += "【" + clean_title + "】";
if (!clean_desc.empty()) {
result += clean_desc;
}
}
return result;
}
// ---------- 4. HTTP POST 推送 ----------
bool httpPost(const std::string& url, const std::string& data) {
std::wstring wurl(url.begin(), url.end());
URL_COMPONENTS urlComp = { 0 };
urlComp.dwStructSize = sizeof(urlComp);
wchar_t hostName[512] = { 0 };
wchar_t urlPath[2048] = { 0 };
urlComp.lpszHostName = hostName;
urlComp.dwHostNameLength = 512;
urlComp.lpszUrlPath = urlPath;
urlComp.dwUrlPathLength = 2048;
if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &urlComp))
return false;
HINTERNET hSession = WinHttpOpen(L"RSS Pusher/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0);
if (!hSession) return false;
HINTERNET hConnect = WinHttpConnect(hSession, hostName, urlComp.nPort, 0);
if (!hConnect) { WinHttpCloseHandle(hSession); return false; }
DWORD flags = (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", urlPath, NULL, NULL, NULL, flags);
if (!hRequest) { WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return false; }
std::wstring headers = L"Content-Type: text/plain\r\n";
BOOL bResults = WinHttpSendRequest(hRequest, headers.c_str(), (DWORD)headers.length(), (LPVOID)data.c_str(), (DWORD)data.size(), (DWORD)data.size(), 0);
if (bResults) bResults = WinHttpReceiveResponse(hRequest, NULL);
DWORD statusCode = 0;
if (bResults) {
DWORD len = sizeof(statusCode);
WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &statusCode, &len, NULL);
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return (statusCode >= 200 && statusCode < 300);
}
// ---------- 主函数 ----------
int main() {
const char* urls[] = {
"https://www.chinanews.com.cn/rss/scroll-news.xml",
"https://www.chinanews.com.cn/rss/edu.xml"
};
std::vector<NewsItem> allNews;
for (const char* url : urls) {
std::string xml = httpGet(url);
if (xml.empty()) {
std::cerr << "Failed to fetch: " << url << std::endl;
continue;
}
auto items = parseRSS(xml);
allNews.insert(allNews.end(), items.begin(), items.end());
}
std::string payload = formatNews(allNews);
if (payload.empty()) {
std::cerr << "No news to push." << std::endl;
return 1;
}
// 调试输出:保存到本地
// std::ofstream logFile("debug_payload.txt", std::ios::binary);
// if (logFile.is_open()) {
// logFile << payload;
// logFile.close();
// }
// 推送给本地接收端
bool ok = httpPost("http://localhost:36000/news", payload);
if (ok)
std::cout << "Pushed " << allNews.size() << " news items." << std::endl;
else
std::cerr << "POST failed." << std::endl;
return ok ? 0 : 1;
}