なろう分析記録

『小説家になろう』をふくめ『ネット小説投稿サイト』を分析する。コード置き場,主にPython,javascript,たまに創作。

『なろう18禁小説API』の全作品・全項目データを取得するPythonスクリプト※コード改良しました

なろう18禁小説APIの”全作品情報データ”を取得する

f:id:karupoimou:20190501110902p:plain:w400
なろう18禁小説API全取得の例
なろうが提供するAPIには、「なろう小説API」とは別に『なろう18禁小説API』という物があります。

https://dev.syosetu.com/xman/api/:なろう18禁小説API(なろうディベロッパー)

これは以下のサイトに投稿された作品を対象とするAPIです。

ノクターンノベルズ(男性向け)
ムーンライトノベルズ(女性向け)
ムーンライトノベルズ(BL)
ミッドナイトノベルズ(大人向け)

『なろう18禁小説API』を使用することで、これらのサイトに投稿された全ての作品情報データを取得できます

なお『なろう小説API』の全データを取得するコードは以下の記事を御覧ください

karupoimou.hatenablog.com

全データ取得コード

Pythonを用いて「なろう18禁小説API」の全作品・全項目データを取得するコードです。
.pyはGithubにアップしていますので、そちらからダウンロードできます。
github.com

内容の説明および使い方

全データ取得コードの使用方法

基本は前回書いた「『なろう小説API』を用いて、なろうの『全作品情報データを一括取得する』Pythonスクリプト - なろう分析記録」と同じです。

pip

このコードでは以下のパッケージを使用しています。

pip install pandas
pip install tqdm
pip install requests

出力項目

「なろう18禁小説API」で取得可能な、以下の31項目を取得できます。

title
ncode
writer
story
nocgenre
gensaku
keyword
general_firstup
general_lastup
novel_type
end
general_all_no
length
time
isstop
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

※2019.8.20より追加
daily_point
weekly_point
monthly_point
quarter_point
yearly_point
impression_cnt

項目について詳しくはなろうディベロッパーのサイトをご覧ください。
https://dev.syosetu.com/xman/api/:なろう18禁小説API(なろうディベロッパー)

『なろう18禁API』全取得コード

#『なろう18禁API』を用いて、なろう18禁の『全作品情報データを一括取得する』Pythonスクリプト
#2019-09-20更新
import sys
import requests
import pandas as pd
import json
import time as tm
import datetime
import gzip
from tqdm import tqdm
import re

#出力ファイル名
filename ='Narou_18_ALL_OUTPUT_0920.xlsx'

#リクエストの秒数間隔(1以上を推奨)
interval=1

#取得するGETパラメータの「lastup」リスト
temp_lastup_list=[]   

#取得パラメータを指定
column_name_list =  ['title',
                       'ncode',
                       'writer',
                       'story',
                       'nocgenre',
                       'gensaku',
                       'keyword',
                       'general_firstup',
                       'general_lastup',
                       'novel_type',
                       'end',
                       'general_all_no',
                       'length','time',
                       'isstop',
                       'isbl',
                       'isgl',
                       'iszankoku',
                       'istensei',
                       'istenni',
                       'pc_or_k',
                       'global_point',
                       'daily_point',
                       'weekly_point',
                       'monthly_point',
                       'quarter_point',
                       'yearly_point',               
                       'fav_novel_cnt',
                       'impression_cnt',
                       'review_cnt',
                       'all_point',
                       'all_hyoka_cnt',
                       'sasie_cnt',
                       'kaiwaritu',
                       'novelupdated_at',
                       'updated_at',
                       'weekly_unique']

##### 以上設定、以下関数 ##############
    
#最初に処理される関数 全体の数をメモ
def start_process():
    payload = {'out': 'json','of':'n','lim':1}
    allnum = requests.get('https://api.syosetu.com/novel18api/api/', params=payload).text
    print('対象作品数  ',allnum);

#GETパラメータで取得する「lastup」リストを生成する
def generate_lastup_list():
    
    #現在日時の取得
    now = datetime.datetime.today()
    now_time=int(now.timestamp())
    
    #終点
    dd=datetime.datetime(2004, 1, 1, 1, 1, 1, 1)
    start_day=dd.timestamp()

    #作業経過一時保存用の変数
    unix_time = int(now.timestamp())
   
    #Unixtimeを使った期間指定で作品情報を取得する
    for i in range(100000):

        if start_day < unix_time:

            # 約日以内の投稿
            if now_time-300000 <  unix_time:
                next_time=int(unix_time-20000)
                
            # 約日以内の投稿
            elif now_time-3000000 <  unix_time <= now_time-300000:
                next_time=int(unix_time-100000)

            #だいぶ以前の投稿(エポック秒で直接指定していしてます)
            elif 1545000000 <  unix_time <= now_time-500000:
                next_time=int(unix_time-300000)

            elif 1400000000 < unix_time <= 1545000000:
                next_time=int(unix_time-1000000)
                
            elif 1334000000 < unix_time <= 1400000000:
                next_time=int(unix_time-2000000)
                
            elif 1170000000 < unix_time <= 1334000000:
                next_time=int(unix_time-5000000)
                
            elif unix_time <= 1170000000:
                next_time=int(unix_time-100000000)

            #リストに追加する
            lastup="%s-%s"%(next_time,unix_time)
            temp_lastup_list.append(lastup)
           
            #作業完了で次の取得期間を設定する
            unix_time=next_time

#全作品情報の取得
def get_all_novel_info():
    
    #レスポンスの一時リスト
    res_list=[]
    
    #リクエストエラー回数のメモ用
    error_cnt = 0 
    
    #リストを逆順にし、過去から現在に向かって取得していく        
    temp_lastup_list.reverse()
    
    #APIへリクエスト
    for lastup in tqdm(temp_lastup_list):
        payload = {'out': 'json','gzip':5,'opt':'weekly','lim':500,'lastup':lastup} 
        res = requests.get('https://api.syosetu.com/novel18api/api/', params=payload, timeout=10).content
        r =  gzip.decompress(res).decode("utf-8")
        
        #レスポンスを一旦リストに収納する
        res_list.append(r)
        
        #取得間隔を空ける
        tm.sleep(interval)
 
    #リクエストの展開
    dump_to_list(res_list)
        
#書き込み処理の関数
def dump_to_list(res_list):
    
    #各情報を一時的に保存していくための配列の準備
    temp_data_list=[]
    
    for i in range(len(column_name_list)):
        temp_data_list.append([])
    
    #レスポンスリストを展開
    for r in res_list:
    
        for data in json.loads(r):
            try:
                for i in range(len(column_name_list)):               
                    temp_data_list[i].append(data[column_name_list[i]])
            except KeyError:
                pass
    
        #取れていない小説が無いか確認
        for data in json.loads(r):
            try:
                 if 500 <= data["allcount"]:
                    print("取得できなかった作品が存在します。generate_lastup_listの取得間隔を変更してください")
            except KeyError:
                pass            
     
    #エクセルファイルに書き込む処理へ
    dump_to_excel(temp_data_list)
        
#エクセルファイルに書き込む処理
def dump_to_excel(temp_data_list):
    
    exportlist=[]
    
    #各項目のリストを1つにまとめる
    for i in range(len(column_name_list)):
        exportlist.append(temp_data_list[i])

    #pandasのデータフレームに収納 
    df = pd.DataFrame(exportlist, index=column_name_list)   
    df= df.T

    #IllegalCharacterErrorの予防
    df = df.applymap(illegal_char_remover)

    # .xlsx ファイル出力
    writer = pd.ExcelWriter(filename,options={'strings_to_urls': False})
    df.to_excel(writer, sheet_name="Sheet1")#Writerを通して書き込み
    writer.close() 

# IllegalCharacterErrorの予防、無効文字の除去
def illegal_char_remover(data):
    ILLEGAL_CHARACTERS_RE = re.compile(
        r'[\000-\010]|[\013-\014]|[\016-\037]|[\x00-\x1f\x7f-\x9f]|[\uffff]')
    """Remove ILLEGAL CHARACTER."""
    if isinstance(data, str):
        return ILLEGAL_CHARACTERS_RE.sub("", data)
    else:
        return data

####### 関数の実行を指定 ##########
print("start",datetime.datetime.now())

start_process()

generate_lastup_list()
get_all_novel_info()

print("end",datetime.datetime.now())

上記のコードを使用すればおよそ8分ぐらいで全取得できます。

「なろう小説API」から全作品・全項目のデータを一括取得するコード

「なろう小説API」については前回の記事を御覧ください。

karupoimou.hatenablog.com

備考

小説家になろう」は株式会社ヒナプロジェクトの登録商標です。
このスクリプトは非公式のものです。