OneNote操作API接口

前言

OneNote是很好用的资料整理、撰写文章笔记的工具。

有很多时候,从晚上看到的比较好的资料都可以随手整理进去。

不过,全部手动是比较麻烦的,诸如知乎收藏夹之类的文章,平时点击了收藏后,如果不及时保存,有时候会被编辑掉,好文章变成无法访问,还是很郁闷的。

好在微软提供了office的开发接口,其中包含了OneNote。通过这些接口,可以将数据通过程序提交到Onenote中,增加分类、增删文章等等。

在阅读前,请熟读微软MSDN中的相关章节:

本文只是对以上文档的简单归纳汇总。

正文

1. 注册微软服务

​ 需要在自己的微软开发者中心账户中开通服务。

Microsoft account Developer Center

​ 由于OneNote使用的是类似OAuth的鉴权方案。需要提交自己的转向域名地址。

​ 点击新增,然后把oauth相关的地址配置好。如果没有域名,就把redirect url改为localhost:9000 。

​ 记住识别码和安全码。API中的ClientID和SecretID就是这两个。

2. 鉴权方式的选择

​ 微软提供了两种OAuth的健全方法。一种是code flow;一种是token flow。理论上,code flow在使用上比较方便。用户通过一次鉴权后,就无需再次登陆;而token flow的安全性比较高,鉴权是一次性并且有时效的。

​ 根据自己程序的性质加以选择。由于是自己开发,所以这里会选择code flow:登陆后,记录refreash token,然后下次就无需再次登陆了。

3. 流程

  1. 在程序应该会启动浏览器访问微软的鉴权页面。

  2. 页面会自动跳转到微软账号登陆界面。

  3. 成功登陆后,会提示用户服务使用的权限。

  4. 用户同意权限后,会跳转到服务注册Redirect URL。本例是localhost:9000

  5. 理论上,在localhost:9000上,应该跑着一个httpd。这个httpd可以捕获微软通过http get方法传递过来的后续参数。自己本机使用,无需这么重的应用。启动个socket,然后简单listen 9000端口,捕获http服务,然后分析其get的url字串即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # 创建本机简易web服务器,取得code
    s = socket.socket()
    s.bind((self.cfg['host'],self.cfg['port'])) # localhost , 9000
    s.listen(1)
    c , addr = s.accept()
    req_body = c.recv(4096)
    f = StringIO.StringIO(req_body)
    code_re = re.search(r'GET /\?code=([^\s]+) ', f.readline() ) #标准http协议,拿到code
    f.close()
    code = code_re.group(1)
    #输出给浏览器一个简单的html页面
    #close_page.html是一个简单html模板,替换掉里面=CODE=,就可以显示code,便于调试
    with open(self.pkg_dir + "close_page.html","r") as f:
    html = f.read()
    html.replace("=CODE=",code)
    c.send(html.replace("=CODE=",code))
    c.close()
    s.close()
    html模板如下:
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    <html>
    <head>
    <meta charset="UTF-8">
    <title>已成功</title>
    <style type="text/css">
    body{text-align: center;font-family: "微软雅黑";}
    #box{width: 500px;height: 700px;margin: 0 auto;border: 1px solid #b2b2b2;margin-top:80px;border-radius: 5px; background-color:#d9edf7 }
    h1{color: #5cb85c;line-height:80px;}
    h2{line-height:80px;}
    #code_num{color:#337ab7;}
    #time{color:#ff322d;}
    </style>
    <style>
    html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}
    </style>
    <style>
    *, *:before, *:after{
    box-sizing:border-box;
    }
    body{
    font-family: 'Lato', sans-serif;
    }
    div.foo{
    width: 90%;
    margin: 0 auto;
    text-align: center;
    }
    .letter{
    display: inline-block;
    font-weight: 900;
    font-size: 3em;
    margin: 0.2em;
    position: relative;
    color: #00B4F1;
    transform-style: preserve-3d;
    perspective: 400;
    z-index: 1;
    }
    .letter:before, .letter:after{
    position:absolute;
    content: attr(data-letter);
    transform-origin: top left;
    top:0;
    left:0;
    }
    .letter, .letter:before, .letter:after{
    transition: all 0.3s ease-in-out;
    }
    .letter:before{
    color: #fff;
    text-shadow:
    -1px 0px 1px rgba(255,255,255,.8),
    1px 0px 1px rgba(0,0,0,.8);
    z-index: 3;
    transform:
    rotateX(0deg)
    rotateY(-15deg)
    rotateZ(0deg);
    }
    .letter:after{
    color: rgba(0,0,0,.11);
    z-index:2;
    transform:
    scale(1.08,1)
    rotateX(0deg)
    rotateY(0deg)
    rotateZ(0deg)
    skew(0deg,1deg);
    }
    .letter:hover:before{
    color: #fafafa;
    transform:
    rotateX(0deg)
    rotateY(-40deg)
    rotateZ(0deg);
    }
    .letter:hover:after{
    transform:
    scale(1.08,1)
    rotateX(0deg)
    rotateY(40deg)
    rotateZ(0deg)
    skew(0deg,22deg);
    }
    </style>
    <script type="text/javascript">
    var start = 10;
    var step = 1;
    function count()
    {
    document.getElementById("time").innerHTML = start;
    if(start == 10)
    step *= -1;
    start += step;
    if(start<0){
    window.close();
    return;
    }
    setTimeout("count()",1000);
    document.getElementById("time").innerHTML=start;
    }
    window.onload = count;
    </script>
    </head>
    <body>
    <div id="box">
    <h1><span class="letter" data-letter="已"></span><span class="letter" data-letter="成"></span><span class="letter" data-letter="功"></span></h1>
    <h2 style="">code:<span id="code_num">=CODE=</span></h2>
    <h2 style=""><span id="time">10</span>秒后自动关闭</h2>
    </div>
    </body>
    </html>
  6. 记录需要的信息。关闭这个土httpd。

  7. 根据信息,访问onenote的RESTFUL API即可。

    这里注意下,最好使用文档中提及的post file模式。如果不是,很容易上传不全。
    
    原理就是,先做一个html的文章模板。然后将内容替换进去,再上传整个文件内容(等同于浏览器的文件上传方式)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def CreatePage(self,Session,Title,Content):
    headers = {"Authorization" :"Bearer %s" %(self.cfg['access_token']) }
    url = "https://www.onenote.com/api/v1.0/me/notes/pages?sectionName=%s" %(Session)
    # 加载模板
    with open(self.pkg_dir+"article_tpl.html" ,"r") as f:
    tpl = f.read()
    data = tpl.replace("TITLE",Title).replace("CONTENT",Content).replace("\n","\r\n").replace("<br>","<br>\r\n")
    myfile = {'Presentation':("",data,"text/html")}
    r = requests.post(url,headers=headers,files=myfile,proxies={'https':'socks5://127.0.0.1:9675'})
    if "contentUrl" in r.content:
    return True
    else:
    return False

后记

OneNote很好用,增删改文章也比较容易。麻烦的就是鉴权部分。好好测试吧。