The jonki

呼ばれて飛び出てじょじょじょじょーんき

arXiv論文のためのChrome拡張を作った

皆さんはarXivの論文を読んでまとめようとするとき,どうやってその論文情報をEvernoteなりOneNoteなりに書いていますか?私はいちいちコピペしていたのですが,arXivは更新頻度が高いので面倒だなと思い,Chromeの拡張を作って単純作業は自動化しました.

作ったのは2つ.1つずつ概要を説明していきます.もし興味あればChrome拡張のコード説明も後半に載せます.

  1. 論文の主要情報をテキストとしてコピーするもの(arXiv clip

  2. 論文の主要情報を所定のGitbhubにissueとしてポストするもの(arxiv2issue

arXiv clip

下記のような特定の論文ページを開いた状態で,arXiv clipのアイコンをクリックすることで,論文情報(タイトル,著者,コメント部(学会情報が書かれること多い,未記入の場合はスキップ),URL)が下記のようにコピーされます.論文を共有したい時とかに便利です. f:id:jonki:20181228111051j:plain

Learning End-to-End Goal-Oriented Dialog
Antoine Bordes, Y-Lan Boureau, Jason Weston    
Accepted as a conference paper at ICLR 2017
https://arxiv.org/abs/1605.07683

arxiv2issue

これは先程の応用編のようなものです.私は論文をGithubのissueにまとめています.気になった論文をこれまではarXiv clipでコピーした情報を,いちいちIssue生成→貼り付け,とやっていたのですが,面倒なのでissueの生成も自動化するようにしました.こちらも先程と同様に所定のarXivの論文のページでarxiv2issueのアイコンをクリックするだけです.issueに投稿するにあたって,GithubのPersonal Access Tokenというものを使用しています.arxiv2issueのアイコンを右クリックして,生成したトークン,Githubユーザー名,リポジトリ名をセットしておくことで,好きなリポジトリでissueを生成することができます.

Personal Access Tokenの取得方法などは,下記のREADMEを参考にしてみてください. github.com

f:id:jonki:20181228111723p:plain
投稿イメージ

以下コード解説をしてみますが,Chrome拡張に興味がある人は見てみてください.私自身,本機能のためだけにChrome拡張を作ったのでちゃんと勉強していません.下手な書き方など大いにあると思いますがご容赦ください.

コード解説

Chromeの拡張作成の細かいところは,公式のドキュメントに譲り,ここでは機能のコア部分について説明します.

まずarxiv-clipは下記のような構成になっています.ソースコードはこちら

$ tree
.
├── background.js    // 所定のページ(今回はarxiv)を現在のタブが開いているかチェックし,開いていればアイコンを有効化
├── jquery-3.3.1.slim.min.js    // HTMLのパース用に追加
├── manifest.json   // Chrome拡張の宣言ファイルのようなもの
├── popup.html   // アイコンを押したときに表示されるGUI.
├── popup.js  // 下記に詳述

もっとも重要なpopup.jsのコードを貼ります.やっている内容は,カレントタブのHTMLを読み込んでjQueryに流し込み,欲しい情報を取得.取得した論文情報をcopyToClipboard関数を使ってクリップボードにコピーしています.このコピー部分はトリッキーなことをやっていますが,ブラックボックスな関数として使って良いと思います.

ARXIV_URL = 'https://arxiv.org/*';

function getCurrentTabUrl(callback) {
    var queryInfo = {
        url: ARXIV_URL,
        active: true,
        currentWindow: true
    };

    chrome.tabs.query(queryInfo, (tabs) => {
        if (tabs.length > 0) {
            var tab = tabs[0];
            var url = tab.url;
            console.assert(typeof url == 'string', 'tab.url should be a string');
            callback(url);
        } else {
            $('#result').text('not arXiv!');
        }
    });
}

function modifyDOM() {
    return document.body.innerHTML;
}

function copyToClipboard(text) {
    const input = document.createElement('textarea');
    input.style.position = 'fixed';
    //input.style.opacity = 0;
    input.value = text;
    document.body.appendChild(input);
    input.select();
    document.execCommand('Copy');
    document.body.removeChild(input);
};

document.addEventListener('DOMContentLoaded', () => {
    getCurrentTabUrl((url) => {
        chrome.tabs.executeScript({
            code: '(' + modifyDOM + ')();' //argument here is a string but function.toString() returns function's code
        }, (results) => {
            var $dom = $($.parseHTML(results[0]));
            title = $dom.find('h1.title').text().split('Title:')[1];
            authors = $dom.find('div.authors').text().split('Authors:')[1];
            authors = authors.replace(/\n/g, '');
            comment = $dom.find('div.metatable').find('.comments').text();
            if (comment != '') {
                info = [title, authors, comment, url].join('\n');
            } else {
                info = [title, authors, url].join('\n');
            }

            copyToClipboard(info);
            $('#result').text('copied!');

            // hide popup automatically
            setTimeout(function () {
                window.close();
            }, 3000);
        });
    });
});

次にarxiv2issueの説明に入ります.コードはこちら.こちらの拡張はarxiv clipを拡張する形で作ります.追加で必要な機能として,トークンやリポジトリ情報を保存する機構とissueにポストする機能の2つを説明します.

情報を保存するためOptionsを呼ばれるページを作ります.アイコンを右クリックしたときに現れる設定ページのようなものです.基本的にはチュートリアルの例をコピペして,自分用にカスタムしただけです. Give Users Options - Google Chrome

情報の保存には,chrome.storage.syncを利用します.これを利用するとChromeで同期しているアカウント内ではデータが共有され,複数台のマシンを持っている人はイチイチ設定し直さなくてよいので便利です.下記のoptions.html/jsでは,3つのテキスト情報をchromeに保存しています.

<!DOCTYPE html>
<html>
    <head><title>arxiv2issue Options</title></head>
    <body>

        Github User Name:
        <p><input id="user-name"></p>
        Github Repository Name:
        <p><input id="repo"></p>
        Github Personal Access Token (<a href="https://github.com/settings/tokens/new">Get your token.</a> "repo" option must be checked for this extension.):
        <p><input id="token"></p>

        <div id="status"></div>
        <button id="save">Save</button>

        <script src="options.js"></script>
    </body>
</html>
// Saves options to chrome.storage
function save_options() {
    var uname = document.getElementById('user-name').value;
    var repo  = document.getElementById('repo').value;
    var token = document.getElementById('token').value;
    chrome.storage.sync.set({
        uname : uname,
        repo  : repo,
        token : token
    }, function() {
        var status = document.getElementById('status');
        status.textContent = 'Options saved.';
        setTimeout(function() {
            status.textContent = '';
        }, 750);
    });
}

// Restores values
function restore_options() {
    chrome.storage.sync.get({
        uname: '',
        repo: '',
        token: ''
    }, function(items) {
        document.getElementById('user-name').value = items.uname;
        document.getElementById('repo').value = items.repo;
        document.getElementById('token').value = items.token;
    });
}
document.addEventListener('DOMContentLoaded', restore_options);
document.getElementById('save').addEventListener('click', save_options);

f:id:jonki:20181228120401p:plain
設定画面

そしてissueにポストします.先程のpopup.jsを改変するだけで良いです.issue作成のために,Githubのv3 APIを使っています.非常に簡単で,先程のトークンをGETパラメタにセットしているだけです.注意としては,Acceptヘッダにjsonを入れるようにする必要があります.issueはJSON文字列として作り,タイトルや本文などの設定ができます.v3 APIのページにセットできるパラメタリストが載っています.私はtitleとbodyのみを設定して,ポストしています.

// 略
function create_issue(title, body, year, callback) {
    var base_url = 'https://api.github.com/repos';
    chrome.storage.sync.get(['uname', 'repo', 'token'], function(data) {
        var uname = data.uname;
        var repo = data.repo;
        var token = data.token;
        var url = [base_url, uname, repo, 'issues'].join('/');

        var url = url += '?access_token=' + token;
        console.log('URL: ' + url);

        var data = JSON.stringify({
            'title': ['🚧', year + ':', title].join(' '),
            'body': body
        });
        var request = new XMLHttpRequest();
        request.open('POST', url);
        request.onreadystatechange = function () {
            if (request.readyState != 4) {
            } else if (request.status != 201) {
                console.log(request.responseText);
                callback('Failed to post an issue.');
            } else {
                var resp = JSON.parse(request.responseText);
                callback('Issue posted!: #' + resp.number);
            }
        };
        request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        request.setRequestHeader('Accept', 'application/vnd.github.symmetra-preview+json');
        request.send(data);
    });
}
// 略

まとめ

今回はarXivの論文をまとめるための簡単なツールを紹介しました.arXivは更新頻度が多いので見るのが大変です.こういったツールを活かして,手作業コピペなどの余計な作業時間は極力減らして,うまく効率的に論文を読みたいものですね.