实现新闻自动采集并发布到博客

PHP+HTML5+JS+CSS实现新闻自动采集并发布到emlog博客系统

下面我将介绍一个完整的解决方案,使用PHP采集新闻内容,通过HTML5+JS+CSS构建前端界面,并通过emlog的API实现自动发布,所有数据以文件形式存储而不使用数据库。

系统架构

  1. 采集模块:PHP实现新闻采集
  2. 存储模块:文件系统存储采集的内容
  3. 发布模块:通过emlog API发布文章
  4. 前端界面:HTML5+JS+CSS构建管理界面

1. 采集模块 (news_collector.php)

php 复制代码
<?php
// 配置文件
define('DATA_DIR', 'data/news/');
define('EMLOG_API_URL', 'http://your-emlog-site/api.php');
define('EMLOG_API_KEY', 'your_api_key_here');

// 自动创建存储目录
if (!file_exists(DATA_DIR)) {
    mkdir(DATA_DIR, 0777, true);
}

// 采集新闻函数
function collectNews($sourceUrl) {
    $html = file_get_contents($sourceUrl);
    
    // 使用DOM解析新闻内容(这里需要根据实际新闻网站结构调整)
    $dom = new DOMDocument();
    @$dom->loadHTML($html);
    
    $xpath = new DOMXPath($dom);
    
    // 提取标题 - 根据实际网站调整XPath
    $titleNodes = $xpath->query("//h1[@class='article-title']");
    $title = $titleNodes->length > 0 ? $titleNodes->item(0)->nodeValue : '无标题';
    
    // 提取内容 - 根据实际网站调整XPath
    $contentNodes = $xpath->query("//div[@class='article-content']");
    $content = '';
    if ($contentNodes->length > 0) {
        foreach ($contentNodes->item(0)->childNodes as $node) {
            $content .= $dom->saveHTML($node);
        }
    }
    
    // 提取发布时间
    $timeNodes = $xpath->query("//span[@class='publish-time']");
    $pubTime = $timeNodes->length > 0 ? strtotime($timeNodes->item(0)->nodeValue) : time();
    
    // 生成唯一ID
    $newsId = md5($sourceUrl . time());
    
    // 保存到文件
    $newsData = [
        'id' => $newsId,
        'title' => $title,
        'content' => $content,
        'source_url' => $sourceUrl,
        'pub_time' => $pubTime,
        'status' => 'collected' // collected, published
    ];
    
    file_put_contents(DATA_DIR . $newsId . '.json', json_encode($newsData));
    
    return $newsId;
}

// 发布到emlog
function publishToEmlog($newsId) {
    $newsFile = DATA_DIR . $newsId . '.json';
    if (!file_exists($newsFile)) {
        return false;
    }
    
    $newsData = json_decode(file_get_contents($newsFile), true);
    
    $postData = [
        'action' => 'addlog',
        'token' => EMLOG_API_KEY,
        'title' => $newsData['title'],
        'content' => $newsData['content'],
        'date' => date('Y-m-d H:i:s', $newsData['pub_time']),
        'alias' => '',
        'excerpt' => mb_substr(strip_tags($newsData['content']), 0, 100),
        'allow_remark' => 'y',
        'password' => '',
        'sortid' => 1, // 分类ID
        'tag' => '新闻,采集',
        'post' => 'publish'
    ];
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, EMLOG_API_URL);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);
    
    if (strpos($response, 'success') !== false) {
        // 更新状态为已发布
        $newsData['status'] = 'published';
        file_put_contents($newsFile, json_encode($newsData));
        return true;
    }
    
    return false;
}

// 示例使用
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['action'])) {
        switch ($_POST['action']) {
            case 'collect':
                if (!empty($_POST['url'])) {
                    $newsId = collectNews($_POST['url']);
                    echo json_encode(['status' => 'success', 'id' => $newsId]);
                }
                break;
            case 'publish':
                if (!empty($_POST['id'])) {
                    $result = publishToEmlog($_POST['id']);
                    echo json_encode(['status' => $result ? 'success' : 'error']);
                }
                break;
        }
    }
    exit;
}
?>

2. 前端管理界面 (index.html)

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>新闻采集发布系统</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            line-height: 1.6;
            margin: 0;
            padding: 20px;
            background-color: #f5f5f5;
            color: #333;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: #fff;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        h1 {
            text-align: center;
            color: #2c3e50;
        }
        .section {
            margin-bottom: 30px;
            padding: 20px;
            border: 1px solid #eee;
            border-radius: 5px;
        }
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input[type="text"], textarea {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
        }
        button {
            background-color: #3498db;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }
        button:hover {
            background-color: #2980b9;
        }
        .news-list {
            margin-top: 20px;
        }
        .news-item {
            padding: 15px;
            border-bottom: 1px solid #eee;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .news-item:last-child {
            border-bottom: none;
        }
        .news-title {
            font-weight: bold;
            flex: 1;
        }
        .news-status {
            padding: 5px 10px;
            border-radius: 3px;
            font-size: 12px;
        }
        .status-collected {
            background-color: #f39c12;
            color: white;
        }
        .status-published {
            background-color: #2ecc71;
            color: white;
        }
        .news-actions {
            margin-left: 15px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>新闻采集发布系统</h1>
        
        <div class="section">
            <h2>采集新文章</h2>
            <div class="form-group">
                <label for="news-url">新闻URL:</label>
                <input type="text" id="news-url" placeholder="输入新闻网址">
            </div>
            <button id="collect-btn">开始采集</button>
        </div>
        
        <div class="section">
            <h2>已采集文章</h2>
            <div class="news-list" id="news-list">
                <!-- 新闻列表将通过JS动态加载 -->
            </div>
        </div>
    </div>

    <script>
        // 加载新闻列表
        function loadNewsList() {
            fetch('news_collector.php?action=list')
                .then(response => response.json())
                .then(data => {
                    const newsList = document.getElementById('news-list');
                    newsList.innerHTML = '';
                    
                    if (data.length === 0) {
                        newsList.innerHTML = '<p>暂无采集的新闻</p>';
                        return;
                    }
                    
                    data.forEach(news => {
                        const newsItem = document.createElement('div');
                        newsItem.className = 'news-item';
                        
                        const statusClass = news.status === 'published' ? 'status-published' : 'status-collected';
                        
                        newsItem.innerHTML = `
                            <div class="news-title">${news.title}</div>
                            <div class="news-status ${statusClass}">${news.status === 'published' ? '已发布' : '已采集'}</div>
                            <div class="news-actions">
                                ${news.status === 'collected' ? `<button onclick="publishNews('${news.id}')">发布</button>` : ''}
                                <button onclick="deleteNews('${news.id}')">删除</button>
                            </div>
                        `;
                        
                        newsList.appendChild(newsItem);
                    });
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        }
        
        // 采集新闻
        document.getElementById('collect-btn').addEventListener('click', () => {
            const url = document.getElementById('news-url').value.trim();
            if (!url) {
                alert('请输入新闻URL');
                return;
            }
            
            const formData = new FormData();
            formData.append('action', 'collect');
            formData.append('url', url);
            
            fetch('news_collector.php', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'success') {
                    alert('采集成功!');
                    document.getElementById('news-url').value = '';
                    loadNewsList();
                } else {
                    alert('采集失败');
                }
            })
            .catch(error => {
                console.error('Error:', error);
                alert('采集过程中发生错误');
            });
        });
        
        // 发布新闻
        function publishNews(newsId) {
            if (!confirm('确定要发布这篇文章吗?')) return;
            
            const formData = new FormData();
            formData.append('action', 'publish');
            formData.append('id', newsId);
            
            fetch('news_collector.php', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'success') {
                    alert('发布成功!');
                    loadNewsList();
                } else {
                    alert('发布失败');
                }
            })
            .catch(error => {
                console.error('Error:', error);
                alert('发布过程中发生错误');
            });
        }
        
        // 删除新闻
        function deleteNews(newsId) {
            if (!confirm('确定要删除这篇文章吗?')) return;
            
            fetch(`news_collector.php?action=delete&id=${newsId}`)
                .then(response => response.json())
                .then(data => {
                    if (data.status === 'success') {
                        alert('删除成功!');
                        loadNewsList();
                    } else {
                        alert('删除失败');
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                    alert('删除过程中发生错误');
                });
        }
        
        // 页面加载时获取新闻列表
        document.addEventListener('DOMContentLoaded', loadNewsList);
    </script>
</body>
</html>

3. 完善采集模块 (添加列表和删除功能)

在news_collector.php中添加以下代码:

php 复制代码
// 获取新闻列表
function getNewsList() {
    $newsList = [];
    $files = glob(DATA_DIR . '*.json');
    
    foreach ($files as $file) {
        $content = file_get_contents($file);
        $newsData = json_decode($content, true);
        $newsList[] = [
            'id' => $newsData['id'],
            'title' => $newsData['title'],
            'status' => $newsData['status'],
            'pub_time' => $newsData['pub_time']
        ];
    }
    
    // 按发布时间排序
    usort($newsList, function($a, $b) {
        return $b['pub_time'] - $a['pub_time'];
    });
    
    return $newsList;
}

// 删除新闻
function deleteNews($newsId) {
    $newsFile = DATA_DIR . $newsId . '.json';
    if (file_exists($newsFile)) {
        unlink($newsFile);
        return true;
    }
    return false;
}

// 处理GET请求
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action'])) {
    switch ($_GET['action']) {
        case 'list':
            echo json_encode(getNewsList());
            break;
        case 'delete':
            if (!empty($_GET['id'])) {
                $result = deleteNews($_GET['id']);
                echo json_encode(['status' => $result ? 'success' : 'error']);
            }
            break;
    }
    exit;
}

系统部署说明

  1. 将上述代码保存为三个文件:

    • news_collector.php (采集和发布逻辑)
    • index.html (前端管理界面)
    • data/ 目录 (用于存储采集的新闻数据)
  2. 配置emlog API:

    • 确保你的emlog博客已安装API插件
    • 在代码中设置正确的EMLOG_API_URLEMLOG_API_KEY
  3. 系统功能:

    • 输入新闻URL采集文章
    • 查看已采集文章列表
    • 将文章发布到emlog
    • 删除不需要的文章
  4. 安全建议:

    • 为管理界面添加密码保护
    • 限制可采集的域名白名单
    • 定期清理旧数据

扩展建议

  1. 可以添加定时任务自动采集指定网站的新闻
  2. 增加多站点采集支持
  3. 添加内容过滤和格式化功能
  4. 实现分类和标签的自动识别
  5. 添加图片本地化功能

这个系统完全基于文件存储,不使用数据库,适合小型新闻采集发布需求。

相关推荐
豆浆Whisky9 分钟前
Go泛型实战指南:从入门到工程最佳实践|Go语言进阶(12)
后端·go
元亓亓亓29 分钟前
SSM--day4--SpringMVC(补充)
java·后端·ssm
沐雨橙风ιε1 小时前
Spring Boot整合Apache Shiro权限认证框架(应用篇)
java·spring boot·后端·apache shiro
考虑考虑1 小时前
fastjson调用is方法开头注意
java·后端·java ee
小蒜学长2 小时前
springboot基于javaweb的小零食销售系统的设计与实现(代码+数据库+LW)
java·开发语言·数据库·spring boot·后端
brzhang2 小时前
为什么 OpenAI 不让 LLM 生成 UI?深度解析 OpenAI Apps SDK 背后的新一代交互范式
前端·后端·架构
EnCi Zheng2 小时前
JPA 连接 PostgreSQL 数据库完全指南
java·数据库·spring boot·后端·postgresql
brzhang2 小时前
OpenAI Apps SDK ,一个好的 App,不是让用户知道它该怎么用,而是让用户自然地知道自己在做什么。
前端·后端·架构
LucianaiB3 小时前
从玩具到工业:基于 CodeBuddy code CLI 构建电力变压器绕组短路智能诊断系统
后端
武子康4 小时前
大数据-118 - Flink 批处理 DataSet API 全面解析:应用场景、代码示例与优化机制
大数据·后端·flink