Help us understand the problem. What is going on with this article?

ジョブカンをWEBスクレイピング(打刻情報)

前記事ジョブカンをWEBスクレイピング(スタッフ情報) に引き続き、打刻情報の取出しにチャレンジします。

前記事より
日本で一番有名な勤怠管理システム「ジョブカン」(ごますりすり)!
残念ながら公開APIがなく自社システムへデータを連動させる仕組みを提示して
くれていません。
CSVダウンロード機能はありますが、自分の手でCSVを一旦ダウンロードして、
それを自社システムへアップロードして・・・という手間が必要です。
ここのところを自動化したいという思いがありまして、連動させるための仕組みを
調べてみた次第です。

はじめに

前回記事では「スタッフ情報」をJSON形式で取得できました。

当社ではジョブカンを使ってICカード打刻をしております。
打刻情報を自社の勤怠管理システムへ自動的に取り込みたいと考え、打刻情報の取出しにトライしました。

本稿で開示するソースは一部分のみです。ご要望があればコメントいただければ必要部を開示します。開示していないのは面倒だからだけです。

前提

前回同様、Java を使ってごりごりと実装します。(Java 1.8 で確認)

利用するライブラリ

前回同様、jsoup を使います。

わかったこと

・WEBスクライピングでジョブカン管理画面へログインできること
・打刻情報を CSV形式で取得できること(以外の形式は見つけられない)
・特定個人の日別打刻、月間の打刻、全社員の日別打刻、月間の打刻、いろいろ取れる。
・打刻情報は、打刻した時刻順で取れる。一日に数回打刻していれば全部とれる。
・取得できる情報は下記のとおり(リスト形式)。

位置 CSV項目 内容 補記
1 スタッフコード ジョブカンへ登録したスタッフコード 英数字のやつ
2 スタッフ名 スタッフの名前
3 日付 打刻した年月日 西暦 yyyy/m/d ( 前ゼロつかない)
4 時刻 打刻した時刻 24時間形式の H:M (前ゼロつかない)
5 打刻方法 (*註1)
6 GPS住所 GPS打刻時のみ住所が出る aligned
7 不明 情報なし
8 備考 打刻時に入れたコメントらしい

(*註1) 下記の例のとおり、ICカード/PCマイページ打刻/GPS と表示されるようです。

【CSVの例】(1行目はヘッダー、テキトーにマスクしています)
"スタッフコード","","日付","時刻","打刻方法","","","備考"
"it-01","奄美権兵衛","2019/9/2","8:43","ICカード","","",""
"it-01","奄美権兵衛","2019/9/2","20:27","ICカード","","",""
"it-02","鹿児島太郎","2019/9/2","8:53","PCマイページ打刻","","",""
"it-02","鹿児島太郎","2019/9/2","20:38","PCマイページ打刻","","",""
"it-03","福岡正子","2019/9/2","8:25","GPS","住所が特定できませんでした。","","PC打刻"
"it-03","福岡正子","2019/9/2","19:8","GPS","福岡県福岡市※※(Mask)","",""

上記はブラウザより手でダウンロードした内容と同じです(それ以上の情報はとれない)。

今回記事のポイントは、取得する範囲をパラメータとして与えてCSV形式の情報を取り出せる!というところにあります(パラメータの解析で試行錯誤しましたので)。

WEBスクレイピングしているところの解説

打刻CSVダウンロードのURLは下記のようです。

https://ssl.jobcan.jp/client/raw-adit/download?searching=1&search_type=(TYPE)&year=(yyyy)&month=(mm)&fromYear=(yyyy)&fromMonth=(mm)&fromDay=(dd)&toYear=(yyyy)&toMonth=(mm)&toDay=(dd)&employee_id=(ID)

【必須の項目】

parmeter 内容 設定値 補記
searching 1 固定 (*註1)
search_type 月・日 の指定 month or date 必須
year 取得するデータの西暦年 yyyy  必須
month 取得するデータの月 01~12 必須

(*註1) searching の意味は結局わからず。固定の1 でよさそう。

【search_type=date のときに必要である項目】

parmeter 内容 設定値 補記
fromYear 開始年 yyyy (*註2)
fromMonth 開始月 mm 
fromDay 開始日 dd 
toYear 終了年 yyyy  (*註2)
toMonth 終了月 mm 
toDay 終了日 dd 

(*註2) 必須の year と異なる年を入れたらどうなるかは試していない。

【 特定のスタッフの打刻のみ取り出したいときの項目】

parmeter 内容 設定値 補記
employee_id スタッフ連番 数字 (*註3)

(*註3) 英数字のスタッフコードではありません。スタッフに連番で振られた数字です。
前記事で取得したスタッフ情報(JSON形式)にて取得できます。

スタッフ情報の件はここにあります

今回は・・・

全スタッフの特定年月の打刻情報を取りたいときは、下記のURLになります。

https://ssl.jobcan.jp/client/raw-adit/download?searching=1&search_type=month&year=2019&month=09

ジョブカン管理画面にログインして上記URLを叩けばCSVがダウンロードされることがわかります。

ソース一部公開

ということで、これを自動化するためのソースは下記です。(一部抜粋)

ログインしてみるところは前記事を参照願います。(ここではCSVをダウンロードするところのみ)

public class JobCanWebDakokuDL {
    /** 打刻CSVダウンロード */
    private static final String DOWNLOAD_URI = "https://ssl.jobcan.jp/client/raw-adit/download";
    private static final String PARMS_BASE = "searching=1&search_type=@type&year=@year&month=@month";
    private static final String PARMS_FROM = "&fromYear=@year&fromMonth=@month&fromDay=@date";
    private static final String PARMS_TO = "&toYear=@year&toMonth=@month&toDay=@date";
    private static final String PARMS_STAFF_NO = "&employee_id=@staffNo";

    /** JobCan Cookies */
    private JobCanCookies cookies;

    public JobCanWebDakokuDL(JobCanCookies cookies){

        this.cookies = cookies;
    }

    public String download(JobCanDakokuDownloadParams params) throws JobCanWebException{
        return download(params,null);
    }
    public String download(JobCanDakokuDownloadParams params, Integer staffNo) throws JobCanWebException{

        StringBuilder b = new StringBuilder();
        b.append(DOWNLOAD_URI);
        b.append("?");
        b.append(
                PARMS_BASE
                .replaceAll("@type",params.getType())
                .replaceAll("@year", String.format("%04d", params.getYear()))
                .replaceAll("@month", String.format("%02d", params.getMonth()))
                );
        if(JobCanDakokuDownloadParams.TYPE_DATE.equals(params.getType())){
            b.append(
                    PARMS_FROM
                    .replaceAll("@year", String.format("%04d", params.getYear()))
                    .replaceAll("@month", String.format("%02d", params.getMonth()))
                    .replaceAll("@date", String.format("%02d", params.getFromDay()))
                    );
            if(params.getToDay()!=null && params.getToDay().compareTo(params.getFromDay())>0){
                // ToDay > FromDay のとき
                b.append(
                        PARMS_TO
                        .replaceAll("@year", String.format("%04d", params.getYear()))
                        .replaceAll("@month", String.format("%02d", params.getMonth()))
                        .replaceAll("@date", String.format("%02d", params.getToDay()))
                        );

            }else{
                // ToDay を FromDay にする。
                b.append(
                        PARMS_TO
                        .replaceAll("@year", String.format("%04d", params.getYear()))
                        .replaceAll("@month", String.format("%02d", params.getMonth()))
                        .replaceAll("@date", String.format("%02d", params.getFromDay()))
                        );

            }

        }
        if( staffNo != null){
            b.append(
                    PARMS_STAFF_NO
                    .replaceAll("@staffNo", String.format("%d", staffNo))
                    );
        }
        try {
            URL targetUrl = new URL(b.toString());
            Connection con = Jsoup.connect(targetUrl.toString())
                    // ContentTypeを無視する
                    .ignoreContentType(true) 
                    .cookies(this.cookies.getCookies())
                    .userAgent(JobCanConstants.Jsoup.UA)
                    .method(Method.GET)
                    .followRedirects(true);
            Response res = con.execute();
            this.cookies.save(res.cookies());

            if(JobCanWeb.isLogin(res)){
                return res.body();
            }else{
                Response reRes = JobCanWeb.login(targetUrl, this.cookies);
                this.cookies.save(reRes.cookies());
                return reRes.body();
            }

        } catch (IOException e) {
            e.printStackTrace();
            logger.error(e.getMessage());
            throw new JobCanWebException(e);
        }
    }
}

最後に

取得したCSV形式の打刻情報を、改行部でSPLITして、ヘッダー行を除去して、"," でSPLITして・・とごにょごにょとすると、必要な情報を取り出すことができそうです(実際できた)。

思い切り端折ったソース公開ですが、参考にはなると信じています!

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away