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

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. 添加图片本地化功能

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

相关推荐
婪苏1 小时前
Python 元类:类的创造者
后端
陈随易1 小时前
Kimi k2发布,效果比肩Sonnet4,价格与DeepSeek一致
前端·后端·程序员
到账一个亿1 小时前
代码的隐形守护者:Spring AOP 是如何做到的?
后端
SparkX开源AI知识库1 小时前
SparkX开源AI知识库系统V1.0.0发布
后端
知其然亦知其所以然1 小时前
Java 面试高频题:GC 到底回收了什么、怎么回收、啥时候回收?
java·后端·面试
Z_W_H_1 小时前
【SpringBoot】 整合MyBatis+Postgresql
java·spring boot·后端
lihainuo1 小时前
Next.js + AI-SDK 实战:模型注册表从类型设计到工具调用全解析
后端·node.js
考虑考虑2 小时前
Redis8新增特性
redis·后端·程序员
labixiong2 小时前
全方位理解跨源资源共享-CORS
前端·后端
AntBlack2 小时前
闲谈 :AI编程效率反而降低了 ,大家AI 编程的正确姿势到底是什么?
前端·后端·ai编程