SharePoint REST APIを使ってみた

SharePoint OnlineでサイトにアップロードしているExcelファイルを毎日自動でダウンロードしたいという要望をもらったので,
公開されているAPIを使ってやってみた.

環境

  • OS X El Capitan v10.11.6
  • Python 3.5.2
  • Office 365 Enterprise E3

Office 365 APIを使う

認証情報を取得する

Office 365 APIを使用するには,まずアクセストークンと呼ばれる認証情報を取得する必要がある.
次の手順でアクセストークンを取得する.

なお,今回はOffice 365 Enterprise E3を使用し,すでにSharePointでサイトを作成していることとする.

  1. 新Azure PortalにOffice 365の管理者アカウントでログインする.
  2. 左側のメニューから「Azure Active Direcotory」を選択する.
  3. メニューから「アプリの登録」を選択する.
  4. ページ上部にある「追加」を選択して新規アプリケーションを作成する.
    • 名前: 好きなものを
    • アプリケーションの種類: Webアプリ/API
    • サインオンURL: 作成したアプリケーションを動かすURL
  5. アプリケーションの作成後,作成したアプリケーションを選択して,以下の情報をメモしておく.
    • アプリケーションID
    • ホームページ(サインオンURLで指定したものになっているはず)
  6. 右側の設定メニューから「キー」を選択して新しくキーを作成する.
    キーの説明を入力して期間をドロップダウンリストから選択し,保存をクリックするとキーが生成される.
    この時キーを 必ず メモしておくこと(この画面から移動した後はキーを再確認できない).
  7. 設定メニューから「必要なアクセス許可」を選択してアプリケーションに対して必要な権限を付与する.
    今回は,SharePointの機能を使いたいので,上部にある「追加」をクリック後,表示されるサービス名の中から
    「Office 365 SharePoint Online」を選択し,必要な権限を追加する.
  8. アクセストークンを生成するために必要な code を取得する.次のURLにGETでリクエストを送信する.
    • URL: https://login.windows.net/common/oauth2/authorize?response_type=code&client_id=<client_id>&resource=<resource>&redirect_uri=<redirect_uri>
    • パラメータ
      • client_id : アプリケーションID
      • resource : https://<テナント名>.sharepoint.com/
        (例: テナント名が testtenanthttps://testtenant.sharepoint.com/
      • redirect_uri : サインオンURL
  9. 正しくリクエストを送れていればログイン画面が表示され,ログインが成功すると redirect_uri で指定したURIにリダイレクトされ,パラメータに code がセットされる.
  10. 取得した code を用いてアクセストークンを取得する.次のURLにPOSTでリクエストを送信する.
    • URL: https://login.windows.net/common/oauth2/token
    • HTTPヘッダに追加: Content-Type: application/x-www-form-urlencoded
    • リクエストボディ: grant_type=authorization_code&code=<code>&client_id=<client_id>&client_secret=<client_secret>&redirect_uri=<redirect_uri>
    • パラメータ
      • code : 先程取得したもの
      • client_secret : アプリケーション作成時に生成したキー
  11. 正しくリクエストを送れていれば, access_token を含むJSONが返却される.

APIを叩く

上記の手順で取得したアクセストークンを使用して実際にAPIを使用した.
APIを叩くときには,HTTPヘッダに取得したアクセストークンを以下のようなフォーマットで加える必要がある.

Authorization: Bearer <access_token>

今回はSharePointのサイト上にアップロードしているExcelファイルをダウンロードするのが目的なので,
それっぽいAPIをリファレンスから探し出して叩いてみた.

SharePoint 2013 REST API リファレンスによれば,
Fileというリソースがサイト内のファイルを表していて,そのファイルを取得するには次のようなURIを指定すれば良い.

http://<site_url>/_api/web/getfilebyserverrelativeurl('/<folder>/<file>')

また,ファイル自体をダウンロードするためには, $value というODataのクエリオプションを付加する.

というわけで,Pythonで簡単にAPIを叩く.なお,以下のプログラムではRequestsという外部ライブラリを使用しているので注意.

import requests
import urllib.parse

def main():
    # ファイル自体を取得するため,$valueを付加
    uri = "https://<tenant>.sharepoint.com/_api/web/getfilebyserverrelativeurl('<file-path>')/$value"
    access_token = sys.argv[1]
    headers = {
        'accept': 'application/json;odata=verbose',
        'Content-Type': 'application/json;odata=verbose',
        'Authorization': 'Bearer ' + access_token,
    }
    res = requests.get(urllib.parse.quote(uri, safe=':/'), headers=headers, stream=True)
    with open('file.xlsx', 'wb') as f:
        f.write(res.raw.read())


if __name__ == '__main__':
    main()

これを実行すると,URIに指定したファイルが file.xlsx という名前でダウンロードされる.

まだ未完成なところ

  • 現時点では code を取得する際にブラウザを使う必要がある
    • Requestsモジュールの Session を使えばできるようなので後で修正する

参考にしたところ

PythonからMySQLを使う

DjangoでデータベースにMySQLを使用するときはmysqlclientを使用することが推奨されている.Djangoが勝手にデータベースに接続などの処理をしてくれるのでモジュールの使い方は知らなくても使うことは可能だが,せっかくなら使い方もわかるほうが良いので調べてみた.

基本的に,Python標準ライブラリのsqlite3と使い方は同じ.
まずコネクションオブジェクトを作成し,そこからカーソルオブジェクトを作る.できたカーソルオブジェクトを使って様々なクエリを実行する.

インストール

$ pip install mysqlclient

MySQLに接続する

conn = MySQLdb.connect(
    user='username',
    passwd='password',
    host='host',
    db='dbname'
)

返り値はコネクションオブジェクト.userpasswd は名前の通り.MySQLに登録されているユーザー情報を記述する. host はデータベースの置いてある場所を指定する.ローカルのMySQLに接続する場合は localhost を指定する. db には使用するデータベース名を指定する.

カーソルオブジェクトの作成

c = conn.cursor()

MySQLdb.connect で作成したオブジェクトを使ってカーソルオブジェクトを作成する.

クエリの実行

c.execute(query)

query に指定したクエリを実行する.

プレースホルダ

クエリ中に %s を記述すると,プレースホルダとして扱える.ここに値を埋め込む場合は,与えたい値を execute() の第2引数にタプルで渡す.

c.execute('select * from test where id = %s', (2,))

レコードの取得

execute() でselect文を実行した後,レコードを得るためには以下のいずれかを使用する.

  • fetchone() : レコードを1件取得
  • fetchmany(n) : レコードをn件取得
  • fetchall() : レコードをすべて取得

データベースへの変更を保存

conn.commit()

このメソッドを呼び出すことで,変更を保存できる. これを呼び出し忘れると,追加・削除などの変更が破棄される ので注意.

このメソッドはカーソルオブジェクトではなく,コネクションオブジェクトが持っていることにも注意.

サンプルコード

# coding: utf-8

import MySQLdb


def main():
    conn = MySQLdb.connect(
        user='testuser',
        passwd='testuser',
        host='192.168.33.3',
        db='testdb'
    )
    c = conn.cursor()

    # テーブルの作成
    sql = 'create table test (id int, content varchar(32))'
    c.execute(sql)
    print('* testテーブルを作成\n')

    # テーブル一覧の取得
    sql = 'show tables'
    c.execute(sql)
    print('===== テーブル一覧 =====')
    print(c.fetchone())

    # レコードの登録
    sql = 'insert into test values (%s, %s)'
    c.execute(sql, (1, 'hoge'))  # 1件のみ
    datas = [
        (2, 'foo'),
        (3, 'bar')
    ]
    c.executemany(sql, datas)    # 複数件
    print('\n* レコードを3件登録\n')

    # レコードの取得
    sql = 'select * from test'
    c.execute(sql)
    print('===== レコード =====')
    for row in c.fetchall():
        print('Id:', row[0], 'Content:', row[1])
    
    # レコードの削除
    sql = 'delete from test where id=%s'
    c.execute(sql, (2,))
    print('\n* idが2のレコードを削除\n')

    # レコードの取得
    sql = 'select * from test'
    c.execute(sql)
    print('===== レコード =====')
    for row in c.fetchall():
        print('Id:', row[0], 'Content:', row[1])

    # データベースへの変更を保存
    conn.commit()
    
    c.close()
    conn.close()


if __name__ == '__main__':
    main()

実行結果

* testテーブルを作成

===== テーブル一覧 =====
('test',)

* レコードを3件登録

===== レコード =====
Id: 1 Content: hoge
Id: 2 Content: foo
Id: 3 Content: bar

* idが2のレコードを削除

===== レコード =====
Id: 1 Content: hoge
Id: 3 Content: bar

Python3にOpenCVをインストールした

環境はOS X Yosemite 10.10.5.

Python3はHomebrewからインストールしたものを使用した.

Homebrewが入っていない場合はHomebrew公式の「インストール」の部分に書いてあるコマンドを実行してインストールしておく.

HomebrewからOpenCVをインストール

下記コマンドを実行してOpenCVをインストールする.

$ brew tap homebrew/science
$ brew install opencv3 --with-python3

インストール後,Homebrewの指示に従ってパスを通す.(<username>の箇所は自分のユーザー名に置き換える)

echo /usr/local/opt/opencv3/lib/python2.7/site-packages >> /usr/local/lib/python2.7/site-packages/opencv3.pth
mkdir -p /Users/<username>/.local/lib/python3.5/site-packages
echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> /Users/<username>/.local/lib/python3.5/site-packages/homebrew.pth

ここまで完了したら,下記コマンドで確認.バージョンが出ればインストール完了.

$ python -c 'import cv2; print(cv2.__version__)'
3.1.0

※Homebrewでは上記のように出てきたが,自分の環境では実際に実行してもOpenCVのパスは通らなかった.上のコマンドの代わりに下記のように実行するとうまく行った.

echo /usr/local/opt/opencv3/lib/python3.5/site-packages >> /usr/local/lib/python3.5/site-packages/opencv3.pth
mkdir -p /Users/<username>/.local/lib/python3.5/site-packages
echo 'import site; site.addsitedir("/usr/local/lib/python3.5/site-packages")' >> /Users/<username>/.local/lib/python3.5/site-packages/homebrew.pth

Base64のデコード・エンコード

Webの世界などで使用されているBase64のデコード・エンコードについて.
CTF (Capture The Flag) でも使用されることがあるので,軽くまとめてみた.

UNIXコマンド

エンコード

Base64コマンドを使用する.

$ echo 'base64 encode' | base64
YmFzZTY0IGVuY29kZQo=

-iオプションを使用すると,ファイルから文字列を読み込んでエンコードする.
-oオプションを使用すると,結果をファイルに書き込む.

$ base64 -i input.txt -o output.txt

input.txtの中身

base64 encode

output.txtの中身

YmFzZTY0IGVuY29kZQo=

デコード

-Dオプションをつける.

$ echo 'YmFzZTY0IGVuY29kZQo=' | base64 -D
base64 encode

エンコードと同じく,-iオプションと-oオプションは有効.

Python

標準ライブラリであるbase64モジュールを使用する.

エンコード

base64.b64encode()メソッドを使う.

>>> import base64
>>> s = 'base64 encode'
>>> base64.b64encode(s.encode('utf-8'))
b'YmFzZTY0IGVuY29kZQ=='

デコード

base64.b64decode()メソッドを使う.

>>> import base64
>>> encoded = b'YmFzZTY0IGVuY29kZQ=='
>>> base64.b64decode(encoded)
b'base64 encode'

どちらのメソッドも引数,返り値ともにバイトオブジェクトということに注意.

Pythonでsqlite

Pythonでデータベースを使ってみたかったので,標準ライブラリに含まれているsqlite3モジュールを触ってみた.

サンプルコードはこちら.
GitHubにもあげました.

# -*- coding: utf-8 -*-

import sqlite3


dbname = 'database.db'
conn = sqlite3.connect(dbname)

c = conn.cursor()
# executeメソッドでSQL文を実行する
create_table = '''create table users (id int, name varchar(64),
                  age int, gender varchar(32))'''
c.execute(create_table)

# SQL文に値をセットする場合は,Pythonのformatメソッドなどは使わずに,
# セットしたい場所に?を記述し,executeメソッドの第2引数に?に当てはめる値を
# タプルで渡す.
sql = 'insert into users (id, name, age, gender) values (?,?,?,?)'
user = (1, 'Taro', 20, 'male')
c.execute(sql, user)

# 一度に複数のSQL文を実行したいときは,タプルのリストを作成した上で
# executemanyメソッドを実行する
insert_sql = 'insert into users (id, name, age, gender) values (?,?,?,?)'
users = [
    (2, 'Shota', 54, 'male'),
    (3, 'Nana', 40, 'female'),
    (4, 'Tooru', 78, 'male'),
    (5, 'Saki', 31, 'female')
]
c.executemany(insert_sql, users)
conn.commit()

select_sql = 'select * from users'
for row in c.execute(select_sql):
    print(row)

conn.close()

以下,サンプルコードの解説を少し.

データベースへ接続

データベースに接続するには,sqlite3.connect()メソッドを使用する.

conn = sqlite3.connect(dbname)

sqlite3.connect()メソッドではConnectionオブジェクトが作成される.
SQL文を実行するには,ConnectionオブジェクトからさらにCursorオブジェクトを作成する必要がある.

c = conn.cursor()

このCursorオブジェクトを使用することでデータベースに対して様々なコマンドを実行することができる.

SQLの実行

SQL文を実行するには,Cursorオブジェクトのexecute()メソッドを使用する.

c.execute(sql[, parameters])

第1引数のSQL文に?を埋め込んだ場合は,第2引数で?にセットしたい値をタプルで渡す.

Example

user = (1, 'Taro', 20, 'male')
c.execute('insert into users (id, name, age, gender) values (?,?,?,?)', user)

こうすることで,SQLの?部分にタプル内の値がそれぞれ当てはめられ,最終的に以下のようなSQL文が実行される.

insert into users (id, name, age, gender) values (1, 'Taro', 20, 'male')

なお,一度に複数のSQLを実行したい場合は,executemany()メソッドを使用し,第2引数にタプルのリストを渡す(サンプルコード参照).

変更をデータベースに保存

execute()メソッドやexecutemany()メソッドでデータベースに追加・削除などを行った後には,必ずcommit()メソッドを呼び出す.このメソッドを呼び出さずにデータベースを閉じてしまうと,変更が保存されない.

conn.commit()

データベースを閉じる

プログラムの最後には忘れずにデータベースコネクションを閉じます.これにはclose()メソッドを使用する.このメソッドは自動的にcommit()を呼び出さないことに注意.

conn.close()