本文目录导读:

在 PHP API 中统计热搜词,通常需要以下几个核心步骤:
设计基础数据表结构
-- 方案一:简单统计表
CREATE TABLE search_hotwords (
id INT AUTO_INCREMENT PRIMARY KEY,
keyword VARCHAR(255) NOT NULL,
search_count INT DEFAULT 0,
last_search_time DATETIME,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_keyword (keyword),
INDEX idx_count (search_count DESC)
);
-- 方案二:包含更多维度
CREATE TABLE search_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
keyword VARCHAR(255) NOT NULL,
user_id INT,
ip_address VARCHAR(45),
search_time DATETIME,
device_type VARCHAR(50),
INDEX idx_keyword (keyword),
INDEX idx_time (search_time)
);
记录搜索接口实现
<?php
// search.php - 搜索接口
class SearchController {
private $db;
public function __construct() {
$this->db = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
}
/**
* 处理搜索请求并记录热搜
*/
public function search($keyword) {
// 1. 过滤和验证关键词
$keyword = $this->sanitizeKeyword($keyword);
if (empty($keyword)) {
return $this->error('关键词不能为空');
}
// 2. 记录搜索日志(可选)
$this->logSearch($keyword);
// 3. 更新热词统计
$this->updateHotwordStats($keyword);
// 4. 执行实际搜索逻辑
$results = $this->performSearch($keyword);
return $this->success($results);
}
/**
* 更新热词统计
*/
private function updateHotwordStats($keyword) {
try {
$sql = "INSERT INTO search_hotwords (keyword, search_count, last_search_time)
VALUES (:keyword, 1, NOW())
ON DUPLICATE KEY UPDATE
search_count = search_count + 1,
last_search_time = NOW()";
$stmt = $this->db->prepare($sql);
$stmt->execute(['keyword' => $keyword]);
} catch (PDOException $e) {
// 记录错误日志
error_log("更新热词失败: " . $e->getMessage());
}
}
/**
* 记录详细搜索日志
*/
private function logSearch($keyword) {
$sql = "INSERT INTO search_logs (keyword, user_id, ip_address, search_time, device_type)
VALUES (:keyword, :user_id, :ip, NOW(), :device)";
$stmt = $this->db->prepare($sql);
$stmt->execute([
'keyword' => $keyword,
'user_id' => $_SESSION['user_id'] ?? 0,
'ip' => $_SERVER['REMOTE_ADDR'],
'device' => $this->detectDevice()
]);
}
private function sanitizeKeyword($keyword) {
// 去除 HTML 标签,限制长度等
$keyword = strip_tags($keyword);
$keyword = trim($keyword);
$keyword = mb_substr($keyword, 0, 100, 'UTF-8');
return $keyword;
}
}
获取热搜词 API
<?php
// hotwords.php - 获取热搜词接口
class HotWordsController {
private $db;
private $cache;
public function __construct() {
$this->db = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
// 可以使用 Redis 或文件缓存
$this->cache = new RedisCache();
}
/**
* 获取热搜词列表
*/
public function getHotWords($limit = 10, $timeRange = '24h') {
// 1. 尝试从缓存获取
$cacheKey = "hotwords:{$limit}:{$timeRange}";
$cached = $this->cache->get($cacheKey);
if ($cached !== false) {
return $this->success($cached);
}
// 2. 从数据库获取
$hotwords = $this->fetchHotWords($limit, $timeRange);
// 3. 存入缓存(5分钟过期)
$this->cache->set($cacheKey, $hotwords, 300);
return $this->success($hotwords);
}
/**
* 从数据库查询热搜词
*/
private function fetchHotWords($limit, $timeRange) {
$timeCondition = $this->buildTimeCondition($timeRange);
$sql = "SELECT keyword, search_count
FROM search_hotwords
WHERE {$timeCondition}
ORDER BY search_count DESC
LIMIT :limit";
$stmt = $this->db->prepare($sql);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 构建时间条件
*/
private function buildTimeCondition($timeRange) {
switch ($timeRange) {
case '1h':
return "last_search_time >= DATE_SUB(NOW(), INTERVAL 1 HOUR)";
case '24h':
return "last_search_time >= DATE_SUB(NOW(), INTERVAL 24 HOUR)";
case '7d':
return "last_search_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)";
case '30d':
return "last_search_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)";
default:
return "1=1"; // 全部时间
}
}
}
高级统计方案(推荐)
<?php
// AdvancedHotWords.php - 高级热搜统计
class AdvancedHotWords {
/**
* 加权热搜算法
*/
public function calculateWeightedHotWords($limit = 10) {
// 多个维度加权计算
$sql = "
SELECT
keyword,
SUM(
CASE
WHEN search_time >= NOW() - INTERVAL 1 HOUR THEN 5 -- 最近1小时搜索权重5
WHEN search_time >= NOW() - INTERVAL 24 HOUR THEN 3 -- 最近24小时权重3
WHEN search_time >= NOW() - INTERVAL 7 DAY THEN 1 -- 最近7天权重1
ELSE 0.5 -- 更早权重0.5
END
) AS hot_score
FROM search_logs
WHERE search_time >= DATE_SUB(NOW(), INTERVAL 30 DAY) -- 只统计最近30天
GROUP BY keyword
ORDER BY hot_score DESC
LIMIT :limit
";
// 执行查询...
}
/**
* 防止刷榜
*/
public function antiSpamCheck($userId, $keyword) {
// 1. 频率限制:同一用户1分钟内最多搜索3次
$recentCount = $this->getUserSearchCount($userId, 60); // 60秒内
if ($recentCount >= 3) {
return false; // 拒绝记录
}
// 2. 同IP限制
$ipCount = $this->getIpSearchCount($userId, 60);
if ($ipCount >= 10) {
return false;
}
return true;
}
/**
* 实时热搜(使用Redis)
*/
public function realTimeHotWords() {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 使用 Sorted Set 存储热词
// 每次搜索时执行:ZINCRBY hotwords 1 $keyword
// 获取top 10
$hotwords = $redis->zRevRange('hotwords', 0, 9, true);
return $hotwords;
}
}
API 路由和响应
<?php
// router.php - API路由
$route = $_GET['route'] ?? '';
switch ($route) {
case 'search':
$controller = new SearchController();
$result = $controller->search($_POST['keyword'] ?? '');
break;
case 'hotwords':
$controller = new HotWordsController();
$limit = $_GET['limit'] ?? 10;
$timeRange = $_GET['time'] ?? '24h';
$result = $controller->getHotWords($limit, $timeRange);
break;
default:
$result = ['error' => 'Invalid route'];
}
header('Content-Type: application/json');
echo json_encode($result);
前端调用示例
// 前端搜索
async function search(keyword) {
const response = await fetch('/api/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ keyword })
});
return response.json();
}
// 获取热搜词
async function getHotWords() {
const response = await fetch('/api/hotwords?limit=10&time=24h');
return response.json();
}
优化建议
- 使用缓存:Redis 或 Memcached 缓存热词结果
- 异步处理:使用消息队列(RabbitMQ、Kafka)异步更新统计
- 定时任务:定期清理过期数据,重建索引
- 防刷机制:IP限制、用户频率限制、验证码
- 数据清洗:过滤敏感词、无效词、过短词
根据你的实际访问量和需求,可以选择简单或复杂的统计方案,小型项目用方案一即可,大型项目推荐使用 Redis + 消息队列的方案。