系统架构
前端部分
- HTML5单页应用
- CSS3样式
- JavaScript (使用jQuery简化DOM操作)
- AJAX与后端交互
后端部分
- PHP处理业务逻辑
- JSON存储数据
- 采集功能使用PHP cURL和正则表达式
伪静态支持
- IIS伪静态规则
- 宝塔面板伪静态规则
实现步骤
1. 文件结构
/forum/
├── index.php # 主入口文件
├── admin/ # 后台管理
│ ├── index.php # 后台首页
│ ├── settings.php # 采集设置
│ └── collect.php # 执行采集
├── data/ # 数据存储
│ ├── posts.json # 帖子数据
│ └── settings.json # 采集配置
├── assets/ # 静态资源
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── main.js
└── .htaccess # Apache伪静态规则
2. 数据库结构 (JSON格式)
data/posts.json:
{
"posts": [
{
"id": 1,
"title": "示例帖子",
"content": "这是帖子内容",
"author": "admin",
"date": "2023-05-01 10:00:00",
"views": 100
}
],
"total": 1,
"per_page": 10
}
data/settings.json (采集配置):
{
"target_url": "https://example.com/forum",
"list_rule": {
"pattern": "<div class=\"item\">.*?<a href=\"(.*?)\".*?>(.*?)</a>.*?</div>",
"modifiers": "s"
},
"content_rule": {
"pattern": "<div class=\"content\">(.*?)</div>",
"modifiers": "s"
},
"interval": 3600,
"last_collect": "2023-05-01 10:00:00"
}
3. 前端实现 (index.php)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单页论坛</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<header>
<h1>单页论坛</h1>
<nav>
<a href="/">首页</a>
<a href="/admin" target="_blank">管理</a>
</nav>
</header>
<div id="forum-container">
<div id="posts-list"></div>
<div id="pagination"></div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="assets/js/main.js"></script>
</body>
</html>
4. JavaScript (main.js)
$(document).ready(function() {
// 加载帖子列表
loadPosts(1);
// 翻页事件
$(document).on('click', '.page-link', function(e) {
e.preventDefault();
var page = $(this).data('page');
loadPosts(page);
});
});
function loadPosts(page) {
$.ajax({
url: '/api/posts?page=' + page,
type: 'GET',
dataType: 'json',
success: function(response) {
renderPosts(response.posts);
renderPagination(page, Math.ceil(response.total / response.per_page));
}
});
}
function renderPosts(posts) {
var html = '';
posts.forEach(function(post) {
html += `
<div class="post">
<h3><a href="/post/${post.id}" class="post-link">${post.title}</a></h3>
<p>${post.content.substring(0, 100)}...</p>
<div class="meta">
<span>作者: ${post.author}</span>
<span>日期: ${post.date}</span>
<span>浏览: ${post.views}</span>
</div>
</div>
`;
});
$('#posts-list').html(html);
}
function renderPagination(currentPage, totalPages) {
var html = '<div class="pagination">';
if (currentPage > 1) {
html += `<a href="#" class="page-link" data-page="${currentPage - 1}">上一页</a>`;
}
for (var i = 1; i <= totalPages; i++) {
if (i === currentPage) {
html += `<span class="current">${i}</span>`;
} else {
html += `<a href="#" class="page-link" data-page="${i}">${i}</a>`;
}
}
if (currentPage < totalPages) {
html += `<a href="#" class="page-link" data-page="${currentPage + 1}">下一页</a>`;
}
html += '</div>';
$('#pagination').html(html);
}
5. PHP后端处理 (index.php)
<?php
// 开启伪静态时,实际请求会由index.php处理
define('DATA_DIR', __DIR__ . '/data');
// 路由处理
$request = $_SERVER['REQUEST_URI'];
$request = str_replace('/forum', '', $request); // 如果放在子目录
if (strpos($request, '/api/') === 0) {
// API请求
header('Content-Type: application/json');
if (strpos($request, '/api/posts') === 0) {
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
echo getPosts($page);
}
} elseif (strpos($request, '/post/') === 0) {
// 帖子详情页
$postId = intval(str_replace('/post/', '', $request));
displayPost($postId);
} elseif (strpos($request, '/admin') === 0) {
// 后台管理
include __DIR__ . '/admin/index.php';
} else {
// 首页
?>
<!DOCTYPE html>
<!-- 前面提供的HTML内容 -->
<?php
}
function getPosts($page = 1) {
$data = json_decode(file_get_contents(DATA_DIR . '/posts.json'), true);
$perPage = $data['per_page'] ?? 10;
$total = $data['total'] ?? count($data['posts']);
$posts = array_slice($data['posts'], ($page - 1) * $perPage, $perPage);
return json_encode([
'posts' => $posts,
'total' => $total,
'per_page' => $perPage
]);
}
function displayPost($id) {
$data = json_decode(file_get_contents(DATA_DIR . '/posts.json'), true);
$post = null;
foreach ($data['posts'] as $p) {
if ($p['id'] == $id) {
$post = $p;
break;
}
}
if ($post) {
// 增加浏览量
$post['views']++;
// 更新数据
foreach ($data['posts'] as &$p) {
if ($p['id'] == $id) {
$p = $post;
break;
}
}
file_put_contents(DATA_DIR . '/posts.json', json_encode($data, JSON_PRETTY_PRINT));
// 显示帖子
?>
<!DOCTYPE html>
<html>
<head>
<title><?= htmlspecialchars($post['title']) ?></title>
<link rel="stylesheet" href="/forum/assets/css/style.css">
</head>
<body>
<header>
<h1><?= htmlspecialchars($post['title']) ?></h1>
<a href="/forum">返回列表</a>
</header>
<article>
<?= nl2br(htmlspecialchars($post['content'])) ?>
</article>
<div class="meta">
<span>作者: <?= htmlspecialchars($post['author']) ?></span>
<span>日期: <?= htmlspecialchars($post['date']) ?></span>
<span>浏览: <?= $post['views'] ?></span>
</div>
</body>
</html>
<?php
} else {
http_response_code(404);
echo '帖子不存在';
}
}
6. 后台管理 (admin/index.php)
<?php
// 简单的身份验证
session_start();
if (!isset($_SESSION['admin_logged_in'])) {
header('Location: /forum/admin/login.php');
exit;
}
// 后台首页
?>
<!DOCTYPE html>
<html>
<head>
<title>论坛管理</title>
<link rel="stylesheet" href="/forum/assets/css/admin.css">
</head>
<body>
<header>
<h1>论坛管理</h1>
<nav>
<a href="/forum/admin/">首页</a>
<a href="/forum/admin/settings.php">采集设置</a>
<a href="/forum/admin/collect.php">执行采集</a>
<a href="/forum/admin/logout.php">退出</a>
</nav>
</header>
<div class="container">
<h2>系统概览</h2>
<?php
$posts = json_decode(file_get_contents(DATA_DIR . '/posts.json'), true);
$settings = json_decode(file_get_contents(DATA_DIR . '/settings.json'), true);
?>
<p>帖子总数: <?= count($posts['posts']) ?></p>
<p>最后采集时间: <?= $settings['last_collect'] ?? '从未采集' ?></p>
</div>
</body>
</html>
7. 采集功能 (admin/collect.php)
<?php
session_start();
if (!isset($_SESSION['admin_logged_in'])) {
header('Location: /forum/admin/login.php');
exit;
}
// 执行采集
$settings = json_decode(file_get_contents(DATA_DIR . '/settings.json'), true);
$posts = json_decode(file_get_contents(DATA_DIR . '/posts.json'), true);
// 获取目标页面内容
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $settings['target_url']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$html = curl_exec($ch);
curl_close($ch);
// 匹配帖子列表
preg_match_all('/' . $settings['list_rule']['pattern'] . '/' . $settings['list_rule']['modifiers'], $html, $matches, PREG_SET_ORDER);
$newPosts = [];
foreach ($matches as $match) {
$url = $match[1];
$title = $match[2];
// 获取帖子内容
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$postHtml = curl_exec($ch);
curl_close($ch);
// 匹配内容
preg_match('/' . $settings['content_rule']['pattern'] . '/' . $settings['content_rule']['modifiers'], $postHtml, $contentMatch);
$content = $contentMatch[1] ?? '';
// 添加到新帖子数组
$newPosts[] = [
'id' => count($posts['posts']) + count($newPosts) + 1,
'title' => $title,
'content' => $content,
'author' => '采集用户',
'date' => date('Y-m-d H:i:s'),
'views' => 0
];
}
// 合并新旧帖子
$posts['posts'] = array_merge($newPosts, $posts['posts']);
$posts['total'] = count($posts['posts']);
// 保存数据
file_put_contents(DATA_DIR . '/posts.json', json_encode($posts, JSON_PRETTY_PRINT));
// 更新采集时间
$settings['last_collect'] = date('Y-m-d H:i:s');
file_put_contents(DATA_DIR . '/settings.json', json_encode($settings, JSON_PRETTY_PRINT));
echo "采集完成,新增 " . count($newPosts) . " 条帖子";
?>
8. 伪静态规则
IIS伪静态规则 (web.config)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Forum Posts">
<match url="^post/([0-9]+)/?$" />
<action type="Rewrite" url="index.php" />
</rule>
<rule name="Forum API">
<match url="^api/(.*)$" />
<action type="Rewrite" url="index.php" />
</rule>
<rule name="Admin Panel">
<match url="^admin/?(.*)$" />
<action type="Rewrite" url="admin/index.php" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
宝塔面板伪静态规则 (Nginx)
location / {
try_files $uri $uri/ /index.php?$args;
}
location /post/ {
rewrite ^/post/([0-9]+)/?$ /index.php last;
}
location /api/ {
rewrite ^/api/(.*)$ /index.php last;
}
location /admin/ {
rewrite ^/admin/?(.*)$ /admin/index.php last;
}
Apache伪静态规则 (.htaccess)
RewriteEngine On
# 帖子详情
RewriteRule ^post/([0-9]+)/?$ index.php [L]
# API路由
RewriteRule ^api/(.*)$ index.php [L]
# 后台管理
RewriteRule ^admin/?(.*)$ admin/index.php [L]
9. CSS样式 (assets/css/style.css)
/* 基础样式 */
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
color: #333;
}
header {
background: #333;
color: #fff;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
header a {
color: #fff;
text-decoration: none;
margin-left: 1rem;
}
#forum-container {
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
}
.post {
border-bottom: 1px solid #eee;
padding: 1rem 0;
}
.post h3 {
margin: 0 0 0.5rem 0;
}
.post .meta {
font-size: 0.8rem;
color: #666;
margin-top: 0.5rem;
}
.post .meta span {
margin-right: 1rem;
}
.pagination {
margin: 2rem 0;
text-align: center;
}
.pagination a, .pagination span {
display: inline-block;
padding: 0.5rem 1rem;
margin: 0 0.2rem;
border: 1px solid #ddd;
text-decoration: none;
color: #333;
}
.pagination a:hover {
background: #f5f5f5;
}
.pagination .current {
background: #333;
color: #fff;
border-color: #333;
}
/* 后台样式 */
.container {
max-width: 1000px;
margin: 2rem auto;
padding: 0 1rem;
}
自动采集规则搜索实现
要实现自动搜索采集规则,可以在后台添加一个规则发现功能:
// admin/find_rules.php
function findListRules($html) {
// 尝试发现常见的列表模式
$commonPatterns = [
'/<div class="item">.*?<a href="(.*?)".*?>(.*?)<\/a>.*?<\/div>/s',
'/<li>.*?<a href="(.*?)".*?>(.*?)<\/a>.*?<\/li>/s',
'/<article>.*?<h2>.*?<a href="(.*?)".*?>(.*?)<\/a>.*?<\/h2>.*?<\/article>/s'
];
foreach ($commonPatterns as $pattern) {
if (preg_match_all($pattern, $html, $matches) && count($matches[0]) > 0) {
return [
'pattern' => str_replace('/', '\/', substr($pattern, 1, -1)),
'modifiers' => 's'
];
}
}
return null;
}
function findContentRules($html) {
// 尝试发现常见的内容模式
$commonPatterns = [
'/<div class="content">(.*?)<\/div>/s',
'/<article.*?>(.*?)<\/article>/s',
'/<div class="post-content">(.*?)<\/div>/s'
];
foreach ($commonPatterns as $pattern) {
if (preg_match($pattern, $html, $matches) && strlen($matches[1]) > 50) {
return [
'pattern' => str_replace('/', '\/', substr($pattern, 1, -1)),
'modifiers' => 's'
];
}
}
return null;
}
部署说明
- 将代码上传到服务器
- 确保PHP版本 >= 7.0
- 创建data目录并设置可写权限 (chmod 777 data)
- 初始化posts.json和settings.json文件
- 根据服务器类型配置伪静态规则
- 访问/admin设置采集规则
安全注意事项
- 为后台管理添加强密码验证
- 限制采集频率防止被封
- 对采集的内容进行过滤防止XSS攻击
- 定期备份data目录下的JSON文件
这个实现提供了一个完整的单页论坛系统,包含了所有你要求的功能。你可以根据实际需求进行调整和扩展。 更多详情:baijiahao.baidu.com/s?id=183050…