Google Search Console API の java client を使ってみる


以前、Google Analytics V4 API(Java) URL ごとのpageview を取得する | Monotalkで、Google Analytics V4 API について書きました。

Search Consoleについても、Search Console APIs  |  Google Developers というAPI があり、java の client があることを知ったので、今回はこちらを使ってみた結果を記載します。


参考


使用可能な API について

Google APIs Explorer から取得した Search Console API v3 の API のリストです

検索パフォーマンスデータの取得、sitemap の送信 / 取得 / 削除 、 site 登録 、 クロールエラーURL の 編集が可能です。

API名説明
webmasters.searchanalytics.query検索アナリティクスのデータを取得する
webmasters.sitemaps.deleteSearch Console に 送信した sitemap を 削除する
webmasters.sitemaps.getSearch Console に 送信した sitemap を 取得する
webmasters.sitemaps.listSearch Console に 送信した sitemap の list を取得する。
webmasters.sitemaps.submitSearch Console に sitemap を送信する
webmasters.sites.addSearch Console に site を追加する
webmasters.sites.deleteSearch Console に 登録した site を削除する
webmasters.sites.getSearch Console に 登録した site の情報を取得する
webmasters.sites.listSearch Console に 登録した site の情報の list を取得する
webmasters.urlcrawlerrorscounts.queryエラーカテゴリとプラットフォームごとのURLクロールエラー数の時系列を取得する
webmasters.urlcrawlerrorssamples.getサイトのサンプルURLのクロールエラーの詳細を取得する
webmasters.urlcrawlerrorssamples.list指定されたクロールエラーカテゴリとプラットフォームのサイトのサンプルURLを一覧表示する
webmasters.urlcrawlerrorssamples.markAsFixed提供されたサイトのサンプルURLを修正済みとしてマークし、サンプルリストから削除する

Google Search Console API を使用するメリット

触った限り、Google Serach Console の画面から操作するのに比べて以下のようなメリットがあると思いました。

  • Dimension を複数設定できる
    Dimension の要素として、画面上からだと、クエリページデバイス日付検索タイプ が指定可能ですが、1つしか指定できません。
    API 経由だと、検索タイプ 以外の Dimension を複数指定して検索することができます。

  • スケジューラに設定して、バックアップを自動化する
    Google Search Console は、過去3月分しかデータを参照できないため、
    定期的に、データをExportしておく必要があります。
    スケジューラから、APIを起動することで、データのバックアップの自動化ができます。

  • サイト管理作業の自動化
    SiteMap登録、サイト削除などのいくつかの更新オペレーションを行うAPIがあります。
    大量に登録/削除を実施する場合は、このあたりを使用することで、効率化できそうに思います。

ただ、サイトの分析データのバックアップだけであれば、Search Analytics for Sheets - Google スプレッドシート アドオン を使うのが簡単でいいと思います。
サイト管理系のオペレーションを行う、または、APIの結果から独自の指標を計算して出力する必要がある場合、各言語のクライアントを使用することになります。1 [1].この記事を書いておいてなのですが、個人的に利用する分には、Search Analytics for Sheets で事足りそうです。


サービスアカウントの作成と、キーの発行

API の認証には OAuth2 ではなくて、 サービスアカウントの Json キーを使用して、認証を行いました。

手順

以下の記事がわかりやすいです。
Google Search Console に読み替えれば設定できるかと思います。

[API] Google APIを使う際に、APIを有効化して認証キーを取得する方法 - YoheiM .NET

作成したアカウントをGoogle Search Console に追加する。

記事に記載がないところの補足です。

  • Google Search Console Top の対象サイト、プロパティの管理から、ユーザーのを追加/削除をクリック
    Search Console Top

  • ユーザーとプロパティ所有者の一覧に遷移、新しいユーザーの追加をクリック
    ユーザーとプロパティ所有者

  • 作成したアカウントのメールアドレスを入力し、追加をクリック。
    権限はフルでなくても参照はできるかと思います。
    ユーザーの追加

  • ユーザーとプロパティ所有者の一覧に戻る。追加したアカウントが一覧にも追加される。
    ユーザーとプロパティ所有者(追加後)

これで、プログラム実行の事前準備は終わりです。


gradle 、maven にライブラリ依存関係を追加

google-api-services-webmasters というライブラリと、
google-api-client が必要になります。

  • maven

    <!-- https://mvnrepository.com/artifact/com.google.api-client/google-api-client -->
    <dependency>
        <groupId>com.google.api-client</groupId>
        <artifactId>google-api-client</artifactId>
        <version>1.22.0</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/com.google.apis/google-api-services-webmasters -->
    <dependency>
        <groupId>com.google.apis</groupId>
        <artifactId>google-api-services-webmasters</artifactId>
        <version>v3-rev22-1.22.0</version>
    </dependency>
    

  • gradle

        compile group: 'com.google.api-client', name: 'google-api-client', version: '1.22.0'
        compile 'com.google.apis:google-api-services-webmasters:v3-rev22-1.22.0'
    


サンプル実装

SitesExample.java

Google Search Console のサイトの一覧情報するプログラムです。
Webmasters.Sites.List#execute() で取得しています。
Quickstart: Run a Search Console App in Java  |  Search Console API  |  Google Developers と実装上やっていることは同じです。
認証はサービスアカウントのJSONキーを使って認証しているので、
GoogleCredential.fromStream(new FileInputStream(KEY_FILE_LOCATION)).createScoped(Collections.singleton(WebmastersScopes.WEBMASTERS)); の、KEY_FILE_LOCATION にJSONキーのファイルパスを指定しています。
動かす場合は、private static final String KEY_FILE_LOCATION = "{api_key_file_path}"; をダウンロードした、JSONキーのファイルパスに変更してください。

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.webmasters.Webmasters;
import com.google.api.services.webmasters.WebmastersScopes;
import com.google.api.services.webmasters.model.SitesListResponse;
import com.google.api.services.webmasters.model.WmxSite;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;

import static java.lang.System.out;

/**
 * SitesExample
 */
public class SitesExample {

    private static final String KEY_FILE_LOCATION = "{api_key_file_path}";

    /**
     * main method
     *
     * @param args
     */
    public static void main(String[] args) {
        Webmasters webmasters = buildWebmasters();
        try {
            Webmasters.Sites.List request = webmasters.sites().list();
            SitesListResponse siteList = request.execute();
            out.println("siteList#toPrettyString() START>>>");
            out.println(siteList.toPrettyString());
            out.println("<<<END");

            out.println("siteList#getSiteEntry(); START>>>");
            List<WmxSite> wmxSites = siteList.getSiteEntry();
            int i = 0;
            for (WmxSite wmxSite : wmxSites) {
                out.println("--------wmxSite:elem" + (++i) + "--------");
                out.println("permissionLevel:" + wmxSite.getPermissionLevel());
                out.println("siteUrl:" + wmxSite.getSiteUrl());
            }
            out.println("<<<END");
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    private static Webmasters buildWebmasters() {
        HttpTransport httpTransport = null;
        try {
            httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(e);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        GoogleCredential credential = null;
        try {
            credential = GoogleCredential.fromStream(new FileInputStream(KEY_FILE_LOCATION)).createScoped(Collections.singleton(WebmastersScopes.WEBMASTERS));
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        // Create a new authorized API client
        Webmasters webmasters = new Webmasters.Builder(httpTransport, jsonFactory, credential).setApplicationName("Search Console Cli").build();
        return webmasters;
    }
}

SiteMapsExample.java

以下、登録したSiteMap の一覧情報を取得するサンプルになります。
Webmasters.Sitemaps.List#execute() でサイトマップの一覧は取得可能です。

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.webmasters.Webmasters;
import com.google.api.services.webmasters.WebmastersScopes;
import com.google.api.services.webmasters.model.SitemapsListResponse;
import com.google.api.services.webmasters.model.WmxSitemap;
import com.google.api.services.webmasters.model.WmxSitemapContent;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;

import static java.lang.System.out;

/**
 * SiteMapsExample
 */
public class SiteMapsExample {

    private static final String KEY_FILE_LOCATION = "{api_key_file_path}";

    /**
     * main method
     *
     * @param args
     */
    public static void main(String[] args) {
        Webmasters webmasters = buildWebmasters();
        try {
            Webmasters.Sitemaps.List siteMaps = webmasters.sitemaps().list("https://www.monotalk.xyz");
            SitemapsListResponse response = siteMaps.execute();
            out.print("response#toPrettyString() START>>>");
            out.print(response.toPrettyString());
            out.print("<<<END");

            List<WmxSitemap> wmxSitemaps = response.getSitemap();
            out.println("response#getSitemap(); START>>>");
            int i = 0;
            for (WmxSitemap wmxSitemap : wmxSitemaps) {
                out.println("--------wmxSitemap:elem" + (++i) + "--------");
                out.println("path:" + wmxSitemap.getPath());
                out.println("type:" + wmxSitemap.getType());
                out.println("isPending:" + wmxSitemap.getIsPending());
                out.println("isSitemapsIndex:" + wmxSitemap.getIsSitemapsIndex());
                out.println("lastDownloaded:" + wmxSitemap.getLastDownloaded());
                out.println("lastSubmitted:" + wmxSitemap.getLastSubmitted());
                List<WmxSitemapContent> wmxSitemapContents = wmxSitemap.getContents();
                int j = 0;
                for (WmxSitemapContent wmxSitemapContent : wmxSitemapContents) {
                    out.println("--------wmxSitemapContent:elem" + (++j) + "---");
                    out.println("wmxSitemapContent.indexed:" + wmxSitemapContent.getIndexed());
                    out.println("wmxSitemapContent.submitted:" + wmxSitemapContent.getSubmitted());
                    out.println("wmxSitemapContent.type:" + wmxSitemapContent.getType());
                }
            }
            out.println("<<<END");

        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    private static Webmasters buildWebmasters() {
        HttpTransport httpTransport = null;
        try {
            httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(e);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        GoogleCredential credential = null;
        try {
            credential = GoogleCredential.fromStream(new FileInputStream(KEY_FILE_LOCATION)).createScoped(Collections.singleton(WebmastersScopes.WEBMASTERS));
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        // Create a new authorized API client
        Webmasters webmasters = new Webmasters.Builder(httpTransport, jsonFactory, credential).setApplicationName("Search Console Cli").build();
        return webmasters;
    }

}

SearchAnalyticsExample.java

サイト分析結果取得のサンプルになります。
Webmasters#searchanalytics()#query()#execute() でクエリを発行して結果を取得しています。
dimentions として、date を指定していますが、ドキュメントには指定可能な記載がありませんでした。
試しに指定してみたところ、ちゃんとグルーピングされて取得できたので、指定可能と判断して指定しています。
query#setAggregationType() については、挙動がよくわからず、指定せずにコメントアウトしています。
また、query.setDimensionFilterGroups(apiDimensionFilterGroups) で設定しているフィルターの条件は、
「page の、url に “java”を含み、且つ、page の、url に “codec”を含む」 FilterGroupはand でのみ設定可能なようで、
FilterGroup を2つ設定しても、Filterを2つ設定しても挙動は変わりませんでした。

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.webmasters.Webmasters;
import com.google.api.services.webmasters.WebmastersScopes;
import com.google.api.services.webmasters.model.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static java.lang.System.out;

/**
 * SearchAnalyticsExample
 */
public class SearchAnalyticsExample {

    private static final String KEY_FILE_LOCATION = "{api_key_file_path}";

    /**
     * main method
     *
     * @param args
     */
    public static void main(String[] args) {
        Webmasters webmasters = buildWebmasters();
        try {
            SearchAnalyticsQueryRequest query = new SearchAnalyticsQueryRequest();
            query.setStartDate("2016-07-02");
            query.setEndDate("2017-05-02");
            query.setRowLimit(5000);
            query.setStartRow(0);
            query.setSearchType("web");
            List<String> dimentions = new ArrayList<String>();
            dimentions.add("page");
            dimentions.add("query");
            dimentions.add("date");
            dimentions.add("device");
            dimentions.add("country");
            query.setDimensions(dimentions);
            // ----------------------------
            // AgregationType
            // -----------------
            // query.setAggregationType("auto");
            // query.setAggregationType("byPage");
            // query.setAggregationType("byProperty");

            List<ApiDimensionFilterGroup> apiDimensionFilterGroups = getApiDimensionFilterGroups();
            query.setDimensionFilterGroups(apiDimensionFilterGroups);

            SearchAnalyticsQueryResponse searchAnalyticsQueryResponse = webmasters.searchanalytics().query("https://www.monotalk.xyz", query).execute();
            out.println("searchAnalyticsQueryResponse#.toPrettyString() START>>>");
            out.println(searchAnalyticsQueryResponse.toPrettyString());
            out.println("<<<END");

            for (ApiDataRow row : searchAnalyticsQueryResponse.getRows()) {
                out.println(row.toPrettyString());
            }
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    private static List<ApiDimensionFilterGroup> getApiDimensionFilterGroups() {
        // page の、url に "java"を含み、且つ、page の、url に "codec"を含む
        // ページのみに絞り込み
        ApiDimensionFilter javaFilter = new ApiDimensionFilter();
        javaFilter.setDimension("page");
        javaFilter.setExpression("java");
        javaFilter.setOperator("contains");
        ArrayList<ApiDimensionFilter> javaFilters = new ArrayList<>();
        javaFilters.add(javaFilter);
        ApiDimensionFilterGroup apiDimensionJavaFilterGroup = new ApiDimensionFilterGroup();
        apiDimensionJavaFilterGroup.setFilters(javaFilters);
        apiDimensionJavaFilterGroup.setGroupType("and");
        ApiDimensionFilter codecFilter = new ApiDimensionFilter();
        codecFilter.setDimension("page");
        codecFilter.setExpression("codec");
        codecFilter.setOperator("contains");
        ArrayList<ApiDimensionFilter> pythonFilters = new ArrayList<>();
        pythonFilters.add(codecFilter);
        ApiDimensionFilterGroup apiDimensionPythonFilterGroup = new ApiDimensionFilterGroup();
        apiDimensionPythonFilterGroup.setFilters(pythonFilters);
        apiDimensionPythonFilterGroup.setGroupType("and");
        List<ApiDimensionFilterGroup> apiDimensionFilterGroups = new ArrayList<>();
        apiDimensionFilterGroups.add(apiDimensionJavaFilterGroup);
        apiDimensionFilterGroups.add(apiDimensionPythonFilterGroup);
        return apiDimensionFilterGroups;
    }

    private static Webmasters buildWebmasters() {
        HttpTransport httpTransport = null;
        try {
            httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(e);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        GoogleCredential credential = null;
        try {
            credential = GoogleCredential.fromStream(new FileInputStream(KEY_FILE_LOCATION)).createScoped(Collections.singleton(WebmastersScopes.WEBMASTERS));
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        // Create a new authorized API client
        Webmasters webmasters = new Webmasters.Builder(httpTransport, jsonFactory, credential).setApplicationName("Search Console Cli").build();
        return webmasters;
    }
}

UrlCrawlErrorsCountsExample.java

クロールエラーのサマリーを返す、APIのサンプルです。
Webmasters.Urlcrawlerrorscounts#query() で、サマリーを取得しています。
エラーチェック用途のAPIでしょうか..?

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.webmasters.Webmasters;
import com.google.api.services.webmasters.WebmastersScopes;
import com.google.api.services.webmasters.model.UrlCrawlErrorCount;
import com.google.api.services.webmasters.model.UrlCrawlErrorCountsPerType;
import com.google.api.services.webmasters.model.UrlCrawlErrorsCountsQueryResponse;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;

import static java.lang.System.out;

/**
 * UrlCrawlErrorsCountsExample
 */
public class UrlCrawlErrorsCountsExample {
    private static final String KEY_FILE_LOCATION = "{api_key_file_path}";

    /**
     * main method
     *
     * @param args
     */
    public static void main(String[] args) {
        Webmasters webmasters = buildWebmasters();
        try {
            Webmasters.Urlcrawlerrorscounts.Query request = webmasters.urlcrawlerrorscounts().query("https://www.monotalk.xyz");
            UrlCrawlErrorsCountsQueryResponse response = request.execute();
            out.println("response#toPrettyString() START>>>");
            out.println(response.toPrettyString());
            out.println("<<<END");

            out.println("responset#getCountPerTypes(); START>>>");
            List<UrlCrawlErrorCountsPerType> urlCrawlErrorCountsPerTypes = response.getCountPerTypes();
            int i = 0;
            for (UrlCrawlErrorCountsPerType urlCrawlErrorCountsPerType : urlCrawlErrorCountsPerTypes) {
                out.println("--------urlCrawlErrorCountsPerType:elem" + (++i) + "--------");
                out.println("category:" + urlCrawlErrorCountsPerType.getCategory());
                out.println("platform:" + urlCrawlErrorCountsPerType.getPlatform());
                java.util.List<UrlCrawlErrorCount> urlCrawlErrorCounts = urlCrawlErrorCountsPerType.getEntries();
                int j = 0;
                for (UrlCrawlErrorCount urlCrawlErrorCount : urlCrawlErrorCounts) {
                    out.println("----urlCrawlErrorCount:elem" + (++j) + "--------");
                    out.println("count:" + urlCrawlErrorCount.getCount());
                    out.println("timestamp:" + urlCrawlErrorCount.getTimestamp());
                }
            }
            out.println("<<<END");
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    private static Webmasters buildWebmasters() {
        HttpTransport httpTransport = null;
        try {
            httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(e);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        GoogleCredential credential = null;
        try {
            credential = GoogleCredential.fromStream(new FileInputStream(KEY_FILE_LOCATION)).createScoped(Collections.singleton(WebmastersScopes.WEBMASTERS));
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        // Create a new authorized API client
        Webmasters webmasters = new Webmasters.Builder(httpTransport, jsonFactory, credential).setApplicationName("Search Console Cli").build();
        return webmasters;
    }
}

UrlCrawlErrorsSamplesExample.java

こちらは、サマリーではなく、クロールエラーとなったURLの、
詳細情報が取得できます。
Webmasters.Urlcrawlerrorssamples#list() で引数には、エラーのカテゴリと検索タイプの指定が、必須になります。

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.webmasters.Webmasters;
import com.google.api.services.webmasters.WebmastersScopes;
import com.google.api.services.webmasters.model.UrlCrawlErrorsSample;
import com.google.api.services.webmasters.model.UrlCrawlErrorsSamplesListResponse;
import com.google.api.services.webmasters.model.UrlSampleDetails;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;

import static java.lang.System.out;

/**
 * UrlCrawlErrorsSamplesExample
 */
public class UrlCrawlErrorsSamplesExample {
    private static final String KEY_FILE_LOCATION = "{api_key_file_path}";

    /**
     * main method
     *
     * @param args
     */
    public static void main(String[] args) {
        Webmasters webmasters = buildWebmasters();
        try {
            Webmasters.Urlcrawlerrorssamples.List request = webmasters.urlcrawlerrorssamples().list("https://www.monotalk.xyz", "notFound", "web");
            UrlCrawlErrorsSamplesListResponse response = request.execute();
            out.println("response#toPrettyString() START>>>");
            out.println(response.toPrettyString());
            out.println("<<<END");
            out.println("responset#getUrlCrawlErrorSample(); START>>>");
            List<UrlCrawlErrorsSample> urlCrawlErrorSamples = response.getUrlCrawlErrorSample();
            int i = 0;
            for (UrlCrawlErrorsSample urlCrawlErrorsample : urlCrawlErrorSamples) {
                out.println("--------urlCrawlErrorsample:elem" + (++i) + "--------");
                out.println("pageUrl:" + urlCrawlErrorsample.getPageUrl());
                out.println("firstDetected:" + urlCrawlErrorsample.getFirstDetected());
                out.println("lastCrawled:" + urlCrawlErrorsample.getLastCrawled());
                out.println("responseCode:" + urlCrawlErrorsample.getResponseCode());
                UrlSampleDetails userDetails = urlCrawlErrorsample.getUrlDetails();
            }
            out.println("<<<END");
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    private static Webmasters buildWebmasters() {
        HttpTransport httpTransport = null;
        try {
            httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(e);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        GoogleCredential credential = null;
        try {
            credential = GoogleCredential.fromStream(new FileInputStream(KEY_FILE_LOCATION)).createScoped(Collections.singleton(WebmastersScopes.WEBMASTERS));
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        // Create a new authorized API client
        Webmasters webmasters = new Webmasters.Builder(httpTransport, jsonFactory, credential).setApplicationName("Search Console Cli").build();
        return webmasters;
    }
}

後半、ちょっと駆け足になりました。
そのうち補足でもしようかと思います。
以上です。

コメント