diff --git a/src/DjangoBlog-master/plugins/seo_optimizer/plugin.py b/src/DjangoBlog-master/plugins/seo_optimizer/plugin.py index b5b19a3..66a9192 100644 --- a/src/DjangoBlog-master/plugins/seo_optimizer/plugin.py +++ b/src/DjangoBlog-master/plugins/seo_optimizer/plugin.py @@ -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''' @@ -34,49 +41,58 @@ class SeoOptimizerPlugin(BasePlugin): ''' + # 为每个标签添加meta标签 for tag in article.tags.all(): meta_tags += f'' meta_tags += f'' + # 生成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'' + # 组合所有SEO标签并返回 return f"""