<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 0. Web APIを用いた目次情報の取得
文化庁メディア芸術データベース　マンガ分野　WebAPIを用いて，分析に必要なデータを入手します．なお，python3を使ったweb APIの利用については，Python3でjsonを返却するwebAPIにアクセスして結果を出力するまでを参考にさせて頂きました．
環境構築
bash
conda env create -f env.yml
準備
Step2:  雑誌巻号検索結果の取得
以下の関数search_magazine()を使って，週刊少年ジャンプの雑誌巻号情報を検索し，結果を変数magazinesに保存します．ここで取得したユニークIDは，次節の「雑誌巻号情報の取得」に必要になります．
Step3:  Web APIでは，パラメータidで「ユニークID」「雑誌巻号ID」あるいは「雑誌コード」を，pageで検索結果の取得ページ番号（1ページあたり100件，デフォルトは1）を指定することができます．ここで，週刊少年ジャンプは「雑誌巻号ID」にJUMPrglを含むため，id=JUMPrglを指定します．また，週刊少年ジャンプの検索結果は合計24ページ（2320件）あるため，pageに1から24を順次指定する必要があります．
詳細はWebAPI仕様をご参照ください．
なお，2017年3月31日より文化庁メディア芸術データベースのURLが変更されたため，WebAPI仕様に記載のリクエストURL（https
Step5:  雑誌巻号情報の取得
以下の関数extract_data()で必要な目次情報を抽出し，save_data()で目次情報を保存します．
Step7:  泥臭い話ですが，一部のギャグ漫画の扱いに苦労しました．例えば，「いぬまるだしっ」は，基本的に一週間に2話ずつ掲載していましたが，データベースでは各話が別々の行に記載されています．これらを１つの作品として見なす必要があるので，当該comicのtitleがdata中にある場合は，別datumとしてdataに追加せず，既存のdatumのpagesを加算する処理を行っています．また，例えば「ピューと吹く!ジャガー」は，その人気に関係なく（実際めちゃくちゃ面白かったです），連載中は常に雑誌の最後に掲載されていました．これを外れ値として除外するかどうかで悩みましたが，結局残すことにしました．
Step8:  タイムアウトに柔軟に対応するため，目次情報を一括処理せず，無理やり逐次処理にしています．また，サーバに負荷をかけないよう，sleep()で一時停止していることにご注意ください．
なお，2017年3月31日より文化庁メディア芸術データベースのURLが変更されたため，WebAPI仕様に記載のリクエストURL（https
Step9:  タイムアウトした場合は，offsetを利用して再開します．例えば，447/2320でタイムアウトした場合は，save_data(offset=448)を実行します．
<ASSISTANT_TASK:>
Python Code:
import json
import urllib.request
from time import sleep
Explanation: 0. Web APIを用いた目次情報の取得
文化庁メディア芸術データベース　マンガ分野　WebAPIを用いて，分析に必要なデータを入手します．なお，python3を使ったweb APIの利用については，Python3でjsonを返却するwebAPIにアクセスして結果を出力するまでを参考にさせて頂きました．
環境構築
bash
conda env create -f env.yml
準備
End of explanation
def search_magazine(key='JUMPrgl', n_pages=25):
    
    「ユニークID」「雑誌巻号ID」あるいは「雑誌コード」にkey含む雑誌を，
    n_pages分取得する関数です．
    
    
    url = 'https://mediaarts-db.bunka.go.jp/mg/api/v1/results_magazines?id=' + \
        key + '&page='
    magazines = []
    
    for i in range(1, n_pages):
        response = urllib.request.urlopen(url + str(i))
        content = json.loads(response.read().decode('utf8'))
        magazines.extend(content['results'])
    return magazines
Explanation: 雑誌巻号検索結果の取得
以下の関数search_magazine()を使って，週刊少年ジャンプの雑誌巻号情報を検索し，結果を変数magazinesに保存します．ここで取得したユニークIDは，次節の「雑誌巻号情報の取得」に必要になります．
End of explanation
magazines = search_magazine()
len(magazines)
magazines[0]
magazines[-1]
Explanation: Web APIでは，パラメータidで「ユニークID」「雑誌巻号ID」あるいは「雑誌コード」を，pageで検索結果の取得ページ番号（1ページあたり100件，デフォルトは1）を指定することができます．ここで，週刊少年ジャンプは「雑誌巻号ID」にJUMPrglを含むため，id=JUMPrglを指定します．また，週刊少年ジャンプの検索結果は合計24ページ（2320件）あるため，pageに1から24を順次指定する必要があります．
詳細はWebAPI仕様をご参照ください．
なお，2017年3月31日より文化庁メディア芸術データベースのURLが変更されたため，WebAPI仕様に記載のリクエストURL（https://mediaarts-db.jp/mg/api/v1/results_magazines）ではなく，新しいURL（https://mediaarts-db.bunka.go.jp/mg/api/v1/results_magazines）を使用する必要があることにご注意ください．
End of explanation
def extract_data(content):
    
    contentに含まれる目次情報を取得する関数です．
    - year: 発行年
    - no: 号数
    - title: 作品名
    - author: 著者
    - color: カラーか否か
    - pages: 掲載ページ数
    - start_page: 作品のスタートページ
    - best: 巻頭から数えた掲載順
    - worst: 巻末から数えた掲載順
    
    
    # マンガ作品のみ抽出します．
    comics = [comic for comic in content['contents'] 
             if comic['category']=='マンガ作品'] 
    data = []
    year = int(content['basics']['date_indication'][:4])
    
    # 号数が記載されていない場合があるので，例外処理が必要です．
    try:
        no = int(content['basics']['number_indication'])
    except ValueError:
        no = content['basics']['number_indication']
    
    for comic in comics:
        title= comic['work']
        if not title:
            continue
            
        # ページ数が記載されていない作品があるので，例外処理が必要です．
        # 特に理由はないですが，無記載の作品は10ページとして処理を進めます．
        try:
            pages = int(comic['work_pages'])
        except ValueError:
            pages = 10
        # 「いぬまるだしっ」等，1週に複数話掲載されている作品に対応するため
        # data中にすでにtitleが含まれる場合は，新規datumとして登録せずに，
        # 既存のdatumのページ数のみ加算します．
        if len(data) > 0 and title in [datum['title'] for datum in data]:
            data[[datum['title'] for datum in 
                  data].index(title)]['pages'] += pages
        else:
            data.append({
                'year': year,
                'no': no,
                'title': comic['work'],
                'author': comic['author'],
                'subtitle': comic['subtitle'],
                'color': comic['note'].count('カラー'),
                'pages': int(comic['work_pages']),
                'start_pages': int(comic['start_page'])
            })
    # 企画物のミニマンガを除外するため，合計5ページ以下のdatumはリストから除外します．
    filterd_data = [datum for datum in data if datum['pages'] > 5]
    for n, datum in enumerate(filterd_data):
        datum['best'] = n + 1
        datum['worst'] = len(filterd_data) - n
        
    return filterd_data
Explanation: 雑誌巻号情報の取得
以下の関数extract_data()で必要な目次情報を抽出し，save_data()で目次情報を保存します．
End of explanation
def save_data(magazines, offset=0, file_name='data/wj-api.json'):
    
    magazinesに含まれる全てのmagazineについて，先頭からoffset以降の巻号の
    目次情報を取得し，file_nameに保存する関数です．
    
    
    url = 'https://mediaarts-db.bunka.go.jp/mg/api/v1/magazine?id='
    
    #　ファイル先頭行
    if offset == 0:
        with open(file_name, 'w') as f:
            f.write('[\n')
        
    with open(file_name, 'a') as f:
        
        # magazines中のmagazine毎にWeb APIを叩きます．
        for m, magazine in enumerate(magazines[offset:]):
            response = urllib.request.urlopen(url + str(magazine['id']),
                                              timeout=30)
            content = json.loads(response.read().decode('utf8'))
            
            # 前記の関数extract_data()で，必要な情報を抽出します．
            comics = extract_data(content)
            print('{0:4d}/{1}: Extracted data from {2}'.\
                  format(m + offset, len(magazines), url + str(magazine['id'])))
            
            # comics中の各comicについて，file_nameに情報を保存します．
            for n, comic in enumerate(comics):
                
                # ファイル先頭以外の，magazineの最初のcomicの場合は，
                # まず',\n'を追記．
                if m + offset > 0 and n == 0:
                    f.write(',\n')
                
                json.dump(comic, f, ensure_ascii=False)
                
                # 最後のcomic以外は',\n'を追記．
                if not n == len(comics) - 1:
                    f.write(',\n')
            print('{0:9}: Saved data to {1}'.format(' ', file_name))
            
            # サーバへの負荷を抑えるため，必ず一時停止します．
            sleep(3)
            
    # ファイル最終行
    with open(file_name, 'a') as f:
        f.write(']')
Explanation: 泥臭い話ですが，一部のギャグ漫画の扱いに苦労しました．例えば，「いぬまるだしっ」は，基本的に一週間に2話ずつ掲載していましたが，データベースでは各話が別々の行に記載されています．これらを１つの作品として見なす必要があるので，当該comicのtitleがdata中にある場合は，別datumとしてdataに追加せず，既存のdatumのpagesを加算する処理を行っています．また，例えば「ピューと吹く!ジャガー」は，その人気に関係なく（実際めちゃくちゃ面白かったです），連載中は常に雑誌の最後に掲載されていました．これを外れ値として除外するかどうかで悩みましたが，結局残すことにしました．
End of explanation
save_data(magazines)
Explanation: タイムアウトに柔軟に対応するため，目次情報を一括処理せず，無理やり逐次処理にしています．また，サーバに負荷をかけないよう，sleep()で一時停止していることにご注意ください．
なお，2017年3月31日より文化庁メディア芸術データベースのURLが変更されたため，WebAPI仕様に記載のリクエストURL（https://mediaarts-db.jp/mg/api/v1/magazine）ではなく，新しいURL（https://mediaarts-db.bunka.go.jp/mg/api/v1/magazine）を使用する必要があることにご注意ください．
以下を実行し，wj-api.jsonにデータを保存します．
End of explanation
save_data(magazines, offset=448)
save_data(magazines, offset=500)
save_data(magazines, offset=1269)
save_data(magazines, offset=1889)
save_data(magazines, offset=2274)
Explanation: タイムアウトした場合は，offsetを利用して再開します．例えば，447/2320でタイムアウトした場合は，save_data(offset=448)を実行します．
End of explanation
<END_TASK>