パースしてシートに張り付けていこう
今回は得たデータをパース(分割)してシートに張り付けていきます。
サンプルコードをコピペすると空行が勝手に入るので、それを消して使ってください……。
すみません……
「さて今回からはパースについて説明していくよ」
「なんだか久しぶりな気がしますね」
「……ごめんね」
※長らくお待たせしました
「……そういえば今回はパースについてなんですね? あのパースってなんなんですか?」
「えーっと、パースっていうのはプログラムの機能・処理の一つで、一定の書式や文法に従って記述されたデータを解析し、プログラムで扱えるようなデータ構造の集合体に変換することだよ。
よく『パーサ』(parser)あるいは『パーザ』とかいうよ」
「……すいません。あまりよく分からないのですが……」
「まぁ要するに単純な文字列の集まりを、データ分析しやすいように分解するものだと思ってもらえばいいよ。Excelでいうとこんな感じに、文字列をそれぞれの要素に分けてセルに入れていくそんなものだと理解してくらたらいいかな」
※A1のセルがパース前、B2:E6がパースした後です。
※この章でやるのは取得データを1つのセルに全部入れるのではなくそれぞれの要素ごとに分解することですね。
「そうなんだ。たしかに表に分けると見やすいですね」
「うん。見やすいだけではなくて集計分析しやすくなるよ。ということで、ここでは得られたデータをExcelのセルに1つ1つ格納していくやり方を教えるね。ちなみにパースのやり方はいっぱいあるから自分にあった方法を見つけてね。ここでは1つの例だけしかやらないし、これが正解というわけではないので注意ね」
「はーい」
「ということで簡単になろう小説APIのパース関数を作ったからこれを使う方法を教えていくね」
※忘れている方は最初から読み返してね♪
※動かなかったらメッセージください……
1.前回作成したエクセルファイルのSheet1のところに以下のプログラムをコピペしてね。
(Sheet名を変えたらその名称も変わります。)
その場合
SheetName = "Sheet1"
の""を変えたシート名にしてください。
■サンプル なろう小説APIパースサンプル■
*☆*――*☆*――*☆*――*☆*――*☆*――*☆*――*☆*――*☆*
Option Explicit
Private Sub CommandButton1_Click()
Dim i As Integer, j As Integer, k As Integer, Allcount As Long, LoopNum As Integer
Dim AryID As Variant, AryIDCaption As Variant, SplList() As String
Dim StrAPIData As String, StrGetCommand As String, SheetName As String
Dim buf As Variant, buf2 As Variant, strbuf As String
Application.ScreenUpdating = False 'デバッグするときはコメントアウト
'パースするための識別子の事前準備。
'サンプルコードをコピペすると空行が勝手に入るので、それを消して使ってください……。赤くならなければOKです。
AryID = Array("title:", "ncode:", "userid:", "writer:", "story:", "biggenre:", " genre:" , "gensaku:", "keyword:", "general_firstup:", "general_lastup:", "novel_type:", "end:", "general_all_no:" , "length:", "time:", "isstop:", "isr15:", "isbl:", "isgl:", "iszankoku:", "istensei:" , "istenni:", "pc_or_k:", "global_point:", "fav_novel_cnt:", "review_cnt:", "all_point:", "all_hyoka_cnt:" , "sasie_cnt:", "kaiwaritu:", "novelupdated_at:", "updated_at:", "weekly_unique:")
AryIDCaption = Array("小説名", "Nコード", "ユーザID", "作者名", "あらすじ", "大ジャンル", "ジャンル" _
, "現在未使用項目", "キーワード", "初回掲載日", "最終掲載日", "連載の場合は1、短編の場合は2", "短編と完結は0連載中は1", "全掲載部分数" , "小説文字数", "読了時間", "長期連載停止中なら1", "R15", "ボーイズラブ", "ガールズラブ", "残酷な描写あり", "異世界転生" , "異世界転移", "投稿媒体(1ケータイ、2PC、3PCとケータイ)", " 総合評価ポイント", "ブックマーク数", "レビュー数", "評価点", "評価者数" , "挿絵の数", "会話率", "小説の更新日時", "最終更新日時", "週間ユニーク")
'Sheet名を変えた場合は""内を変えたシート名に変える
SheetName = "Sheet1"
Sheets(SheetName).Cells.Clear 'Sheetのクリア
'APIコマンド 並べ替えを総合評価順にする。※アレンジする場合は自分の出力したいものにする。(例題あり)
StrGetCommand = "https://api.syosetu.com/novelapi/api/?order=hyoka" '
StrAPIData = GetAPIData(StrGetCommand & "&lim=1") 'APIのデータを取得する。まずはデータ数を確認するため出力は1個にしておく
k = 1
'APIから取得したデータの要素の確認を行う
For i = 0 To UBound(AryID)
If InStr(StrAPIData, AryID(i)) <> 0 Then
Sheets(SheetName).Cells(1, k + 1) = AryIDCaption(i) 'Headerに記述していく
ReDim Preserve SplList(1 To k)
SplList(k) = AryID(i)
k = k + 1
End If
Next
'全体数の確認
buf = Split(StrAPIData, "allcount:")
Allcount = CLng(Left(buf(1), InStr(buf(1), vbLf) - 1))
Sheets(SheetName).Cells(1, 1) = Allcount 'A1には全件数を入れる
If Allcount > 500 Then '500以上の場合はループさせる
LoopNum = Application.WorksheetFunction.RoundUp(Allcount / 500, 0) 'Loop数のカウント 切り上げ
Else
LoopNum = 1
End If
If LoopNum > 4 Then LoopNum = 4 'max 2000件まで 4回分 そんなに必要なければ1とかにしても
For i = 1 To LoopNum
StrAPIData = GetAPIData(StrGetCommand & "&lim=500&st=" & ((i - 1) * 500 + 1)) '1, 501, 1001, 1501 stは取得開始番号
'それぞれの要素のパース
'Debug.Print StrAPIData
buf = Split(StrAPIData, SplList(1)) '1個めの要素で先に分割。
For j = 1 To UBound(buf) '小説数だけカウント
For k = 1 To UBound(SplList)
'Debug.Print buf(j)
If k = 1 Then '最初の項の処理
strbuf = Left(buf(j), InStr(buf(j), SplList(k + 1)) - 1)
ElseIf k = UBound(SplList) Then '最終項の処理
buf2 = Split(buf(j), SplList(k))
strbuf = buf2(1)
Else
buf2 = Split(buf(j), SplList(k))
strbuf = Left(buf2(1), InStr(buf2(1), SplList(k + 1)) - 1)
End If
Call strCleaern(strbuf, SplList(k)) '余計な空行などの除去
'分割した要素をCellに入れていく Variantで一括貼り付けすると高速化できる
Sheets(SheetName).Cells((i - 1) * 500 + j + 1, k + 1) = strbuf
Next
Next
Next
Application.ScreenUpdating = True
End Sub
'余計な改行コードと空白の除去 + 文字列の場合の特殊処理追加
Sub strCleaern(strbuf As String, strID As String)
With WorksheetFunction 'ワークシート関数の使用のため
strbuf = Trim(.Clean(strbuf)) '空行などの除去
Select Case strID
Case "story:", "title:", "keyword:", "writer:" '文字列が返るときの処理は若干特殊処理
If Left(strbuf, 1) = "|" Then strbuf = Right(strbuf, Len(strbuf) - 1)
If Left(strbuf, 1) = ">" Then strbuf = Right(strbuf, Len(strbuf) - 1)
strbuf = Trim(.Clean(strbuf)) 'あらすじの改行コードは残してもいいかもしれない。
If Left(strbuf, 1) = "|" Then strbuf = Right(strbuf, Len(strbuf) - 1) 'なぜか|が二つはいることもあるので削除
If Left(strbuf, 1) = ">" Then strbuf = Right(strbuf, Len(strbuf) - 1)
strbuf = Trim(.Clean(strbuf))
If IsNumeric(strbuf) Then strbuf = "'" & strbuf '文字列変換 数値だった場合に自動変換されるのを防ぐ
If IsDate(strbuf) Then strbuf = "'" & strbuf '文字列変換 日付だった場合に自動変換されるのを防ぐ
If Left(strbuf, 1) = "=" Then strbuf = "'" & strbuf '文字列変換 =が先頭だった場合に式に自動変換されてエラーが出るのを防ぐ
Case Else
If Right(strbuf, 1) = "-" Then strbuf = Left(strbuf, Len(strbuf) - 1) '-が入る場合の処理
End Select
End With
End Sub
*☆*――*☆*――*☆*――*☆*――*☆*――*☆*――*☆*――*☆*
「じゃぁ、これでテストボタンを押してみて」
※ボタンを押すとプログラムが走ります。だいたい10秒~30秒くらいで完了します。
「わ、すごい。急に表ができました。おぉ~。なんだか感動しました。ところでこれってなんのデータなんですか?」
「んーとね、これは総合ポイントの高い作品の上位2000位までのデータを一括で取得するプログラムだよ。これでExcelで扱える形式に変換されたし、次からそのデータを分析していこう」
「おぉー、ようやくですね。がんばります!」
■ ■- 以下パース補足 -■ ■
・Split関数 私は基本的にこの関数を使用してパースを行うことが多いです。
使い方は以下のサイトを見るとよいかと。
http://officetanaka.net/excel/vba/tips/tips62.htm
Split(元の文字列 , 区切り文字)
*☆*――*☆*――*☆*――*☆*――*☆*――*☆*――*☆*――*☆*
■https://api.syosetu.com/novelapi/api/?userid=1090932&lim=1■
---
-
allcount: 12
-
title: >
ランキング以外で「小説家になろう」の面白い小説を探したい! という方に必見。スコッパーな方々のためのレビュー情報サイトを紹介します。
ncode: N1243FL
userid: 1090932
writer: 夕月 悠里
story: |
ランキング以外で「小説家になろう」の面白い小説を探したい!
そんなスコッパーな方々のために作られたレビュー情報サイトの紹介です。
biggenre: 99
genre: 9903
gensaku:
keyword: >
スコッパー レビュー
関連サイト 面白い小説
情報サイト Twitter
general_firstup: 2019-04-14 12:21:12
general_lastup: 2019-04-14 12:39:41
novel_type: 2
end: 0
general_all_no: 1
length: 1281
time: 3
isstop: 0
isr15: 0
isbl: 0
isgl: 0
iszankoku: 0
istensei: 0
istenni: 0
pc_or_k: 2
global_point: 255
fav_novel_cnt: 15
review_cnt: 0
all_point: 225
all_hyoka_cnt: 24
sasie_cnt: 2
kaiwaritu: 19
novelupdated_at: 2019-04-14 12:39:41
updated_at: 2019-04-21 06:14:15
*☆*――*☆*――*☆*――*☆*――*☆*――*☆*――*☆*――*☆*
なろう小説APIの出力(YAML)ではこのようになります。
それぞれに識別が付いているのでそいつたちで区切っています。
またもう一つ使うことが多いのは
・Left関数 左から指定した文字数を抜き出す
LEFT(文字列,文字数)
・Right関数 右から指定した文字数を抜き出す
RIGHT(文字列,文字数)
・Len関数 文字列の長さを返す
LEN(文字列)
・Instr関数 指定した文字列の中に指定した文字が最初に含まれる
InStr( “検索対象となる文字列”, “検索する文字列”)
この辺りの関数を使えば、大体の形式に対応できると思います。
あんまり速度は重視してないのでこんな感じにしてますが、もっといいやり方があるかも知れませんね。
たとえばデータをJSON形式で取得してjavascriptオブジェクトを使ってパースするやり方もあります。
あとはpythonなんかだとYAMLパーサーがあるのでそれを使えば一発ですね。
色々とあるので自分にあった方法をお試しください。
■ ■- 取得データの応用例 -■ ■
今回は累計ポイント順のデータを得ましたが、ここの文字列を変えることで色々なデータが取得できます。
StrGetCommand = "https://api.syosetu.com/novelapi/api/?order=hyoka" '
『https://api.syosetu.com/novelapi/api/?order=hyoka』
を変化させて自分の好きな物を取得してみてください。
例を載せておくので色々と試してください
※詳細はおまけでやりますが、なろうデベロッパーの仕様をお確かめください。
https://dev.syosetu.com/man/api/
・感想が多い順
StrGetCommand = "https://api.syosetu.com/novelapi/api/?order=impressioncnt"
・レビューが多い順
StrGetCommand = "https://api.syosetu.com/novelapi/api/?order=reviewcnt"
・週間ユニークが多い順
StrGetCommand = "https://api.syosetu.com/novelapi/api/?order=weekly"
・ジャンル制限(大ジャンルが恋愛または文芸)
StrGetCommand = "https://api.syosetu.com/novelapi/api/?biggenre=1-3"
・文字数制限(文字数が2000文字から3000文字)
StrGetCommand = "https://api.syosetu.com/novelapi/api/?length=2000-3000"
・挿し絵数指定(挿絵が1つから5つまでの作品)
StrGetCommand = "https://api.syosetu.com/novelapi/api/?sasie=1-5"