Update plugin.py

master
zxc 4 months ago
parent 94c7dbed19
commit 74c83abfde

@ -1,6 +1,6 @@
import json
from django.utils.html import strip_tags
from django.template.defaultfilters import truncatewords
from django.template.defaultfiltersfilters import truncatewords
from djangoblog.plugin_manage.base_plugin import BasePlugin
from djangoblog.plugin_manage import hooks
from blog.models import Article, Category, Tag
@ -8,22 +8,29 @@ from djangoblog.utils import get_blog_setting
class SeoOptimizerPlugin(BasePlugin):
PLUGIN_NAME = 'SEO 优化器'
PLUGIN_DESCRIPTION = '为文章、页面等提供 SEO 优化,动态生成 meta 标签和 JSON-LD 结构化数据。'
PLUGIN_VERSION = '0.2.0'
PLUGIN_AUTHOR = 'liuangliangyy'
# 插件元信息定义
PLUGIN_NAME = 'SEO 优化器' # 插件名称
PLUGIN_DESCRIPTION = '为文章、页面等提供 SEO 优化,动态生成 meta 标签和 JSON-LD 结构化数据。' # 功能描述
PLUGIN_VERSION = '0.2.0' # 版本号
PLUGIN_AUTHOR = 'liuangliangyy' # 作者
# 注册钩子将SEO生成逻辑绑定到'head_meta'钩子(页面头部元信息钩子)
def register_hooks(self):
hooks.register('head_meta', self.dispatch_seo_generation)
# 生成文章页面的SEO数据
def _get_article_seo_data(self, context, request, blog_setting):
# 从上下文获取文章对象验证是否为Article实例
article = context.get('article')
if not isinstance(article, Article):
return None
# 提取文章描述移除HTML标签截取前150字符
description = strip_tags(article.body)[:150]
# 提取关键词(标签名称组合,默认使用网站关键词)
keywords = ",".join([tag.name for tag in article.tags.all()]) or blog_setting.site_keywords
# 生成Open Graph社交分享meta标签
meta_tags = f'''
<meta property="og:type" content="article"/>
<meta property="og:title" content="{article.title}"/>
@ -34,49 +41,58 @@ class SeoOptimizerPlugin(BasePlugin):
<meta property="article:author" content="{article.author.username}"/>
<meta property="article:section" content="{article.category.name}"/>
'''
# 为每个标签添加meta标签
for tag in article.tags.all():
meta_tags += f'<meta property="article:tag" content="{tag.name}"/>'
meta_tags += f'<meta property="og:site_name" content="{blog_setting.site_name}"/>'
# 生成JSON-LD结构化数据供搜索引擎解析的标准化数据
structured_data = {
"@context": "https://schema.org",
"@type": "Article",
"mainEntityOfPage": {"@type": "WebPage", "@id": request.build_absolute_uri()},
"headline": article.title,
"description": description,
"image": request.build_absolute_uri(article.get_first_image_url()),
"image": request.build_absolute_uri(article.get_first_image_url()), # 文章首图
"datePublished": article.pub_time.isoformat(),
"dateModified": article.last_modify_time.isoformat(),
"author": {"@type": "Person", "name": article.author.username},
"publisher": {"@type": "Organization", "name": blog_setting.site_name}
}
# 若没有图片则移除image字段
if not structured_data.get("image"):
del structured_data["image"]
return {
"title": f"{article.title} | {blog_setting.site_name}",
"title": f"{article.title} | {blog_setting.site_name}", # 页面标题(含网站名)
"description": description,
"keywords": keywords,
"meta_tags": meta_tags,
"json_ld": structured_data
}
# 生成分类页面的SEO数据
def _get_category_seo_data(self, context, request, blog_setting):
# 从上下文获取分类名称
category_name = context.get('tag_name')
if not category_name:
return None
# 查询分类对象
category = Category.objects.filter(name=category_name).first()
if not category:
return None
# 构造页面标题、描述和关键词
title = f"{category.name} | {blog_setting.site_name}"
description = strip_tags(category.name) or blog_setting.site_description
keywords = category.name
# BreadcrumbList structured data for category page
breadcrumb_items = [{"@type": "ListItem", "position": 1, "name": "首页", "item": request.build_absolute_uri('/')}]
breadcrumb_items.append({"@type": "ListItem", "position": 2, "name": category.name, "item": request.build_absolute_uri()})
# 生成面包屑导航的JSON-LD数据提升页面结构可读性
breadcrumb_items = [
{"@type": "ListItem", "position": 1, "name": "首页", "item": request.build_absolute_uri('/')},
{"@type": "ListItem", "position": 2, "name": category.name, "item": request.build_absolute_uri()}
]
structured_data = {
"@context": "https://schema.org",
@ -88,12 +104,13 @@ class SeoOptimizerPlugin(BasePlugin):
"title": title,
"description": description,
"keywords": keywords,
"meta_tags": "",
"meta_tags": "", # 分类页暂不添加额外meta标签
"json_ld": structured_data
}
# 生成默认页面如首页的SEO数据
def _get_default_seo_data(self, context, request, blog_setting):
# Homepage and other default pages
# 生成网站级JSON-LD数据含搜索功能描述
structured_data = {
"@context": "https://schema.org",
"@type": "WebSite",
@ -101,36 +118,44 @@ class SeoOptimizerPlugin(BasePlugin):
"potentialAction": {
"@type": "SearchAction",
"target": f"{request.build_absolute_uri('/search/')}?q={{search_term_string}}",
"query-input": "required name=search_term_string"
"query-input": "required name=search_term_string" # 声明搜索框参数
}
}
return {
"title": f"{blog_setting.site_name} | {blog_setting.site_description}",
"title": f"{blog_setting.site_name} | {blog_setting.site_description}", # 首页标题
"description": blog_setting.site_description,
"keywords": blog_setting.site_keywords,
"meta_tags": "",
"json_ld": structured_data
}
# 分发SEO数据生成逻辑根据页面类型调用对应生成方法
def dispatch_seo_generation(self, metas, context):
# 从上下文获取请求对象
request = context.get('request')
if not request:
return metas
# 获取当前视图名称(判断页面类型)
view_name = request.resolver_match.view_name
# 获取博客全局设置
blog_setting = get_blog_setting()
# 根据不同页面类型生成对应SEO数据
seo_data = None
if view_name == 'blog:detailbyid':
if view_name == 'blog:detailbyid': # 文章详情页
seo_data = self._get_article_seo_data(context, request, blog_setting)
elif view_name == 'blog:category_detail':
elif view_name == 'blog:category_detail': # 分类详情页
seo_data = self._get_category_seo_data(context, request, blog_setting)
# 若未匹配到特定页面类型使用默认SEO数据
if not seo_data:
seo_data = self._get_default_seo_data(context, request, blog_setting)
# 生成JSON-LD脚本标签
json_ld_script = f'<script type="application/ld+json">{json.dumps(seo_data.get("json_ld", {}), ensure_ascii=False, indent=4)}</script>'
# 组合所有SEO标签并返回
return f"""
<title>{seo_data.get("title", "")}</title>
<meta name="description" content="{seo_data.get("description", "")}">
@ -139,4 +164,6 @@ class SeoOptimizerPlugin(BasePlugin):
{json_ld_script}
"""
# 实例化插件,自动注册钩子使其生效
plugin = SeoOptimizerPlugin()

Loading…
Cancel
Save