完整代码实现(续)
sent_score * 0.20 +
complex_score * 0.20 +
passive_score * 0.15 +
transition_score * 0.15
)
return overall
# ==================== 模块4: SEO友好度分析器 ====================
class SEOScorer: baike.kuaisou.com/sitemap
"""
SEO友好度分析器
评估标题、关键词密度、链接结构等
"""
def __init__(self): 26145.baike.tongsou.com
self.logger = logging.getLogger(__name__)
# 理想标题长度范围
self.title_min = 30
self.title_max = 60
# 理想描述长度范围
self.desc_min = 120
self.desc_max = 160
# 理想关键词密度范围(百分比)
self.kwd_density_min = 0.5
self.kwd_density_max = 3.0
def evaluate(
self,
parsed_content: Dict[str, Any],
target_keywords: List[str] = None,
) -> SEOMetrics: 22021.baike.tongsou.com
"""评估SEO友好度"""
title = parsed_content.get("title", "")
meta_desc = parsed_content.get("meta_description", "")
headings = parsed_content.get("headings", [])
paragraphs = parsed_content.get("paragraphs", [])
links = parsed_content.get("links", [])
images = parsed_content.get("images", [])
raw_text = parsed_content.get("raw_text", "")
# 标题长度评分
title_len = len(title)
title_score = self._score_range(title_len, self.title_min, self.title_max)
# 标题含关键词
title_kw_present = False
if target_keywords: 22016.baike.tongsou.com
title_lower = title.lower()
title_kw_present = any(kw.lower() in title_lower for kw in target_keywords)
# 描述长度评分
desc_len = len(meta_desc)
desc_score = self._score_range(desc_len, self.desc_min, self.desc_max)
# 关键词密度
keyword_density = {}
if target_keywords and raw_text: 27054.baike.tongsou.com
text_lower = raw_text.lower()
word_count = len(re.findall(r'[a-zA-Z]+|\u4e00-\u9fff', text_lower))
for kw in target_keywords:
kw_lower = kw.lower()
count = len(re.findall(re.escape(kw_lower), text_lower))
density = (count / word_count * 100) if word_count > 0 else 0
keyword_density[kw] = round(density, 2)
# 标题结构评分
heading_score = self._evaluate_heading_structure(headings)
# 链接统计
internal_links = sum(1 for l in links if l.get("is_internal"))
external_links = sum(1 for l in links if not l.get("is_internal"))
# 图片alt覆盖率
alt_coverage = 0.0
if images: 27053.baike.tongsou.com
alt_count = sum(1 for img in images if img.get("has_alt"))
alt_coverage = alt_count / len(images) * 100
# URL友好度(简化:检查是否含连字符、小写等)
url_score = 70 # 默认值,实际应从URL分析
# 综合SEO评分
overall = self._compute_seo_score(
title_score, title_kw_present, desc_score,
keyword_density, heading_score,
internal_links, external_links,
alt_coverage, url_score,
)
return SEOMetrics(
title_length=title_len,
title_keyword_present=title_kw_present,
meta_description_length=desc_len,
keyword_density=keyword_density,
heading_structure_score=round(heading_score, 2),
internal_link_count=internal_links,
external_link_count=external_links,
image_alt_coverage=round(alt_coverage, 2),
url_friendly_score=round(url_score, 2),
overall_score=round(overall, 2),
)
def _score_range(self, value: int, min_val: int, max_val: int) -> float:
"""根据值是否在理想范围内评分"""
if min_val <= value <= max_val: 27027.baike.tongsou.com
return 100.0
elif value < min_val: 27025.baike.tongsou.com
diff = min_val - value
return max(0, 100 - diff * 2)
else:
diff = value - max_val
return max(0, 100 - diff * 2)
def _evaluate_heading_structure(self, headings: List[Dict]) -> float:
"""评估标题层级结构"""
if not headings: 27024.baike.tongsou.com
return 0
# 检查是否以H1开头
has_h1 = any(h["level"] == 1 for h in headings)
if not has_h1: 27016.baike.tongsou.com
return 30
# 检查层级是否跳跃
levels = [h["level"] for h in headings]
jumps = 0
for i in range(1, len(levels)): 27014.baike.tongsou.com
if levels[i] > levels[i - 1] + 1: 27013.baike.tongsou.com
jumps += 1
# 层级跳跃越少越好
jump_penalty = jumps * 10
score = max(0, 100 - jump_penalty)
return score
def _compute_seo_score(
self, title_score: float, title_kw: bool, desc_score: float,
keyword_density: Dict[str, float], heading_score: float,
internal_links: int, external_links: int,
alt_coverage: float, url_score: float,
) -> float: 27011.baike.tongsou.com
"""综合计算SEO评分"""
# 标题权重最高
title_component = title_score * 0.20
if title_kw:
title_component += 10 # 含关键词加分
# 描述
desc_component = desc_score * 0.15
# 关键词密度(取平均)
kwd_scores = []
for kw, density in keyword_density.items(): 27009.baike.tongsou.com
if self.kwd_density_min <= density <= self.kwd_density_max: 27008.baike.tongsou.com
kwd_scores.append(100)
elif density < self.kwd_density_min: 27007.baike.tongsou.com
kwd_scores.append(max(0, 100 - (self.kwd_density_min - density) * 100))
else:
kwd_scores.append(max(0, 100 - (density - self.kwd_density_max) * 50))
kwd_component = statistics.mean(kwd_scores) * 0.20 if kwd_scores else 50 * 0.20
# 标题结构
heading_component = heading_score * 0.15
# 链接与alt
link_component = min(100, (internal_links + external_links) * 10) * 0.10
alt_component = alt_coverage * 0.10
# URL
url_component = url_score * 0.10
total = (
title_component + desc_component + kwd_component +
heading_component + link_component + alt_component + url_component
)
return min(100, total)
# ==================== 模块5: 语义丰富度计算器 ====================
class SemanticCalculator: 27006.baike.tongsou.com
"""
语义丰富度计算器
评估词汇多样性、主题覆盖度、实体密度等
"""
def __init__(self): 27005.baike.tongsou.com
# 常见停用词(英文)
self.stopwords_en = {
"the", "a", "an", "is", "are", "was", "were", "be", "been",
"being", "have", "has", "had", "do", "does", "did", "will",
"would", "could", "should", "may", "might", "can", "shall",
"to", "of", "in", "for", "on", "with", "at", "by", "from",
"as", "into", "through", "during", "before", "after",
"and", "but", "or", "nor", "not", "so", "yet",
"i", "me", "my", "we", "our", "you", "your", "he", "him",
"his", "she", "her", "it", "its", "they", "them", "their",
"this", "that", "these", "those", "what", "which", "who",
"whom", "how", "when", "where", "why", "if", "then",
}
# 常见停用词(中文)
self.stopwords_zh = {
"的", "了", "在", "是", "我", "有", "和", "就",
"不", "人", "都", "一", "一个", "上", "也", "很",
"到", "说", "要", "去", "你", "会", "着", "没有",
"看", "好", "自己", "这", "他", "她", "它", "们",
"那", "些", "什么", "怎么", "可以", "被", "把",
}
def evaluate(
self,
text: str,
target_keywords: List[str] = None,
lsi_keywords: List[str] = None,
) -> SemanticMetrics: 26090.baike.tongsou.com
"""评估语义丰富度"""
words = self._extract_content_words(text)
if not words: 27063.baike.tongsou.com
return SemanticMetrics(
unique_term_count=0, vocabulary_richness=0,
topic_coverage=0, entity_density=0,
lsi_keyword_coverage=0, semantic_coherence=0,
overall_score=0,
)
# 唯一术语数
unique_terms = set(words)
unique_count = len(unique_terms)
# 词汇丰富度(Type-Token Ratio)
vocabulary_richness = unique_count / len(words) if words else 0
# 主题覆盖度(基于目标关键词)
topic_coverage = 0.0
if target_keywords: 27022.baike.tongsou.com
text_lower = text.lower(27039.baike.tongsou.com)
covered = sum(
1 for kw in target_keywords if kw.lower() in text_lower
)
topic_coverage = covered / len(target_keywords) * 100
# 实体密度(简化:统计大写专有名词)
entities = re.findall(r'[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*', text)
entity_density = len(entities) / len(words) * 100 if words else 0
# LSI关键词覆盖率
lsi_coverage = 0.0
if lsi_keywords: 27021.baike.tongsou.com
text_lower = text.lower(27017.baike.tongsou.com)
covered = sum(
1 for kw in lsi_keywords if kw.lower(27015.baike.tongsou.com) in text_lower
)
lsi_coverage = covered / len(lsi_keywords) * 100
# 语义连贯性(简化:段落首尾句相似度)
coherence = self._estimate_coherence(text)
# 综合语义评分
overall = self._compute_semantic_score(
vocabulary_richness, topic_coverage,
entity_density, lsi_coverage, coherence,
)
return SemanticMetrics(
unique_term_count=unique_count,
vocabulary_richness=round(vocabulary_richness, 4),
topic_coverage=round(topic_coverage, 2),
entity_density=round(entity_density, 2),
lsi_keyword_coverage=round(lsi_coverage, 2),
semantic_coherence=round(coherence, 2),
overall_score=round(overall, 2),
)
def _extract_content_words(self, text: str) -> List[str]: 26045.baike.tongsou.com
"""提取内容词(去除停用词)"""
english_words = re.findall(r'[a-zA-Z]{2,}', text.lower())
chinese_chars = re.findall(r'[\u4e00-\u9fff]', text)
filtered_en = [w for w in english_words if w not in self.stopwords_en]
filtered_zh = [c for c in chinese_chars if c not in self.stopwords_zh]
return filtered_en + filtered_zh
def _estimate_coherence(self, text: str) -> float: 27018.baike.tongsou.com
"""估算语义连贯性(简化版)"""
sentences = re.split(r'[.!?。!?]', text)
sentences = [s.strip() for s in sentences if s.strip()]
if len(sentences) < 2: 27034.baike.tongsou.com
return 50.0
# 简化:检查相邻句子是否有共同词汇
common_count = 0
total_pairs = 0
for i in range(len(sentences) - 1): 27029.baike.tongsou.com
words_i = set(re.findall(r'[a-zA-Z]{3,}|\u4e00-\u9fff', sentences[i].lower()))
words_next = set(re.findall(r'[a-zA-Z]{3,}|\u4e00-\u9fff', sentences[i + 1].lower()))
common = words_i & words_next
common_count += len(common)
total_pairs += 1
if total_pairs == 0: 27028.baike.tongsou.com
return 50.0
avg_common = common_count / total_pairs
# 映射到0-100
coherence = min(100, avg_common * 20)
return coherence
def _compute_semantic_score(
self, vocabulary_richness: float, topic_coverage: float,
entity_density: float, lsi_coverage: float, coherence: float,
) -> float:
"""综合计算语义评分"""
# 词汇丰富度(0.4-0.7为理想)
if 0.4 <= vocabulary_richness <= 0.7: 27023.baike.tongsou.com
vocab_score = 100
else:
vocab_score = max(0, 100 - abs(vocabulary_richness - 0.55) * 200)
# 主题覆盖
topic_score = topic_coverage
# 实体密度(1-5%为理想)
if 1 <= entity_density <= 5: 27020.baike.tongsou.com
entity_score = 100
elif entity_density < 1:
entity_score = max(0, entity_density * 100)
else:
entity_score = max(0, 100 - (entity_density - 5) * 20)
# LSI覆盖
lsi_score = lsi_coverage
# 连贯性
coherence_score = coherence
overall = (
vocab_score * 0.20 +
topic_score * 0.25 +
entity_score * 0.15 +
lsi_score * 0.20 +
coherence_score * 0.20
)
return min(100, overall)
# ==================== 模块6: 结构完整性检查器 ====================
class StructureChecker: 27019.baike.tongsou.com
"""
结构完整性检查器
检查标题、元描述、标题层级、段落、列表、表格、代码块等
"""
def __init__(self): 26144.baike.tongsou.com
self.logger = logging.getLogger(__name__)
def evaluate(self, parsed_content: Dict[str, Any]) -> StructureMetrics:
"""评估结构完整性"""
title = parsed_content.get("title", "")
meta_desc = parsed_content.get("meta_description", "")
headings = parsed_content.get("headings", [])
paragraphs = parsed_content.get("paragraphs", [])
lists = parsed_content.get("lists", [])
tables = parsed_content.get("tables", [])
code_blocks = parsed_content.get("code_blocks", [])
has_title = bool(title)
has_meta = bool(meta_desc)
# 标题层级有效性
heading_valid = self._check_heading_hierarchy(headings)
# 内容深度评分(基于段落数、列表数等)
depth_score = self._compute_depth_score(
len(paragraphs), len(lists), len(tables), len(code_blocks)
)
# 综合结构评分
overall = self._compute_structure_score(
has_title, has_meta, heading_valid,
len(paragraphs), len(lists), len(tables),
len(code_blocks), depth_score,
)
return StructureMetrics(
has_title=has_title,
has_meta_description=has_meta,
heading_hierarchy_valid=heading_valid,
paragraph_count=len(paragraphs),
list_count=len(lists),
table_count=len(tables),
code_block_count=len(code_blocks),
content_depth_score=round(depth_score, 2),
overall_score=round(overall, 2),
)
def _check_heading_hierarchy(self, headings: List[Dict]) -> bool:
"""检查标题层级是否有效"""
if not headings: 26128.baike.tongsou.com
return False
# 必须有H1
if not any(h["level"] == 1 for h in headings):
return False
# 检查是否有跳跃
levels = [h["level"] for h in headings]
for i in range(1, len(levels)): 17025.baike.tongsou.com
if levels[i] > levels[i - 1] + 1: 17024.baike.tongsou.com
return False
return True
def _compute_depth_score(
self, para_count: int, list_count: int,
table_count: int, code_count: int,
) -> float:
"""计算内容深度评分"""
# 段落数评分(理想10-30段)
para_score = min(100, para_count * 5) if para_count <= 30 else 100
# 列表加分
list_bonus = min(20, list_count * 5)
# 表格加分
table_bonus = min(15, table_count * 5)
# 代码块加分
code_bonus = min(15, code_count * 5)
score = para_score + list_bonus + table_bonus + code_bonus
return min(100, score)
def _compute_structure_score(
self, has_title: bool, has_meta: bool, heading_valid: bool,
para_count: int, list_count: int, table_count: int,
code_count: int, depth_score: float,
) -> float:
"""综合计算结构评分"""
title_component = 20 if has_title else 0
meta_component = 15 if has_meta else 0
heading_component = 20 if heading_valid else 5
depth_component = depth_score * 0.45
total = title_component + meta_component + heading_component + depth_component
return min(100, total)
# ==================== 模块7: 智能改写引擎 ====================
class RewriteEngine: 17023.baike.tongsou.com
"""
智能改写引擎
根据评估结果生成改写建议
"""
def __init__(self): 17021.baike.tongsou.com
self.logger = logging.getLogger(__name__)
# 同义词库(简化版)
self.synonyms = {
"good": ["excellent", "great", "fine", "superior"],
"bad": ["poor", "inferior", "subpar", "unsatisfactory"],
"important": ["crucial", "vital", "essential", "significant"],
"use": ["utilize", "employ", "leverage", "apply"],
"help": ["assist", "aid", "support", "facilitate"],
"make": ["create", "produce", "generate", "construct"],
"get": ["obtain", "acquire", "gain", "secure"],
"show": ["demonstrate", "illustrate", "display", "reveal"],
"think": ["consider", "believe", "regard", "deem"],
"say": ["state", "mention", "express", "articulate"],
}
def generate_suggestions(
self,
evaluation: ContentEvaluation,
parsed_content: Dict[str, Any],
) -> List[RewriteSuggestion]: 17019.baike.tongsou.com
"""根据评估结果生成改写建议"""
suggestions = []
# 可读性建议
suggestions.extend(
self._suggest_readability_improvements(evaluation.readability, parsed_content)
)
# SEO建议
suggestions.extend(
self._suggest_seo_improvements(evaluation.seo, parsed_content)
)
# 语义建议
suggestions.extend(
self._suggest_semantic_improvements(evaluation.semantic, parsed_content)
)
# 结构建议
suggestions.extend(
self._suggest_structure_improvements(evaluation.structure, parsed_content)
)
# 按优先级排序
priority_order = {"high": 0, "medium": 1, "low": 2}
suggestions.sort(key=lambda s: priority_order.get(s.priority, 3))
return suggestions
def _suggest_readability_improvements(
self, metrics: ReadabilityMetrics, content: Dict[str, Any],
) -> List[RewriteSuggestion]: 17018.baike.tongsou.com
"""生成可读性改进建议"""
suggestions = []
# 句长过长
if metrics.avg_sentence_length > 25: 17017.baike.tongsou.com
suggestions.append(RewriteSuggestion(
suggestion_id=str(uuid.uuid4(17016.baike.tongsou.com)),
original_text="",
suggested_text="将长句拆分为短句,提高可读性。",
strategy=RewriteStrategy.CONDENSE,
reason=f"平均句长{metrics.avg_sentence_length}词,超过理想范围。",
priority="high",
expected_improvement=10.0,
))
#原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。