
前回、概要編として、コストメリットの出るWebサイトの作り方を紹介しましたが、その中のポイントでもある、GoogleBloggerをJavascriptで読み込みするモジュールを作ったので、今回はそれを紹介したいと思います。
ソースコード
このブログの中のJavascriptラベルの一覧を取得するプログラムです。
index.html
<meta charset="utf-8">
<script type="module" src="main.js"></script>
<h1>Google Blogger</h1>
<div class="content"></div>
main.js
import { Blogger } from "./blogger.js"
class Main{
  constructor(options){
    this.options = options || {}
    this.promise = new Promise((resolve, reject)=>{
      this.resolve = resolve
      this.reject  = reject
      new Blogger({
        blog_id : "1302948195388337515",
        domain : "blog.myntinc.com",
        label : "Javascript",
        type : "posts",
        response : "summary",
        orderby : "published",
      }).promise.then((res)=>{
        console.log(res)
      })
    })
  }
  finish(res){
    this.resolve(res)
  }
}
switch(document.readyState){
  case "complete":
  case "interactive":
    new Main()
  break
  default:
    window.addEventListener("DOMCOntentLoaded", (()=>new Main()))
}
blogger.js
export class Blogger{
  constructor(options){
    this.promise = new Promise((resolve, reject)=>{
      this.time = (+new Date())
      this.options = options || {}
      this.set_callback()
      this.resolve = resolve
      this.reject  = reject
      this.load()
    })
  }
  get domain(){
    if(this.options.blog_name){
      return `${this.options.blog_name}.blogspot.com`
    }
    else if(this.options.domain){
      return this.options.domain
    }
    else{
      return null
    }
  }
  get type(){
    switch(this.options.type){
      case "pages":
      case "page":
        return "pages"
      case "article":
      case "posts":
      default:
        return "posts"
    }
  }
  get feed(){
    // blog_idを指定すると、取得件数が多くできる
    const feed = this.options.blog_id ? `https://www.blogger.com/feeds/${this.options.blog_id}` : `https://${this.domain}/feeds`
    switch(this.type){
      case "pages":
        return `${feed}/pages/${this.response}`
      case "comments":
        return `${feed}/comments/${this.response}`
      case "post_comments"://postID/comments
        return `${feed}/${this.options.post_id}/comments/${this.response}`
      case "posts":
        default:
        return `${feed}/posts/${this.response}`
    }
  }
  get response(){
    switch(this.options.response){
      case "summary":
        return "summary"
      default:
        return "default"
    }
  }
  get path(){
    if(this.options.path){
      return this.options.path
    }
    else if(this.options.page){
      return this.options.page
    }
  }
  get label(){
    if(!this.options.label){
      return ""
    }
    // ラベルが複数(配列)ある場合(AND検索)
    if(this.options.label.constructor === Array){
      // return "/-/" + this.options.label.join("/")
      return ""
    }
    else{
      return `/-/${this.options.label}`
    }
  }
  get max_results(){
    return this.options.max_results ? this.options.max_results : ""
  }
  // 並び順 (published:公開日:default , updated:更新日 )
  get orderby(){
    return this.options.orderby || ""
  }
  get start_index(){
    return this.options.start_index || ""
  }
  // 公開日(◯日以降): yyyy-mm-dd
  get published_min(){
    return this.options.publiched_min || ""
  }
  // 公開日(◯日以前): yyyy-mm-dd
  get published_max(){
    return this.options.publiched_max || ""
  }
  // 原稿内検索 (A+B AND検索, A-B NOT検索 , A|B OR検索)
  get search(){
    const datas = []
    if(this.options.search && this.options.search.constructor === Array){
      for(const word of this.options.search){
        datas.push(word)
      }
    }
    else if(typeof this.options.search === "string"){
      datas.push(this.options.search)
    }
    if(this.options.label && this.options.label.constructor === Array){
      for(const label of this.options.label){
        datas.push(`label:${label}`)
      }
    }
    return datas.join("|")
  }
  get query(){
    const datas = []
    if(this.max_results){
      datas.push(`max-results=${this.max_results}`)
    }
    if(this.orderby){
      datas.push(`orderby=${this.orderby}`)
    }
    if(this.start_index){
      datas.push(`start-index=${this.start_index}`)
    }
    if(this.published_min){
      datas.push(`published-min=${this.published_min}`)
    }
    if(this.published_max){
      datas.push(`published-max=${this.published_max}`)
    }
    if(this.search){
      datas.push(`q=${this.search}`)
    }
    if(this.path){
      datas.push(`path=${this.path}`)
    }
    if(datas.length){
      return "&"+ datas.join("&")
    }
    else{
      return ""
    }
  }
  get url(){
    if(this.options.url){
      return this.options.url
    }
    else if(this.type === "pages"){
      return `${this.feed}${this.label}?alt=json&callback=window.${this.callback_name}&${this.path}${this.query}`
    }
    else{
      return `${this.feed}${this.label}?alt=json&callback=window.${this.callback_name}&${this.query}`
    }
  }
  get callback_name(){
    return `blogger_callback_${this.time}`
  }
  set_callback(){
    window[this.callback_name] = this.blogger_callback.bind(this)
  }
  load(){
    if(!this.domain){
      this.finish(null)
      return
    }
    const script = document.createElement("script")
    // console.log(this.url)
    script.src = this.url
    document.body.appendChild(script)
  }
  blogger_callback(res){
    if(!res || !res.feed || !res.feed.entry){
      console.warn(res)
      this.finish(null)
      return
    }
    const datas = []
    for(const entry of res.feed.entry){
      const url  = entry.link.find(e => e.rel === "alternate").href
      const path = "/"+ url.split("/").slice(3).join("/")
      const html = entry.content ? entry.content["$t"] : ""
      const img  = entry["media$thumbnail"] ? entry["media$thumbnail"].url : ""
      datas.push({
        id         : entry.id["$t"],
        date       : entry.published["$t"].split("T")[0],
        time       : entry.published["$t"].split("T")[1].split(".")[0], //09:00:00.000+09:00
        title      : entry.title["$t"],
        html       : html,
        text       : this.get_html2text(html),
        url        : url,
        path       : path,
        label      : entry.category ? entry.category.map(e => e.term) : null,
        thumbnail  : img,
        img        : this.convert_img_size(img, "s1600"),
        img_middle : this.convert_img_size(img, "s800"),
        img_small  : this.convert_img_size(img, "s400"),
      })
    }
    this.finish(datas)
  }
  get_html2text(html){
    const div = document.createElement("div")
    div.innerHTML = html
    return div.textContent.trim()
  }
  convert_img_size(img, size){
    const sp = img.split("/")
    sp[7]    = size
    return sp.join("/")
  }
  finish(res){
    this.resolve(res)
  }
}
使い方と解説
上記ソースコードを同じファイルに保存して、httpプロトコル(vscodeのGoLiveや、docker等)でブラウザアクセスすると、ブラウザコンソールに、取得したデータ(記事一覧)が配列で表示されます。
こんな感じです。
少し解説すると、index.htmlで呼び出すmain.jsは、モジュールタイプになっているので、1ファイルですが、クラス構造で記述しています。
blogger.jsのBloggerクラスに、次のようなデータを送ると、記事一覧やページ情報を返してくれるというプログラムです。
{
  blog_id : "1302948195388337515",
  domain : "blog.myntinc.com",
  // domain : "mynt-inc.blogspot.com",
  label : "Javascript",
  type : "posts",
  response : "summary",
  orderby : "published",
  // max_results : 200,
}
それを、new宣言につなげて、.promise.then((res)=>{...})と書くことで、ajax的に取得したデータを、表示につなげることができます。
※表示方法は、それぞれのサイトにて自作してください。
あとがき
なんか自分で発見した裏技だと思っていたら、すでにWebで紹介している人がいました。
参考ページ : 
【徹底解説】Bloggerフィードの各種パラメータと使い方
今回のソースコードのクエリ情報などの参考にもさせてもらいました。
お陰で、これから作るWebページの礎になるモジュールを作ることができました。
Webサイト制作もこれからどんどん効率化していくことでしょう。
そして、この技術も先日作った「Webデスクトップ」にも使えるので、いろいろな機能追加して行こうと思います。
Webデスクトップ : 
Webデスクトップの制作
 
0 件のコメント:
コメントを投稿