從純文本內容中提取潛在標題,利用中文標點符號作為邊界,篩選長度適中(5-30 字)的片段,隨機選取一個作為標題。
<?php
/**
* 從內容中智能提取標題
* @param string $content 文章內容(已去除 HTML 標籤)
* @param int $minLen 最小長度
* @param int $maxLen 最大長度
* @return string|null 提取的標題或 null
*/
function extractTitleFromContent($content, $minLen = 5, $maxLen = 30) {
// 1. 全角轉半角(統一標點處理)
$content = fullWidthToHalfWidth($content);
// 2. 移除 HTML 標籤(防干擾)
$content = strip_tags($content);
// 3. 正則匹配:標點符號包裹的文本片段
// [\x{3000}-\x{303F}\x{FF00}-\x{FFEF}] 覆蓋中日韓標點
// 也可使用 \p{P} 匹配所有 Unicode 標點(需 u 修飾符)
$pattern = '/[\p{P}\p{S}]([^\p{P}\p{S}]{' . $minLen . ',' . $maxLen . '})[\p{P}\p{S}]/u';
if (preg_match_all($pattern, $content, $matches)) {
// $matches[1] 包含所有捕獲組(不含邊界標點)
$candidates = $matches[1];
// 4. 可選:過濾低質量候選(純數字、重複字符、過多標點等)
$candidates = array_filter($candidates, function($title) {
// 過濾純數字
if (preg_match('/^[\d\s\.\,\%\+\-\*\/]+$/', $title)) return false;
// 過濾重複字符佔比過高
$uniqueChars = count(array_unique(mb_str_split($title)));
if ($uniqueChars / mb_strlen($title) < 0.3) return false;
return true;
});
if (!empty($candidates)) {
// 5. 隨機選取(或按位置權重:首段優先)
return $candidates[array_rand($candidates)];
}
}
return null;
}
/**
* 全角轉半角(優化版:使用映射表而非巨大數組)
*/
function fullWidthToHalfWidth($str) {
static $map = null;
if ($map === null) {
$map = [];
// 數字 0-9 → 0-9
for ($i = 0; $i <= 9; $i++) {
$map[mb_chr(0xFF10 + $i)] = (string)$i;
}
// 大寫 A-Z → A-Z
for ($i = 0; $i < 26; $i++) {
$map[mb_chr(0xFF21 + $i)] = chr(0x41 + $i);
}
// 小寫 a-z → a-z
for ($i = 0; $i < 26; $i++) {
$map[mb_chr(0xFF41 + $i)] = chr(0x61 + $i);
}
// 常用標點
$punctMap = [
'(' => '(', ')' => ')', '〔' => '[', '〕' => ']', '【' => '[', '】' => ']',
'〖' => '[', '〗' => ']', '"' => '"', '"' => '"', ''' => "'", ''' => "'",
'{' => '{', '}' => '}', '《' => '<', '》' => '>',
'%' => '%', '+' => '+', '-' => '-', '~' => '~',
':' => ':', '。' => '.', '、' => ',', ',' => ',', ';' => ';',
'?' => '?', '!' => '!', '…' => '...', '‖' => '|',
'"' => '"', ''' => "'", '|' => '|', '〃' => '"',
' ' => ' ', '$' => '$', '@' => '@', '#' => '#', '^' => '^', '&' => '&', '*' => '*',
];
$map = array_merge($map, $punctMap);
}
return strtr($str, $map);
}
// 測試
$content = "這是一段測試內容。標題候選一:這是一個不錯的標題!另一個候選:PHP 開發技巧分享。結束。";
$title = extractTitleFromContent($content);
echo "提取標題: " . ($title ?? '無') . "\n";
// 可能輸出: "這是一個不錯的標題" 或 "PHP 開發技巧分享"
| 維度 | 當前方案 | 進階方案 |
|---|---|---|
| 邊界識別 | 標點符號 | 結合 NLP 分句(如 jieba-php、Stanford CoreNLP) |
| 候選評分 | 隨機選取 | TF-IDF、TextRank、位置權重(首段加分)、關鍵詞覆蓋率 |
| 質量過濾 | 簡單正則 | 語言模型困惑度、標題分類器(FastText/BERT) |
| 多語言 | 中文標點 | Unicode 通用標點屬性 \p{P} + 語言檢測 |
內容輸入
↓
HTML 清洗 → 純文本
↓
語言檢測(中/英/日/韓...)
↓
分句(jieba / NLTK / Kuromoji / MeCab)
↓
候選生成(長度 5-50,首段優先,含關鍵詞加分)
↓
特徵提取(長度、位置、關鍵詞密度、標題詞特徵、實體識別)
↓
評分模型(LR / XGBoost / BERT fine-tune)
↓
Top-K 輸出 → 業務邏輯選擇或人工審核
u 修飾符失效^([^\p{P}]{5,30})[\p{P}] 和 [\p{P}]([^\p{P}]{5,30})$
// 依賴 composer require jieba/jieba-php
use Fukuball\Jieba\Jieba;
use Fukuball\Jieba\Finalseg;
Jieba::init();
Finalseg::init();
function textRankKeywords($text, $topK = 5) {
$jieba = new Jieba();
$words = $jieba->extractTags($text, $topK);
return $words; // 返回權重排序的關鍵詞
}
// 結合標題生成:取 Top 3 關鍵詞組合或作為標題標籤