You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用原生PHP同时解析展示多个RSS订阅源?

纯原生PHP多RSS源聚合展示实现

你现有单源RSS解析的核心逻辑是可用的,只需要扩展多源遍历能力、补充基础容错和缓存即可,完全不需要依赖第三方库,以下是可直接运行的改造方案:


改造点说明

  • 保留原生simplexml解析逻辑,无额外依赖
  • 支持批量传入多个RSS源配置,每个源可自定义显示名称
  • 增加解析容错:单个源加载失败/格式异常不会导致整个页面崩溃
  • 增加轻量文件缓存:避免每次访问都拉取源内容,提升加载速度、防止被源站限流
  • 支持两种展示模式:跨源按发布时间混排(时间线模式)、按订阅源分组展示
  • 补全发布时间字段解析,支持跨源时间排序

完整实现代码

<?php
class RssAggregator
{
    private $feeds = [];
    private $cachePath;
    private $cacheTime = 600; // 缓存时长,单位秒,默认10分钟

    /**
     * 初始化聚合器
     * @param array $feeds 订阅源列表,格式:[['url'=>'rss地址', 'name'=>'源名称'], ...]
     * @param int $cacheTime 缓存时长
     */
    public function __construct(array $feeds, int $cacheTime = 600)
    {
        $this->feeds = $feeds;
        $this->cacheTime = $cacheTime;
        $this->cachePath = sys_get_temp_dir() . '/rss_agg_cache_' . md5(serialize($feeds)) . '.tmp';
    }

    /**
     * 获取所有解析后的条目,优先读缓存
     * @return array
     */
    public function getItems(): array
    {
        // 缓存存在且未过期直接返回
        if (file_exists($this->cachePath) && (time() - filemtime($this->cachePath)) < $this->cacheTime) {
            return json_decode(file_get_contents($this->cachePath), true) ?: [];
        }

        $allItems = [];
        // 开启XML内部错误捕获,避免解析警告直接输出
        libxml_use_internal_errors(true);
        foreach ($this->feeds as $feed) {
            if (!isset($feed['url']) || empty($feed['url'])) continue;
            // 加载RSS内容
            $rss = @simplexml_load_file($feed['url']);
            if (!$rss || !isset($rss->channel->item)) {
                // 该源解析失败,直接跳过
                continue;
            }
            // 遍历当前源的条目
            foreach ($rss->channel->item as $item) {
                $pubDate = strtotime((string)$item->pubDate) ?: time();
                $allItems[] = [
                    'title' => (string)$item->title,
                    'link' => (string)$item->link,
                    'description' => (string)$item->description,
                    'pub_date' => $pubDate,
                    'source_name' => $feed['name'] ?? '未知来源'
                ];
            }
        }
        libxml_clear_errors();

        // 按发布时间倒序排序
        usort($allItems, function($a, $b) {
            return $b['pub_date'] - $a['pub_date'];
        });

        // 写入缓存
        file_put_contents($this->cachePath, json_encode($allItems), LOCK_EX);
        return $allItems;
    }

    /**
     * 按时间线混排渲染
     * @param int $limit 最多显示条目数
     * @return string
     */
    public function renderTimeline(int $limit = 100): string
    {
        $items = array_slice($this->getItems(), 0, $limit);
        $html = '<div class="timeline-container">';
        foreach ($items as $item) {
            $pubTime = date('Y-m-d H:i', $item['pub_date']);
            $html .= <<<HTML
<div class="item">
    <div class="meta">
        <span class="source">{$item['source_name']}</span>
        <span class="time">{$pubTime}</span>
    </div>
    <a href="{$item['link']}" target="_blank">{$item['title']}</a>
    <hr>
</div>
HTML;
        }
        $html .= '</div>';
        return $html;
    }

    /**
     * 按订阅源分组渲染
     * @param int $perSourceLimit 每个源最多显示条目数
     * @return string
     */
    public function renderGrouped(int $perSourceLimit = 20): string
    {
        $items = $this->getItems();
        $grouped = [];
        foreach ($items as $item) {
            $grouped[$item['source_name']][] = $item;
        }
        $html = '<div class="grouped-container">';
        foreach ($grouped as $sourceName => $sourceItems) {
            $sourceItems = array_slice($sourceItems, 0, $perSourceLimit);
            $html .= '<div class="source-block"><div class="source-title">'.htmlspecialchars($sourceName).'</div><div class="links">';
            foreach ($sourceItems as $item) {
                $html .= <<<HTML
<div class="item">
    <a href="{$item['link']}" target="_blank">{$item['title']}</a>
    <hr>
</div>
HTML;
            }
            $html .= '</div></div>';
        }
        $html .= '</div>';
        return $html;
    }
}

// 配置你的订阅源列表
$feedConfig = [
    ['url' => 'https://rss.app/feeds/OzfHGFFGdG4RG4LV.xml', 'name' => '订阅源1'],
    // 在这里新增更多RSS源即可,格式同上
    // ['url' => '第二个RSS地址', 'name' => '第二个源名称'],
    // ['url' => '第三个RSS地址', 'name' => '第三个源名称'],
];

// 初始化聚合器
$rss = new RssAggregator($feedConfig, 600);

// 选择渲染模式:二选一即可
// 1. 时间线混排模式,最多显示200条
echo $rss->renderTimeline(200);
// 2. 按源分组模式,每个源最多显示30条
// echo $rss->renderGrouped(30);
?>

使用说明

  • 把所有需要展示的RSS地址按照示例格式填入$feedConfig数组即可,没有数量限制
  • 默认缓存时间10分钟,可根据自己的刷新需求修改$cacheTime参数,单位为秒
  • 长期运行在闲置显示器上建议搭配前端自动刷新(比如meta标签设置每10分钟刷新一次页面)即可实现信息流自动更新
  • 只需要PHP默认开启的SimpleXML扩展即可运行,不需要安装任何额外组件
  • 可根据自己的前端样式需求,修改两个render方法里的HTML结构和class类名,适配显示器的展示效果

内容的提问来源于stack exchange,提问作者Devalappar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 01:57:12