Wednesday, February 10, 2021

Pelican 與 Blogger 內容同步2

cmsimde 中的 Pelican blog 內容建議採用 config 目錄中的 pelican.leo 進行管理, 主要的資料管理架構採用 Leo Editor 中的 @clean 標題指令, 能夠與內文指令 @others 配合, 利用階層式的文章管理, 區隔網誌摘要與各段內容.

Google Developer Console

為了能夠將 Pelican blogLeo Editor 中的網誌文章推向 Blogger, 必須要從 Google developer console 取得 與 Blogger 擷取權限對應的 secret json 檔案. 實際操作流程如下:

進入 Library - > ENABLE APIS AND SERVICES -> 啟用 Blogger API v3

設定 OAuth consent screen

新增 Credentials -> Desktop-type Oauth 2.0 client -> Download JSON 即可取得 secret.json 檔案.

將 secrets.json 轉為 token.dat

轉換前必須確定系統已經安裝 google_auth_oauthlib

pip install google_auth_oauthlib

接著利用下列程式將 secrets.json 轉為 token.dat, 隨後使用者就可利用此一 token.dat 將 Leo Editor 中的網誌文章內容傳送到對應的 Blogger.

下列程式在轉換過程會透過操作系統的內定瀏覽器讓使用者登入與 secrets.json 對應的帳號, 一旦通過認證就可以完成 secrets.json 轉為 token.dat 的流程.

# get secrets: https://console.developers.google.com
# https://developers.google.com/blogger/docs/3.0/using
# pip install google_auth_oauthlib
# under Mac command + b to execute
import pickle
import os
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

SCOPES = ['https://www.googleapis.com/auth/blogger', ]

# we check if the file tBo store the credentials exists
if not os.path.exists('./../../yen_gm_blogger_token.dat'):

    flow = InstalledAppFlow.from_client_secrets_file('./../../yen_gm_blogger_secrets.json', SCOPES)
    credentials = flow.run_local_server()

    with open('yen_gm_blogger_token.dat', 'wb') as credentials_dat:
        pickle.dump(credentials, credentials_dat)
else:
    with open('yen_gm_blogger_token.dat', 'rb') as credentials_dat:
        credentials = pickle.load(credentials_dat)
service = build('blogger', 'v3', credentials=credentials)
g.es(service)

將 Pelican 文章轉往 Blogger

將 Pelican 文章轉投 Blogger 的過程包含新增與編輯, 新增的程式碼如下:

from markdown import markdown
from oauth2client import client
#from googleapiclient import sample_tools
import os
# 配合使用 credential token
import pickle
from googleapiclient.discovery import build
#from google_auth_oauthlib.flow import InstalledAppFlow
#from google.auth.transport.requests import Request

os.environ['TZ'] = 'Asia/Taipei'
with open('./../../yen_gm_blogger_token.dat', 'rb') as credentials_dat:
    credentials = pickle.load(credentials_dat)
service = build('blogger', 'v3', credentials=credentials)

def get_cat_tag_content(data):
    # 請注意, 因為 data 來自 .md 的檔案 內容, 第1行為 ---
    # 用跳行符號分割
    data_list = data.split("\n")
    #第 2 行為 title
    title= data_list[1]
    #第 4 行為 category
    category = data_list[3]
    #第 5 行為 tags
    tags = data_list[4]
    # 有多項資料的 content 型別為數列
    # 再將第 9 行之後的資料數列串回成以跳行隔開的資料
    content = "\n".join(data_list[8:])
    # 先將截斷摘要與內文的 pelican md 檔按符號, 換成 Blogger 的 
    content = content.replace('', '')
    # 接著若內容有 ~~~python 與 ~~~ 則換成 Wordpress 格式
    #content = content.replace('~~~python', '[code lang="python"]')
    #content = content.replace('~~~', '[/code]')
    return title, category, tags, content

# 從目前所在節點的 body pan 中取出類別, tags 以及文章內容
# p.h 為 @clean filename.md
# 因為要使用 @clean 節點掛上為後的 blogger post_id, 因此改為讀 .md 檔案
md_filename = p.h.split(" ")[1]
with open(md_filename, 'r', encoding="utf-8") as content_file:
    md_content = content_file.read()
# title_str, category_str, tags_str, content = get_cat_tag_content(p.b)
title_str, category_str, tags_str, content = get_cat_tag_content(md_content)
category = category_str.split(":")[1]
tags = tags_str.split(":")[1].split(",")
tags.append(category)
# title 是一個單獨的字串
title = title_str.split(":")[1]
# 將 markdown 格式 content 轉為 html
content = markdown(content)
# 以下處理 content 的 

標題 content = content.replace("

", "

") content = content.replace("

", "

") # g.es(content) try: ''' users = service.users() # 取得使用者 profile 資料 user = users.get(userId='self').execute() print('網誌名稱: %s' % user['displayName']) ''' blogs = service.blogs() # 取得使用者所建立網誌名稱 blogs = blogs.listByUser(userId='self').execute() # post_id is now blogs["items"][0]["id"] #blog_id = blogs["items"][0]["id"] blog_id = "2403495118140401474" #for blog in blogs['items']: #print(blog['name'], blog['url']) posts = service.posts() # 新增網誌 post 時, 需要 post_id body = { "kind": "blogger#post", "id": blog_id, "title": title, # 利用 markdown 函式, 將 .md 的內文轉為 html, 作為 Blogger 的文章內容 "content": content, "labels": tags } insert = posts.insert(blogId=blog_id, body=body) posts_doc = insert.execute() post_id = posts_doc["id"] #print(posts_doc) # 改用 credential token 後不會產生 blogger.dat #os.remove("blogger.dat") # 利用最後的 child 節點來儲存 post_id to_save_post_id = p.insertAsLastChild() # 改為內文為空的節點, id 直接標在 head 標題 to_save_post_id.b = "" to_save_post_id.h = post_id # 因為新增節點, commander 必須 redraw c.redraw() g.es("post_id 為", post_id) g.es("已經將資料送往 KBlogger!") except(client.AccessTokenRefreshError): g.es("error")

完成上述文章轉投至 Blogger 之後, Blogger 會傳回該文章的 post id, 而新增程式會將此 id 放在該筆 @clean 文章節點的最末端, 由於該 post id 節點只有標題而無內文, 因此即便內縮成為 @clean 的子節點, 也不會在文章中增加任何資料, 但若該網誌內容同步推向一個以上的 Blogger, 則使用者需要將該 post if 內縮外, 還需要在此 post id 節點的上屬節點增加標註, 說明該 post id 所屬的網誌標題或代號.

至於當該文章內容經過編修後, 使用者若希望將新內容推向遠端同步 Blogger, 則必須將原先新增的 Blogger post id 移至該 @clean 文章的最末端, 以便讓程式可以更新與此 post id 對應的 Blogger 文章內容.

可用於編輯 Blogger 文章的程式碼如下:

from markdown import markdown
from oauth2client import client
#from googleapiclient import sample_tools
import os
# 配合使用 credential token
import pickle
from googleapiclient.discovery import build
#from google_auth_oauthlib.flow import InstalledAppFlow
#from google.auth.transport.requests import Request

os.environ['TZ'] = 'Asia/Taipei'
with open('./../../yen_gm_blogger_token.dat', 'rb') as credentials_dat:
    credentials = pickle.load(credentials_dat)
service = build('blogger', 'v3', credentials=credentials)

def get_cat_tag_content(data):
    # 請注意, 因為 data 來自 .md 的檔案 內容, 第1行為 ---
    # 用跳行符號分割
    data_list = data.split("\n")
    #第 2 行為 title
    title= data_list[1]
    #第 4 行為 category
    category = data_list[3]
    #第 5 行為 tags
    tags = data_list[4]
    # 有多項資料的 content 型別為數列
    # 再將第 9 行之後的資料數列串回成以跳行隔開的資料
    content = "\n".join(data_list[8:])
    # 先將截斷摘要與內文的 pelican md 檔按符號, 換成 Blogger 的 
    content = content.replace('', '')
    # 接著若內容有 ~~~python 與 ~~~ 則換成 Wordpress 格式
    #content = content.replace('~~~python', '[code lang="python"]')
    #content = content.replace('~~~', '[/code]')
    return title, category, tags, content

# 從目前所在節點的 body pan 中取出類別, tags 以及文章內容
# p.h 為 @clean filename.md
# 因為要使用 @clean 節點掛上為後的 blogger post_id, 因此改為讀 .md 檔案
md_filename = p.h.split(" ")[1]
with open(md_filename, 'r', encoding="utf-8") as content_file:
    md_content = content_file.read()
# title_str, category_str, tags_str, content = get_cat_tag_content(p.b)
title_str, category_str, tags_str, content = get_cat_tag_content(md_content)
category = category_str.split(":")[1]
tags = tags_str.split(":")[1].split(",")
tags.append(category)
# title 是一個單獨的字串
title = title_str.split(":")[1]
# 將 markdown 格式 content 轉為 html
content = markdown(content)
# 以下處理 content 的 

標題 content = content.replace("

", "

") content = content.replace("

", "

") # g.es(content) try: blogs = service.blogs() # 取得使用者所建立網誌名稱 blogs = blogs.listByUser(userId='self').execute() #blog_id = blogs["items"][0]["id"] blog_id = "2403495118140401474" # 設法取得原 post 的 id postid_outline = p.getLastChild() # 直接從標題取得 post 的 id 號碼 post_id = postid_outline.h posts = service.posts() # 更新網誌文章時的 body body = { "kind": "blogger#post", "title": title, "content": content } # need to save postId to outline head update = posts.update(blogId=blog_id, postId=post_id, body=body, publish=True) update_doc = update.execute() # 使用 credential token 後, 無需刪除 blogger.dat #os.remove("blogger.dat") g.es("post_id 為", post_id) g.es("已經將更新資料送往 K Blogger!") except(client.AccessTokenRefreshError): g.es("error")

No comments:

Post a Comment