본문 바로가기

75 장고 검색엔진최적화 - json-ld 적용

75.1 구글검색 갤러리

구글 검색엔진에게 내 사이트의 특성에 맞는 구조화된 데이터를 제공함으로써 구글 검색 결과를 다른 글들과 다르게 표현할 수 있게 됩니다.

구글검색 갤러리에서 어떤 데이터를 제공하면 어떤 결과가 나오는지 미리 확인 할 수 있으며, 기능가이드를 통해 요구하는 데이터를 확인 할 수 있습니다.

검색갤러리 : https://developers.google.com/search/docs/appearance/structured-data/search-gallery

내 글이 남들과 다른 구글검색 결과화면을 원하신다면 잘 살펴보시면 좋습니다.

75.2 구조화된 데이터 기능가이드 - HowTo

제 블로그는 step by step 형식으로 내용을 작성하였기에 방법(howto type) 형식이 맞는거 같습니다.

json-ld, 마이크로데이터 2가지중 하나로 제공하면 된다고 가이드에 나와있습니다.
기능가이드>방법 : https://developers.google.com/search/docs/appearance/structured-data/how-to


저는 json-ld로 장고에 만들겠습니다.

75.3 장고 django-json-ld 모듈 설치 - base.py

파이썬에 django-json-ld 모듈을 설치합니다.

pip install django-json-ld

모듈 설치 완료 후 Setting 파일인 base.py에 django_json_ld app을 추가하고 JSON_LD_EMPTY_INPUT_RENDERING 등의 옵션을 설정합니다.

django_json_ld 모듈 옵션

옵션 설명
JSON_LD_CONTEXT_ATTRIBUTE CBV에서 context에 등록되는 이름입니다. 기본 sd
JSON_LD_MODEL_ATTRIBUTE JsonLdDetailView 사용시 모델 프라퍼티 이름입니다. 기본 sd
JSON_LD_DEFAULT_CONTEXT CBV에서 기본 컨텍스트 입니다. 기본은 https://schema.org/
JSON_LD_INDENT 디버그 모드에서 사용되며, json을 들여쓰기하여 보기좋게 표시해줍니다.
JSON_LD_DEFAULT_TYPE CBV 사용시 기본유형입니다. 기본 Thing
JSON_LD_GENERATE_URL CBV 를 사용할 때 json-ld의 url을 생성합니다. 기본 True
JSON_LD_EMPTY_INPUT_RENDERING json-ld가 없으면 아무것도 렌더링 하지 않는 옵션입니다.
strict기본적으로 TemplateSyntaxError를 발생시킵니다.
silent아무것도 렌더링하지 않습니다.
generate_thing현재 페이지의 URL로 객체를 생성합니다

pypi.org에서 설치 방법과 사용방법을 확인할 수 있습니다.

pypi : https://pypi.org/project/django-json-ld/

75.4 template에 render_json_ld 추가 - base.html

jsonld가 들어갈 헤더페이지에 추가합니다.

{% load render_json_ld from json_ld %}
{% render_json_ld sd %}

75.5 JsonLdContextMixin - view.py 구현

pypi.org 가이드에서는 JsonLdContextMixin, JsonLdDetailView의 사용법을 알려주고 있습니다.
pyp django-json-ld : https://pypi.org/project/django-json-ld/

테이블 1개만 사용했으면 가이드의 Class-Based View example 처럼 사용해도 되겠지만
저는 2개의 테이블을 조인해서 사용하고 있어 그대로 사용할 수 가 없습니다.

view 구현시 query_set을 내용을 get_structured_data()에 담아 json-ld를 만들어야 되는데
get_structured_data()가 get_context_data()보다 더 빨리 처리되어
model에서 가져온 데이터를 담을 수 없었습니다.
그래서 get_context_data에서 get_structured_data()을 가져와 처리하는 방식으로 변경하였습니다.

#myapp/blog/views.py
from django.shortcuts import render
from django.views import generic
from django_json_ld.views import JsonLdContextMixin
# from django_json_ld.views import JsonLdDetailView

from .models import PyBlog
from .models import PyBlogDetail

#sidebar
from myapp.common.common_views import MenuMixin
import time



class BlogDetail(MenuMixin, JsonLdContextMixin, generic.DetailView):
    model = PyBlogDetail
    template_name   = "blog/blogDetail.html" 
    structured_data = {"@type": "HowTo",
                        "estimatedCost": {"@type": "MonetaryAmount","currency": "USD","value": "0"},
                        "supply": [{"@type": "HowToSupply","name": "server"}, ],
                        "tool": [{"@type": "HowToTool","name": "visual studio code"}, {"@type": "HowToTool","name": "python"}, ],                    
                        "totalTime": "P1D",
                       }

    def get_context_data(self, **kwargs):       
        context   = super().get_context_data(**kwargs)              
        query_set = PyBlog.objects.filter(id=self.kwargs['pk'], use_yn = 'Y')
        context['pageInfo'] = query_set[0].get_page_info()      
        page_title = query_set[0].title

        query_set = query_set.values('id','title','update_dt','regist_dt',
                                    'pb_detail__id',
                                    'pb_detail__detail_id',
                                    'pb_detail__sub_title','pb_detail__img_url','pb_detail__img_size','pb_detail__new_img_url',
                                    'pb_detail__content_body'
                                    ).order_by('pb_detail__sub_title')

        # print("query_set[0].pb_detail__content_body : ", query_set[0]['pb_detail__content_body'])
        page_img_url = query_set[0]['pb_detail__img_url']
        page_desc = query_set[0]['pb_detail__content_body'].replace('```','').replace('"','')[:140]

        context['dataList'] = query_set
        context['pageInfo'].update({ "title": page_title, "get_descript":' '.join(page_desc.split()), "img_url":page_img_url})
        context.update(self.getMenuList)        

        #json-ld 생성
        self.setJsonLD(query_set, page_title, context)

        return context

    def replaceImg(self, new_img, org_img):
        if new_img != None and new_img !='':
            return new_img.replace('/image/upload/','/image/upload/h_306,w_406,q_auto:best/')

        if org_img == None or org_img =='':
            return ''
        return org_img      

    def replaceText(self, text):
        return text.replace("```"," ").replace("<br/>"," ").replace("<br>"," ").replace("\r","").replace("\t","").replace("\n"," ").replace("<","").replace(">","")

    def setJsonLD(self, query_set, page_title, context):
        structured_data = super().get_structured_data()
        structured_data.update({'name':page_title, 'image':{ "@type": "ImageObject","height": "306","width": "406",
                                                "url": self.replaceImg(query_set[0]['pb_detail__new_img_url'],query_set[0]['pb_detail__img_url']),                           
                                               },
                 })     

        stepList = []       
        for i in query_set:
            stepList.append({
                  "@type": "HowToStep",
                  "url": f"{structured_data['url']}#{i['pb_detail__id']}",
                  "name": f"{i['pb_detail__sub_title']}",
                  "itemListElement":[{"@type": "HowToDirection", "text": f"{self.replaceText(i['pb_detail__content_body'][:400])}"}],
                  "image": {"@type": "ImageObject",
                            "url": f"{self.replaceImg(i['pb_detail__new_img_url'],i['pb_detail__img_url'])}",
                            "height": "306",
                            "width": "406"}
                })          
        structured_data.update({'step':stepList})       
        context.update({'sd':structured_data})

75.6 구조화 데이터 테스트

작업한 모든 결과를 서버에 반영 후에
구글에서 제공하는 스키마 마크업 테스트 도구를 통해 잘 적용되 었는지 확인합니다.

구조화 데이터 테스트 도구로 이동하여
구조화 데이터가 적용된 페이지의 URL을 입력합니다.

테스트도구 URL : https://developers.google.com/search/docs/appearance/structured-data

75.7 구조화 데이터 테스트 확인

에러 없이 잘 적용되었다면
구글에 사이트맵을 다시 읽도록 재요청 해주시면 됩니다.

구글 크롤링 재요청: https://www.google.com/ping?sitemap=https://pythonblog.co.kr/sitemap.xml

제가원하는대로 구글검색결과에서 남들과 다르게 나왔으면 좋겠습니다.

75.8 구글웹마스터도구 - 서치콘솔 확인

구조화가 적용된 페이지를 구글에서 다시 크롤링하면
구글 웹마스터 도구 - Google Search Console 에서
위 그림은 제가 운영하는 다른사이트에 리뷰스니펫을 적용했을때 구조화데이터에 관련된 섹션이 나온 내용입니다.

구글 검색엔진 최적화 검색결과

몇일 후에 모바일에서 확인해보니 위와 같이
구글 검색엔진에 howTo가 적용되어 검색 결과에 반영 된 모습니다.

75.9 구조화데이터 관련 링크 모음

json-ld 관련 링크
구글검색엔진 검색갤러리 https://developers.google.com/search/docs/appearance/structured-data/search-gallery
PYPI django-json-ld https://pypi.org/project/django-json-ld/
GITHUB django-json-ld https://github.com/hiimdoublej/django-json-ld
구조화 데이터 테스터 도구 https://developers.google.com/search/docs/appearance/structured-data
구글서치콘솔 https://search.google.com/search-console
json-ld 모듈 https://json-ld.org/
- json-ld 구현을 위한 각 언어별로 제공되는 모듈 및 명세서 등을 확인 할 수 있습니다.
현재글 : 75 장고 검색엔진최적화 - json-ld 적용
Comments
Login:

Copyright © PythonBlog 2021 - 2022 All rights reserved
Mail : PYTHONBLOG