0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

BOOK☆WALKERのデイリーランキングをスクレイピングで取得する。

Posted at

今回はBOOK☆WALKERのデイリーランキングを取得し、毎日Googleスプレッドシートに自動追記するGASを紹介します。

#完成像
book1.jpg
毎日ランキングが右に追記されていくようにします。

#データのソース
ランキングはこちらのURLから見れます。
URL(https://bookwalker.jp/rank/?mode=daily )の末尾?mode=dailyでデイリーランキングを指定しています。この部分を ?mode=weeklyにすると週間ランキングに、 ?mode=monthlyにすると月間ランキングにアクセスできます。

#方法
今回はソースコードからランキング(と評価・レビュー数)を取得します。BOOK☆WALKERのランキングのソースコードは少し変わっています。前半部分にJSONファイルで必要な情報を書いていて、そこからJSで各作品の情報を引っ張ってきています(多分)。
簡単に言えば、ランキングの情報が書いてあるJSONを取得すればいいだけですね。

GAS
  let url = "https://bookwalker.jp/rank/?mode=daily";
  let html = UrlFetchApp.fetch(url).getContentText('UTF-8');

GASのUrlFetchAppクラス(fetchメソッド)を使います。
.getContentText()でソースコード(HTML)を文字列として取得します。

ソースコードの中でも、必要なのはJSONの部分だけです。
そのため、余計な前後の部分を省く必要があります。
まずは、Parserライブラリを追加しましょう(やり方はこちらを参照)。
必要なJSONは、ソースコードの中の<script type="application/ld+json"></script>というタグの間にあります。

GAS
 let list = Parser
    .data(html)
    .from('<script type="application/ld+json">')
    .to('</script>')
    .build();
  let jsonData = JSON.parse(list)

.from().to()で切り取る部分を指定します。
listは文字列なので、扱いやすいようにJSON.parse(list)でJSON形式に直してあげましょう。
これで変数jsonDataにJSON形式のランキング情報を格納できました。
それでは、JSONの形式を詳しく見ていきましょう。

JSON
    {
        "@context": "http://schema.org",
        "@type": "Product",
        "name": "僕のヒーローアカデミア 33",
        "image": "https://c.bookwalker.jp/5759094/t_700x780.jpg",
        "description": "ヒーローへの信頼が揺らいでいる現状では生徒は不安を抱え、故に私たち教員は全力で彼らを守らなければならない! 雄英とは安心していつでも戻ってこられる場所。さあ! 君たちの声を…想いを…彼に届けるのさ! “Plus Ultra”!!",
        "url": "https://bookwalker.jp/deab48af8f-6549-4000-8b56-9dcb2b18459d/",
        "category": "マンガ",
        "brand": {
            "@type": "Brand",
            "name": "集英社"
        },
        "offers": {
            "@type": "Offer",
            "price": 460,
            "priceCurrency": "JPY",
            "itemCondition": "http://schema.org/NewCondition",
            "availability": "http://schema.org/InStock",
            "seller": {
                "@type": "Organization",
                "name": "BOOK☆WALKER",
                "logo": "https://bookwalker.jp/henry/pc/img/common/bw-logo.png",
                "url": "https://bookwalker.jp/",
                "sameAs": "https://twitter.com/BOOK_WALKER"
            },
         "aggregateRating": {
            "@type": "AggregateRating",
            "ratingValue": 4.9,
            "reviewCount": 108,
            "bestRating": 5,
            "worstRating": 0
        }
    },

今回取得するのは作品名(name)、評価(ratingValue)、レビュー数(reviewCount)を取得します。
ちなみに、今回の場合はそれぞれの項目が何を指しているのか、こちらで確認できます。

それでは、以上の3項目を配列に入れましょう。

GAS
  let outputList = [] //配列を生成
  let jsonLength = jsonData.length //JSONのデータ個数を取得
  for (let i = 0; i < jsonLength; i++) { //for文を回す回数をJSONのデータ個数にする
    let name = jsonData[i].name  //i個目の作品の名前
    let ratingValue = jsonData[i].aggregateRating.ratingValue //i個目の作品の評価
    let reviewCount = jsonData[i].aggregateRating.reviewCount  //i個目の作品のレビュー数
    outputList.push([name, ratingValue, reviewCount])
  }

しかし、このままではエラーが起きます。
なぜなら、すべての作品に評価とレビュー数があるとは限らないからです。
image.png

なので、このままだと評価・レビュー数がない作品を取得しようとすると以下のようなエラーが出ます。

TypeError: Cannot read property 'ratingValue' of undefined

そのため、エラーが発生した場合は、当該作品の評価を飛ばす必要があります。

さらに見出しを追加したコードは以下になります。

GAS

  let date = new Date();
  let today = Utilities.formatDate(date, 'JST', 'yyyy-MM-dd kk:mm:ss') //今日の日付を取得

  let outputList = [[today, '評価', 'レビュー数']] //配列を生成し、配列の見出し(日付、評価、レビュー数)を挿入
  let jsonLength = jsonData.length
  for (let i = 0; i < jsonLength; i++) {
    let name = jsonData[i].name
    try {
      ratingValue = jsonData[i].aggregateRating.ratingValue
    } catch { //エラーが発生した場合は
      ratingValue = "" //評価を空白に
    }
    try {
      reviewCount = jsonData[i].aggregateRating.reviewCount
    } catch { //エラーが発生した場合は
      reviewCount = ""  //レビュー数を空白に
    }
    outputList.push([name, ratingValue, reviewCount])
  }

//console.log(outputList)で確認すると↓
//[ [ '2022-02-06 21:53:48', '評価', 'レビュー数' ],
//  [ '僕のヒーローアカデミア 33', 4.9, 108 ],
//  [ 'ザ・ファブル The second contact(2)', 4.8, 54 ],
//  [ '【電子版】B\'s-LOG COMIC 2022 Feb. Vol.109', 5, 3 ],
//  [ 'ラストギアス (6)', 4.3, 22 ],
//  [ 'その着せ替え人形は恋をする 7巻', 4.8, 842 ],
// (以下省略)

ちなみに、変数を定義する際にletを外してグローバル変数としています。

これで、必要な情報は配列に挿入できましたので、あとはスプレッドシートに追加するだけです。

最終列の1つ右の列に毎日どんどんデータを追記していきたいです。

GAS
  let daily = SpreadsheetApp.openById("スプレッドシートID").getSheetByName('シート名')
  let lastCol = daily.getLastColumn() //最終列を取得
  daily.getRange(1, lastCol + 1, 101, 3).setValues(outputList) //最終列の右の列に101行(データ数(100)+見出し)、3列(作品名、評価、レビュー数)のデータを書く

以上になります。完成したコードは以下です。

GAS
function bookwalkerDaily() {
  let url = "https://bookwalker.jp/rank/?mode=daily";
  let html = UrlFetchApp.fetch(url).getContentText('UTF-8');
 
  let list = Parser
    .data(html)
    .from('<script type="application/ld+json">')
    .to('</script>')
    .build();
  let jsonData = JSON.parse(list)
 
  let date = new Date();
  let today = Utilities.formatDate(date, 'JST', 'yyyy-MM-dd kk:mm:ss')
 
  let outputList = [[today, '評価', 'レビュー数']]
  let jsonLength = jsonData.length
  for (let i = 0; i < jsonLength; i++) {
    let name = jsonData[i].name
    try {
      ratingValue = jsonData[i].aggregateRating.ratingValue
    } catch {
      ratingValue = ""
    }
    try {
      reviewCount = jsonData[i].aggregateRating.reviewCount
    } catch {
      reviewCount = ""
    }
    outputList.push([name, ratingValue, reviewCount])
  }
  let daily = SpreadsheetApp.openById("スプレッドシートID").getSheetByName('シート名')
  let lastCol = daily.getLastColumn()
  daily.getRange(1, lastCol + 1, 101, 3).setValues(outputList)
}

完成像の写真にある左端の順位は手動で1から100まで追加してください。

今回は以上となります!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?