Watson Studio (Notebook)からICOSにアクセスする

# はじめに

Watson StudioのNotebookから、ICOSにあるオブジェクトを使って色々なことをする機会があったので、こちらに記録として残しておこうと思います。

SQL Queryは使わずに、直接ICOSにアクセスする方法になります。SQL Queryを使うと、もっと色々なことが出来るのですが、それはまた別の機会に記載出来ればと。

# 事前準備

## ライト・アカウント

本記事の内容は、IBM Cloud ライト・アカウントで実施可能です。アカウントをお持ちで無い方は、以下のURL先から登録ください。

https://cloud.ibm.com/registration?locale=ja

## Watson Studio

Liteプランをオーダーしておきます。ちなみに、以前のLiteプランには、無料でいくらでも使える最小構成のランタイムがありましたが、2020年6月現在は使えませんのでお気をつけください。さらに、2022年5月以降、50CUH(Capacity Unit Hours)あった無料枠が、10CUHに削減されてしまいました。

また、2022年4月1日にWatson Studioのプランが変更され、それまであったStandardとEnterpriseの有償プランが、Professionalプランに統合されています。以前の有償プランでは、一定数を超えた登録ユーザーあたりにも課金されていましたが、CUHのみの課金に変更されています。

2022年4月以前に作成済のStandardとEnterpriseプランは継続して利用可能のようですが、現状の使いっぷりとユーザー数を踏まえて、Professionalプランの方がお得な場合は、乗り換えた方がよいかもしれません。

https://dataplatform.cloud.ibm.com/docs/content/wsj/getting-started/ws-plans.html?audience=wdp&context=wdp

## ICOS (IBM Cloud Object Storage)

こちらもライトプランをオーダーし、適当な名前でバケットを作成しておきます。バケットはRegionalで、ロケーションはjp-tokとします。

## テスト用ファイルの準備

以下のようなコマンドを実行して、テスト用のファイルを1500個ほど作成し、上で作成したバケットに保管しておきます。

```
for i in $(seq -f %04g 1 1500); do echo ${i} > data-${i}.txt; done
```

## APIキーの作成

以下のURLから、任意の名前で自身のアカウントのAPIキーを作成して、そのキーの文字列を控えておきます。作成直後しか見ることが出来ないので、ご注意ください。

https://cloud.ibm.com/iam/apikeys

# Watson Studioでの作業

## プロジェクトの作成


まず新規のプロジェクトを作成します。トップ画面の左上にある 「Create a project」 をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307720/bdfe5dde-0b6a-d8f0-eddd-e77d243688ab.png)

次に、「Create an empty project」 をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307720/2c863e2b-7246-c153-aab8-3d5e5aefe864.png)

最後に適当なプロジェクト名を入力し、「Create」をクリックしてプロジェクトを作成します。このとき、右側の「Storage」というところに、事前に作成しておいたICOSインスタンスが表示されています。複数のICOSインスタンスをお持ちの場合は、任意のものをここで選択します。ここで選んだICOSインスタンスには、このプロジェクトの設定内容等を保管するためのバケットが自動的に作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307720/7738f41b-7c5f-bbae-ce0e-ef8986aea9d3.png)

## 最小構成のランタイム（Environment)の作成

2022年5月30日時点で利用可能なランタイムのPythonは3.9で、最小構成(1vCPU)のランタイムがデフォルトで用意されているので、そちらををご利用ください。以下は、1vCPUのランタイムがデフォルトで存在しなかった場合に、カスタムのランタイムを作成する手順です。

作成したプロジェクトのダッシュボードのManageタブページ内で、メニューからEnvironmentをクリック。そこにあるTempatesタブページ内で、「New template」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307720/3cb32c9f-e6c7-1208-b4f0-b024283cd504.png)


適当な名前を入力し、「Hardware configuration」で一番スペックの低い「1vCPU and 4GB RAM」を選択します。「Software version」は最新のPython、2022年5月時点では「IBM Runtime 22.1 on Python 3.9」を選択し、「Create」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307720/dcf675c5-de1c-50b8-1f92-9187502746fc.png)

## Notebookの作成

プロジェクトのダッシュボードのAssetsタブページ内にある、「New asset」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307720/165093c8-fc05-a749-8d98-fd453d594a0e.png)

Code editorのページにある「Jupyter notebook editor」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307720/90ccec28-2cea-bc94-77fa-04780c4e5f1a.png)


適当なNotebook名を入力し、Select runtimeのところでは、最小構成のランタイムを選択してから、「Create」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307720/3eb31147-0fbd-c937-0036-d02ea37b62f2.png)

ランタイムが起動した後、Notebookの編集画面に入ります。なお、作成済みのNotebookは、プロジェクトダッシュボードのAssetsタブページからアクセス可能です。

## Notebookの記述

### 事前処理

1つ目のセルで、必要なモジュールのimportなどを実施しておきます。

```python
from datetime import datetime, date, timedelta
import pytz
import json
import sys

JST = pytz.timezone('Asia/Tokyo')
```

次に、AWS SDK for Python (Boto3)のICOS版？、ibm_boto3の設定を行います。`ibm_api_key_id`のところは、先に作成したAPIキーに置き換えてください。

```python
from botocore.client import Config
import ibm_boto3

cosclient = ibm_boto3.client(service_name='s3',
    ibm_api_key_id='********************************',
    ibm_auth_endpoint="https://iam.cloud.ibm.com/oidc/token",
    config=Config(signature_version='oauth'),
    endpoint_url='https://s3.private.jp-tok.cloud-object-storage.appdomain.cloud')
```

ibm_boto3を使って、本記事以外でどんなことが出来るかは、こちらをご参照ください。

https://ibm.github.io/ibm-cos-sdk-python/reference/services/index.html

### オブジェクト一覧の取得

次のセルで、ICOSバケット上のオブジェクトの情報を取得します。list_objectsメソッドを使用しますが、その際、バケット名、プレフィックスを指定します。`bucket`には、事前に作成しておいたバケット名を指定します。`prefix`は、今回の場合は、事前に作成したテスト用ファイルを示す`data-`とします。

本家boto3でも同じなのですが、`list_objects`メソッドは、最大1000件しか返すことが出来ません。ただ、1000件以上ある場合は、`IsTruncated`フィールドが`True`で返され、`NextMarker`フィールドに、次にここから読み込んで欲しいという情報があるので、これを`marker`にセットして、次の1000件を読み込む、というのを繰り返します。

try 〜 exceptを使っているのは、オブジェクトやバケットが存在しなかったり、アクセスポリシーでブロックされた場合の例外を処理するためです。

```python
bucket = 'icos-test-bucket'    # 事前に作成したバケット名
prefix = 'data-'               # オブジェクトを入手する際のプレフィックス
marker = ''                    # オブジェクトを入手する際の1つ目のオブジェクトを示すパス。初回はブランク
contents = []                  # オブジェクトの情報を格納するための変数

try:
    while True:
        response = cosclient.list_objects(Bucket=bucket, Prefix=prefix, Marker=marker)
        contents.extend(response['Contents'])
        if response['IsTruncated'] == True:  # 1000件超える場合
            marker = response['NextMarker']
        else:
            break
except Exception as e:
    if str(e) == '\'Contents\'':
        print('オブジェクトが見つかりませんでした')
    else:
        print('以下のエラーでICOSにアクセス出来ませんでした。')
        print(str(e))
    sys.exit(1)
```

### オブジェクト属性情報の取得

前のセルで取得した情報は、JSON形式のリストになっており、個々のオブジェクトのパス(Key)、サイズ(Size)、最終更新日時(LastModified)などの情報が入っています。これをcsv形式で取得してみます。最終更新日時は、日本時間で取得するようにしています。

```python
allfilelist = 'Key,Size,LastModified\n'
for item in contents:
    allfilelist = allfilelist + item['Key'] + ',' + str(item['Size']) + ',' + datetime.strftime(item['LastModified'].astimezone(JST), '%Y/%m/%d %H:%M:%S') + '\n'
    
print(allfilelist)
```

こんな結果が表示されるはずです。

```
Key,Size,LastModified
data-0001.txt,5,2020/06/03 17:47:02
data-0002.txt,5,2020/06/03 17:47:02
data-0003.txt,5,2020/06/03 17:47:02
data-0004.txt,5,2020/06/03 17:47:02
data-0005.txt,5,2020/06/03 17:47:02
・・・略・・・
```

### オブジェクトの中身の取得

get_objectメソッドを使って、オブジェクトの中身を読み取り、1つの文字列変数に繋げて保管してみます。テキストファイルの文字コードに応じて、`.decode('Shift_JIS')`などと付与することで、日本語も問題なく処理できます。

```python
data = ""
for item in contents:
    body = cosclient.get_object(Bucket=bucket,Key=item['Key'])['Body'].read().decode('Shift_JIS')
    data = data + body
```

### バケットへのファイル出力

最後に、put_objectメソッドを使って、収集した情報をバケットにファイルとして出力します。

```python
cosclient.put_object(Bucket=bucket,Key='data.txt', Body=data)
cosclient.put_object(Bucket=bucket,Key='allfilelist.txt', Body=allfilelist)
```

# まとめ

Watson Studio の Notebook を使って、ICOS上のオブジェクトに対する基本的な操作を試してみました。