PHP + CSS + JS + JSON 数据采集与展示系统,支持伪静态

PHP + CSS + JS + JSON 数据采集与展示系统

下面我将为你实现一个完整的系统,包含前端单页展示、后端数据采集、JSON数据存储,并支持IIS和宝塔伪静态。

目录结构

bash 复制代码
/project-root/
├── api/                # PHP后端文件
│   ├── collector.php   # 数据采集脚本
│   ├── data.json       # 存储采集的数据
│   └── config.php      # 配置文件
├── static/             # 静态资源
│   ├── css/
│   │   └── style.css   # 样式文件
│   └── js/
│       └── app.js      # 前端JavaScript
├── index.php           # 前端入口文件
└── .htaccess           # Apache伪静态规则

1. 后端数据采集 (PHP)

api/config.php

php 复制代码
<?php
// 配置采集的站点信息
$config = [
    'target_sites' => [
        'example' => [
            'url' => 'https://example.com',
            'selectors' => [
                'title' => 'h1',
                'content' => '.article-content',
                'date' => '.post-date'
            ]
        ]
        // 可以添加更多站点
    ],
    'storage_file' => __DIR__ . '/data.json',
    'update_interval' => 3600 // 1小时更新一次
];

// 设置JSON头
header('Content-Type: application/json');

// 跨域设置(根据需要)
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET");
?>

api/collector.php

php 复制代码
<?php
require_once 'config.php';

// 简单的HTML内容采集函数
function fetchData($url, $selectors) {
    $html = file_get_contents($url);
    if (!$html) return null;
    
    $dom = new DOMDocument();
    @$dom->loadHTML($html);
    $xpath = new DOMXPath($dom);
    
    $result = [];
    foreach ($selectors as $key => $selector) {
        $nodes = $xpath->query($selector);
        if ($nodes && $nodes->length > 0) {
            $result[$key] = trim($nodes->item(0)->nodeValue);
        } else {
            $result[$key] = '';
        }
    }
    
    return $result;
}

// 检查是否需要更新数据
function needsUpdate($storageFile, $interval) {
    if (!file_exists($storageFile)) return true;
    $lastModified = filemtime($storageFile);
    return (time() - $lastModified) > $interval;
}

// 主采集逻辑
if ($_SERVER['REQUEST_METHOD'] === 'POST' || needsUpdate($config['storage_file'], $config['update_interval'])) {
    $allData = [];
    
    foreach ($config['target_sites'] as $siteName => $siteConfig) {
        $data = fetchData($siteConfig['url'], $siteConfig['selectors']);
        if ($data) {
            $data['source'] = $siteName;
            $data['fetched_at'] = date('Y-m-d H:i:s');
            $allData[] = $data;
        }
    }
    
    file_put_contents($config['storage_file'], json_encode($allData, JSON_PRETTY_PRINT));
    echo json_encode(['status' => 'success', 'message' => 'Data updated']);
} else {
    // 直接返回现有数据
    if (file_exists($config['storage_file'])) {
        readfile($config['storage_file']);
    } else {
        echo json_encode(['status' => 'error', 'message' => 'No data available']);
    }
}
?>

2. 前端展示

index.php

php 复制代码
<?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="static/css/style.css">
</head>
<body>
    <div class="container">
        <h1>数据采集展示系统</h1>
        <div class="controls">
            <button id="refreshBtn">手动刷新数据</button>
            <div class="loading" id="loadingIndicator" style="display: none;">加载中...</div>
        </div>
        <div id="dataContainer" class="data-container"></div>
    </div>

    <script src="static/js/app.js"></script>
</body>
</html>

static/css/style.css

css 复制代码
/* 基础样式 */
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: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h1 {
    color: #2c3e50;
    text-align: center;
    margin-bottom: 30px;
}

.controls {
    margin-bottom: 20px;
    text-align: center;
}

button {
    background-color: #3498db;
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 16px;
    transition: background-color 0.3s;
}

button:hover {
    background-color: #2980b9;
}

.loading {
    margin-top: 10px;
    color: #7f8c8d;
}

.data-container {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 20px;
}

.data-item {
    background: #ecf0f1;
    padding: 15px;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.data-item h2 {
    margin-top: 0;
    color: #2c3e50;
    font-size: 1.2em;
}

.data-item .meta {
    font-size: 0.9em;
    color: #7f8c8d;
    margin-bottom: 10px;
}

.data-item .content {
    margin-top: 10px;
}

static/js/app.js

javascript 复制代码
document.addEventListener('DOMContentLoaded', function() {
    const dataContainer = document.getElementById('dataContainer');
    const refreshBtn = document.getElementById('refreshBtn');
    const loadingIndicator = document.getElementById('loadingIndicator');
    
    // 加载数据
    function loadData() {
        loadingIndicator.style.display = 'block';
        dataContainer.innerHTML = '';
        
        fetch('api/collector.php')
            .then(response => response.json())
            .then(data => {
                renderData(data);
                loadingIndicator.style.display = 'none';
            })
            .catch(error => {
                console.error('Error:', error);
                dataContainer.innerHTML = '<div class="error">加载数据失败,请稍后再试</div>';
                loadingIndicator.style.display = 'none';
            });
    }
    
    // 渲染数据
    function renderData(items) {
        if (!items || items.length === 0) {
            dataContainer.innerHTML = '<div class="no-data">暂无数据</div>';
            return;
        }
        
        items.forEach(item => {
            const itemElement = document.createElement('div');
            itemElement.className = 'data-item';
            
            let html = `
                <h2>${item.title || '无标题'}</h2>
                <div class="meta">
                    <span>来源: ${item.source || '未知'}</span> | 
                    <span>日期: ${item.date || '未知'}</span>
                </div>
                <div class="content">${item.content || '无内容'}</div>
                <div class="fetched-at">更新时间: ${item.fetched_at || '未知'}</div>
            `;
            
            itemElement.innerHTML = html;
            dataContainer.appendChild(itemElement);
        });
    }
    
    // 手动刷新
    refreshBtn.addEventListener('click', function() {
        // 强制更新数据
        fetch('api/collector.php', {
            method: 'POST'
        })
        .then(() => loadData())
        .catch(error => {
            console.error('Error:', error);
            loadingIndicator.style.display = 'none';
        });
    });
    
    // 初始加载
    loadData();
    
    // 定时刷新(每5分钟)
    setInterval(loadData, 5 * 60 * 1000);
});

3. 伪静态配置

IIS伪静态 (web.config)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="JSON API" stopProcessing="true">
                    <match url="^api/data$" />
                    <action type="Rewrite" url="api/collector.php" />
                </rule>
                <rule name="Frontend" stopProcessing="true">
                    <match url=".*" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="index.php" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

宝塔伪静态 (Nginx)

bash 复制代码
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

location /api/data {
    rewrite ^/api/data$ /api/collector.php last;
}

Apache伪静态 (.htaccess)

ruby 复制代码
RewriteEngine On

# API重写
RewriteRule ^api/data$ api/collector.php [L]

# 前端重写
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L,QSA]

4. 使用说明

  1. 将上述文件按照目录结构放置
  2. 根据你的服务器环境配置伪静态规则
  3. 修改api/config.php中的目标站点和选择器配置
  4. 确保PHP环境已启用file_get_contents函数和DOM扩展
  5. 确保api/data.json文件可写(权限设置为666或775)

5. 扩展建议

  1. 安全性增强:添加API密钥验证
  2. 更多采集选项:支持POST请求、自定义头等
  3. 数据库支持:可扩展为使用MySQL等数据库
  4. 日志系统:记录采集历史
  5. 错误处理:更完善的错误处理和重试机制

这个系统实现了基本的数据采集和展示功能,前端使用单页应用模式,后端使用PHP采集数据并存储为JSON,同时支持多种服务器的伪静态配置。 更多数据详情:baijiahao.baidu.com/s?id=183050...

相关推荐
中微子1 小时前
React状态管理最佳实践
前端
烛阴1 小时前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
中微子1 小时前
JavaScript 事件与 React 合成事件完全指南:从入门到精通
前端
Hexene...1 小时前
【前端Vue】如何实现echarts图表根据父元素宽度自适应大小
前端·vue.js·echarts
天天扭码2 小时前
《很全面的前端面试题》——HTML篇
前端·面试·html
xw52 小时前
我犯了错,我于是为我的uni-app项目引入环境标志
前端·uni-app
!win !2 小时前
被老板怼后,我为uni-app项目引入环境标志
前端·小程序·uni-app
Burt2 小时前
tsdown vs tsup, 豆包回答一坨屎,还是google AI厉害
前端
群联云防护小杜3 小时前
构建分布式高防架构实现业务零中断
前端·网络·分布式·tcp/ip·安全·游戏·架构
ohMyGod_1234 小时前
React16,17,18,19新特性更新对比
前端·javascript·react.js