tsurutanのつぶやき

備忘録としてつぶやきます

Wantedly プレミアムに登録してみた結果

f:id:tsurutan:20161006173540p:plain

皆さんWantedlyについてご存知ですか?

www.wantedly.com

Wantedlyとは

運命のチームや仕事に出会えたり、人脈を広げ、ビジネスの情報収集に使えるビジネスSNSです

と公式には書いてあります。

実際にWantedlyに登録しておくと自社の募集を出せたり、また自分のプロフィールを見た会社の方からスカウトメールが届いたりします。

また、Wantedlyにはプロフィールに独自に採点されたスコアがついており、この値が高いとより多くの方から注目を浴びることができます。

そしてWantedlyにはプレミアムモードが存在しており、今回はこれを使ってどうなったかを報告したいと思います。

Wantedlyプレミアムとは

Wantedlyプレミアムを利用することで、 運命の企業に対して自分をうまくアピールして、企業からのアプローチを増やすことができます。

企業からお誘いされるチャンスが10倍に

WantedlyプレミアムのユーザはWantedly上の企業15000社からスカウトを受け取ることができます。通常のユーザが受け取れる企業数は約300社です。

企業にあなたの本気が伝わります

企業にWantedlyプレミアムユーザであることが伝わり、スカウトが増えます。他のユーザには表示されません。

あなたのプロフィールを見た企業を閲覧

企業があなたにどれくらい興味を持っているかがわかります。閲覧した企業に自分から企業への興味を伝えることができます。

プロフィールへのアドバイス

あなたのWantedlyプロフィールへのアドバイスを受けることができます。より魅力的なプロフィールに近づきます。

といった売りこみがありました。

300社から15000社にグレードアップとあるのですが、そんなに必要なのか怪しいですね笑

またQAには

無料期間はありますか?

まずは1週間無料でご利用いただけます。1週間以内に解約すれば料金はかかりません。

どのくらいのスカウトが届くの?

平均毎月10社からのスカウトが受け取れます。

とありました。

とりあえず、一週間無料期間があるということで登録してみました。

結果

登録前

登録する前はなんとスカウト数1件。

これは悲しい。。

たまに自分から会社説明の「興味がある」ボタンを押すと、その会社からメールが来ることはあったのですが何もせずに来たメールはたったの一件でした。

そして。。。

登録後

登録して1日目からなんとスカウトメールが3件きましたww

その後も一週間同じペースでメールが来て合計23件!!!!

なんとその会社の中には誰もが知る有名企業や、一部上場企業などなど!!

いやー、すごいですねWantedly プレミアム。

もし転職を考えている方や、アルバイトを探している学生がいましたら是非Wantedly Premiumになることをお勧めします!

会社を辞めて後悔しない39の質問

会社を辞めて後悔しない39の質問

転職の赤本

転職の赤本

「会社四季報」業界地図 2017年版

「会社四季報」業界地図 2017年版

Pythonでスクレイピングをしてみよう!

f:id:tsurutan:20161006123120j:plain

今回はPythonでスクレイピングをするやり方について説明しようと思います。

まだpythonを始めたばかりスクレイピングって難しそうだけどどうやってやるの?と苦しんでいる読者を想定しています。

Scrapingでできること

店舗一覧、商品一覧を抽出する ・ニュースサイトからヘッドライン一覧を抽出する ・ページのURLを全て抽出する などなど今まで煩わしかったことが解消できます!

Pythonの開発環境を整える

まずはPythonの開発環境を整えましょう。

ただ今回は日本語を今後扱うことを前提としてpython3のインストールの仕方について説明したいと思います。(pythonだと日本語の扱いがめんどくさい)

Homebrewのインストール

homebrewとはパッケージ管理システムで、ターミナルからコマンドを打ち込むことで簡単にインストールすることができます。

まずはxcodeツールが入っているか確認してください。

入っていなければ下記のコマンドを入力してください。

sudo xcode-select --install

そして次に公式サイトに書いてある通り

brew.sh

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

を入力してください。これで完成です。

python3のインストール

先ほどインストールしたbrewを使ってpython3を持ってきたいと思います。

まず

$ brew search python
app-engine-python                        python                                 
boost-python                             python3                                
gst-python                               wxpython                               
homebrew/versions/gst-python010          zpython                                
homebrew/apache/mod_python               Caskroom/cask/python                   
homebrew/python/python-dbus              Caskroom/cask/python3                  
homebrew/python/vpython                

と入力し、python3があることを確認しましょう。

確認が取れましたらpython3のインストールを行います。

brew install python3

はい、これで完成です。とても簡単ですね!

スクレイピングをしてみよう

スクレイピングをするにあたってまずseleniumBeautifulSoupをインストールしましょう。

これらをインストールするのもとても簡単で

pip3 install beautifulsoup4

pip3 install selenium

を入力すれば完了です。

また、seleniumではchrome driverというchromeを仮想的に立ち上げるドライバーが必要となるので

sites.google.com

ここからダウンロードし、適切なフォルダに保存しておいてください。(後でpathを指定するのでわかりやすいところに)

さて準備が整いましたのでコードに移っていきたいと思います。

news.yahoo.co.jp

今回はyahoo newsのトピック一覧から記事のタイトルと遷移先、カテゴリーを取ってくるところを実装してみたいと思います。

まず初めに先ほどインストールしたselniumとbeautifusoupをインポートします

from selenium import webdriver
from bs4 import BeautifulSoup

次にwebdriverの初期化を行います。

driver = webdriver.Chrome("/path/tmp/chromedriver")

webdriver.Chromeに渡しているのは先ほどダウンロードしたchrome driverのpathです。

そして本題、いよいよニュース記事の取得に入っていこうかと思います。

def get_yahoonews():
        driver.get("http://news.yahoo.co.jp/list/?c=world")
        html = driver.page_source
        soup = BeautifulSoup(html, "html.parser")
        for list in soup.body.find("ul", class_="list").find_all("li"):
            category = list.find("span", class_="cate").string
            title = list.find("span", class_="ttl").string
            href = list.a.get("href")

まずdriver.getの引数にyahoo newsの記事のurlを渡しております。 そして

html = driver.page_source
soup = BeautifulSoup(html, "html.parser")

webdriverで取得したhtmlを解析できるようにBeautifulSoupに渡します。

そして

for list in soup.body.find("ul", class_="list").find_all("li"):
            category = list.find("span", class_="cate").string
            title = list.find("span", class_="ttl").string
            href = list.a.get("href")

ここで解析を行ってタイトルと、カテゴリーと遷移先を取得しているのですが

まず一行目に

for list in soup.body.find("ul", class_="list").find_all("li")

と書いてあるのですがこれは、yahoo newsのページをhtmlで見てもられると分かるのですが(chromeであればCommand + Space + iで見ることができます)

f:id:tsurutan:20161006121605p:plain

このようにニュースがリスト状になっております。(liの中に記事が入っています)

よってこのニュースリストの要素をpythonで見れるようにfor分で回しているのです。

そして子要素を見てみると

f:id:tsurutan:20161006122139p:plain

このようにタイトルとカテゴリー、遷移先のurlが入っているので、これを

category = list.find("span", class_="cate").string
            title = list.find("span", class_="ttl").string
            href = list.a.get("href")

と書いて取得しているのです。

どうですか?簡単でしょう?

課題

アマゾンの食品一覧から、タイトル、遷移先を取得しなさい。

Amazon.co.jp: 食品: 食品・飲料・お酒

所感

www.tsurutan.com

以前Rubyでスクレイピングをする記事を書いたのですが、個人的にpythonの方が書きやすかった印象があります。

またChromeを仮想的に立ち上げることでiframeなどの時間差で表示されるものも取得できるのでとても使いやすかったです。

また、気をつけて欲しいのはスクレイピングを間を空けずやってしまうとDoS攻撃になってしまうので、10秒ほどの間隔を取ってやることにしましょう!

PythonによるWebスクレイピング

PythonによるWebスクレイピング

実践 Webスクレイピング&クローリング-オープンデータ時代の収集・整形テクニック

実践 Webスクレイピング&クローリング-オープンデータ時代の収集・整形テクニック

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

Android レイアウトの境界線を表示して開発速度を上げる

f:id:tsurutan:20161004220116j:plain

Android Studioで開発をしている場合、デフォルトのLayout Viewerや最近実装されたConstraint Layoutでだいぶデザインの調整がしやすくなっているのですが、いかんせんアプリを動かしてみると想定外のバグが発生してしまします。

そこで、アプリを起動しながらレイアウトの境界線を表示する方法を紹介したいと思います。

レイアウトの境界線の表示方法

まず、初めにAndroidの設定画面を開き端末情報をタップします。

そして下の方にスクロールして言うと ビルド番号 と書かれたところがあるので、そこを7回ほどタップします。

f:id:tsurutan:20161004214920j:plain

すると 開発者向けオプション という新しい項目が出てくるのでそちらをタップします。

f:id:tsurutan:20161004214937j:plain

そして、下の方にスクロールしていくと レイアウト境界を表示 とあるので、そちらをオンにします。

f:id:tsurutan:20161004214947j:plain

するとこのようにレイアウトの境界線が表示されるのです。

f:id:tsurutan:20161004215048j:plain

レイアウトの境界線が表示されることで、細かいデザインの修正(パッディングやマージン)を効率的に行うことができ、また今まで気づかなかったバグを見つけるなど良いことづくしです。(見た目も厨二病っぽくてカッコ良いですね笑)

Smashing Android UI レスポンシブUIとデザインパターン

Smashing Android UI レスポンシブUIとデザインパターン

中二病大事典

中二病大事典

Android Security Internals: An In-Depth Guide to Android's Security Architecture

Android Security Internals: An In-Depth Guide to Android's Security Architecture

Android 簡単にテレビ電話を実装する [Skyway]

f:id:tsurutan:20161004115601p:plain

今回はAndroidでテレビ電話を実装する方法を説明したいと思います。

使用するのはNTT Communicationsさんが提供しているSkywayというライブラリーです。

SkywayはWebRtcを使って、テレビ電話を実現したもので通信処理などの煩わしい部分をこのライブラリーが賄っており、簡単に実装することができます。

Skywayを入れるには?

現在Skywayではライブラリーがmaven上に存在しない為、手動でローカルにとってこないといけません。

github.com

こちらからzipファイルをダウンロードし、解凍したら中にSkyWay.aarというファイルがあるのでそれをandroidプロジェクトのapp直下にあるlibsディレクトリに入れてください。

そして app下のbuild.gradle

compile (name: 'SkyWay', ext: 'aar')

と記入し、プロジェクト下のbuild.gradle

allprojects {
    repositories {
        //何かしらのコード
        flatDir {
            dirs 'libs'
        }
    }
}

と記入すれば完成です。これでAndroid内にSkywayライブラリーをインポートすることができます。

API Keyの取得

Skywayライブラリーを使用するためにはAPI key とドメイン名が必要になるためこちらの公式ページから登録を行ってください。

nttcom.github.io

Skywayの使い方

github.com

こちらのサンプルファイルをベースに説明していきたいと思います。

まず初めにPeerの作成をします。

       PeerOption options = new PeerOption();

        //Enter your API Key.
        options.key = "";
        //Enter your registered Domain.
        options.domain = "";

        _peer = new Peer(context, options);

この時 options.key には先ほど取得したAPI Keyを options.domain にはドメイン名をセットしてください。 PeerOption にはkeyやdomain以外にもhostやport, debugなど様々な設定を与えることができます。

そしてPeerを作成した後にリスナーをセットします。

peer.on(Peer.PeerEventEnum.OPEN, new OnCallback()
        {
            @Override
            public void onCallback(Object object)
            {
                Log.d(TAG, "[On/Open]");

                if (object instanceof String)
                {
                    _id = (String) object;
                    Log.d(TAG, "ID:" + _id);

                    updateUI();
                }
            }
        });

        // !!!: Event/Call
        peer.on(Peer.PeerEventEnum.CALL, new OnCallback()
        {
            @Override
            public void onCallback(Object object)
            {
                Log.d(TAG, "[On/Call]");
                if (!(object instanceof MediaConnection))
                {
                    return;
                }

                _media = (MediaConnection) object;

                _media.answer(_msLocal);

                setMediaCallback(_media);

                _bCalling = true;

                updateUI();
            }
        });

        // !!!: Event/Close
        peer.on(Peer.PeerEventEnum.CLOSE, new OnCallback()
        {
            @Override
            public void onCallback(Object object)
            {
                Log.d(TAG, "[On/Close]");
            }
        });

        // !!!: Event/Disconnected
        peer.on(Peer.PeerEventEnum.DISCONNECTED, new OnCallback()
        {
            @Override
            public void onCallback(Object object)
            {
                Log.d(TAG, "[On/Disconnected]");
            }
        });

        // !!!: Event/Error
        peer.on(Peer.PeerEventEnum.ERROR, new OnCallback()
        {
            @Override
            public void onCallback(Object object)
            {
                PeerError error = (PeerError) object;

                Log.d(TAG, "[On/Error]" + error);

                String strMessage = "" + error;
                String strLabel = getString(android.R.string.ok);

                MessageDialogFragment dialog = new MessageDialogFragment();
                dialog.setPositiveLabel(strLabel);
                dialog.setMessage(strMessage);

                dialog.show(getFragmentManager(), "error");
            }
        });

これでPeerの作成は完了です。

次にカメラで撮影された映像を、自分のデバイスで表示したいと思います。

void startLocalStream(){
        Navigator.initialize(_peer);
        MediaConstraints constraints = new MediaConstraints();
        _msLocal = Navigator.getUserMedia(constraints);

        Canvas canvas = (Canvas) findViewById(R.id.svSecondary);
        canvas.addSrc(_msLocal, 0);
    }

まず初めに 映像を取得するクラス Navigatorに先ほど作成したPeerを渡します。

そして Navigatorのオプション的役割を持つ MeidaConstraints を作成し Navigator.getUserMedia の引数に渡します。

MeidaConstraintsにも様々なオプションがあり、動画のみ、音声のみ、フロントカメラなどの設定をすることができます。

これでstreamが取得できたので、これをCanvasに渡せばローカルのストリーム再生は完成です。

そして最後に呼び出しの部分ですね。

void calling(String strPeerId)
    {
        //////////////////////////////////////////////////////////////////////
        ////////////////// START: Calling SkyWay Peer   //////////////////////
        //////////////////////////////////////////////////////////////////////

        if (null == _peer)
        {
            return;
        }

        if (null != _media)
        {
            _media.close();
            _media = null;
        }

        CallOption option = new CallOption();

        _media = _peer.call(strPeerId, _msLocal, option);

        if (null != _media)
        {
            setMediaCallback(_media);

            _bCalling = true;
        }

        //////////////////////////////////////////////////////////////////////
        /////////////////// END: Calling SkyWay Peer   ///////////////////////
        //////////////////////////////////////////////////////////////////////


        updateUI();
    }

ここの _media = _peer.call(strPeerId, _msLocal, option);でコールをしています。第一引数には呼び出し先のPeer Idを指定しています。

ちなみにPeer IdはPeerを作成するときに指定することができます。デフォルトはランダムでIDは振り当てられます。

第二引数では先ほど作成したローカルのストリームが渡され、第三引数にはCallOptionが渡されます。

はい、これでテレビ電話の実装が完成しました。

いかがでしたでしょうか?

これで皆さんもテレビ電話機能を持つサービスを簡単に作ることができますね!

詳しいドキュメントは公式ページに記載されていますので、ぜひ目を通してみてください。

SkyWay ドキュメント

Android Retrofit 2 を使った Http通信

今日はRetrofit2を使ってAndroidのHttpレスポンスの実装方法を紹介したいと思います。

Retrofit2って?

f:id:tsurutan:20161004000732j:plain

Retrofit2はSquare(Jake 神)が開発しているネットワークアクセスライブラリです。

特徴的なのはサーバ側のAPIをインタフェースとして定義することで、API呼び出しの実装とAPI定義を分離しコードの見通しを簡潔に保つことができるという点です。

実装とAPI定義が分離しているため、後からAPIを追加することも容易となり保守性の高い設計ができそうです。

以前まではVolleyが主流でしたが今のイケてるサービスはほとんどRetrofit2を使っているのではないのでしょうか?

使い方

まず、想定として http://example.com/users/tsurutan/get というレスポンスを叩くと

{"user": {
  "id": 100,
  "name": "tsurutan",
}}

といったJsonが返ってくるとします。

この時Retrofit2では

public interface UserService {
  @GET("users/{user}/get")
  Call<User> getUser(@Path("user") String user);
}

このように記述します。

まずAPI用のインタフェースを作成します。

@GET というアノテーションがありますがこれは文字通りゲットレスポンスを表します。@Get以外にも@Post@Deleteなど様々なレスポンスに対応しております。

ただし@Patchは残念ながら存在しない為Postにヘッダーを加えてPatchに変換しないといけません。

また@Postを使ってMultipart通信を行えないのが少し難点かもしれません。。(この場合はIonやVolleyと言ったライブラリーを別途使った方が良いかもしれません)

また @Get( ) の中身にはレスポンスのエンドポイントを記します。

この場合エンドポイントの{user}の部分が各ユーザーごとに異なると思うのでその場合メソッドの引数に @Pathと指定し動的に変更することができます。

そしてCallの型に指定されているUserはgsonを使ったクラスで表されます。

gsonって何?という方に簡単にgsonについて説明したいと思います。

GSONは、Googleが提供するJSONデータとJavaオブジェクトを相互に変換するためのライブラリです。 JavaでJSONを扱うためのライブラリは他にJackson、JSONIC等があります。 GSONのメインとなるのは Gson クラスです。

例えば先ほどの

{"user": {
  "id": 100,
  "name": "tsurutan",
}}

というJsonのレスポンスを

class User {
  private int id;
  private String name;

  public int getId() {
    return id;
  }
  
  public void setId(int id) {
    this.id = id;
  }
  
  public String getName() {
    return name;
  }
  
  public void setName(String name) {
    this.name = name;
  }
}

と言ったクラスを記述することでJsonをオブジェクトにパースしてくれる優れたライブラリーなのです。

注意して欲しいのは変数の名前とJsonのkeyの名前が一致しているということです。

これはgsonが自動的に判断してくれる為あえてこのようにしております。

またkeyにtsuru_tanと言った感じにアンダーバーが入る場合はtsuru_tanとするか、gsonのフォーマット規約を変更してtsuruTanのように記述することもできます。

retrofit 2ではこのgsonを介することでapiから間接的にオブジェクトを取得することができるのです。

次に

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://example.com/")
 .addConverterFactory(GsonConverterFactory.create())
    .build();

UserService service = retrofit.create(UserService.class);

と記述します。このようにして先ほど作成したインタフェースを呼び出すのです。

ここで addConvertFactoryGsonConverterFactory.create()を渡しているのは先ほど説明したgsonを使う為メソッドを呼び出しております。

そして最後に

service.getUser("tsurutan").enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccess()) {
                    //Success !!
                } else {
                    Toast.makeText(getActivity(), getString(R.string.error_text), Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                Toast.makeText(getActivity(), getString(R.string.error_text), Toast.LENGTH_SHORT).show();
            }
        });

と記入します。getUserで引数にuser名を渡しその後enqueueでコールバックに対するコードを記入します

            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccess()) {
                    User user = response.body();
                    //Success !!
                } else {
                    Toast.makeText(getActivity(), getString(R.string.error_text), Toast.LENGTH_SHORT).show();
                }
            }

このメソッドはレスポンスのコードが400~451以外の時に呼び出されます。

またresponse.isSuccess()ではコードが200~226の時にtrueを返します。

そして成功した時にresponse.body()を呼び出すことで、先ほど作成したUserを受け取ることができます。

AndroidにおいてはAPI通信を非同期で行わなければならなく、以前までは非常にこのあたりのコードが長くなっておりました。

しかしこのようなライブラリーの登場でコードの簡潔化、開発の爆速化が実現しました。

Jake神に足を向けて寝ることはもう出来ませんね。

どうやって使うの?

compile 'com.squareup.retrofit2:retrofit:(insert latest version)'

お馴染みapp層のbuild.gradleにこの一行を加えるだけです!

簡単すぎて笑えてきますね!

超初心者でも大丈夫! はじめてのAndroidプログラミング Android Studio 2対応

超初心者でも大丈夫! はじめてのAndroidプログラミング Android Studio 2対応

ランダムフォレスト(機械学習)による文章分類

前置き

今回はランダムフォレストという手法で機械学習を行い文章の分類を行おうと思います。

例えばこのような生物医療に使われるデータベースがあったとします。

name of enzyme

alkaline phosphatases

comment

Comparison with alkaline phosphatases and 5-nucleotidase

name of enzyme酵素の名前で comment はその酵素についてのコメントです。 このようなデータベースを普通使うことはないと思うのですが、とりあえずこちらを使います。

想像してみてください。

あなたは生物学者です。

あなたは研究で扱った酵素について一つ一つコメントをつけ、データベースに保存していました。

しかし、ある日不運なことに一部酵素の名前データーが破損してしまいました。

この時、あなたは酵素とコメントがセットになっているデータとコメントのみのデータを持っています。

そこであなたは紛失してしまった酵素の名前をコメントを元に復元したいと考えました。

そこで扱うのが機械学習ランダムフォレストです。

機械にコメントと酵素の名前を学習させて、その後コメントから酵素の名前を予測してもらいましょう。

ツール

機械学習するためのツールを準備します。 使用する言語はpythonで、ライブラリーとして gensimsklearn を使用します

gensim

これはのちに説明するのですが、特徴ベクトルを作成するために使います。
特徴ベクトルとは機械がコメントを学習できるようにするために数値のベクトルで表されたものです。

sklearn

このライブラリーは機械学習などを行うために必要となるモジュールが豊富に含まれているものです。
今回はsklearnのランダムフォレストを使って学習を行います。

これらのライブラリーは pip install gensim pip install sklearnでインストールすることができます。とても簡単ですね!

それではまず、コメントから重要そうな単語を取り出します。

from gensim import corpora
import mysql

mysql = mysql.MySql()
words = []
for line in mysql.read_medtag():
    words.append(line["comment"].split(" "))

dictionary = corpora.Dictionary(words)
dictionary.filter_extremes(no_below=3, no_above=0.3)
dictionary.save_as_text('dict.txt')

このコードで何をしているか説明すると、

mysql = mysql.MySql()
words = []
for line in mysql.read_medtag():
    words.append(line["comment"].split(" "))

ここでデータベースからコメントを読み込み、スペースを区切りに文章を単語に分解して words という配列に格納していきます。

そして残りのコード

dictionary = corpora.Dictionary(words)
dictionary.filter_extremes(no_below=3, no_above=0.3)
dictionary.save_as_text('dict.txt')

では特徴語を作成し、dict.txt に保存しておきます。テキストに保存することで再度データベースを呼び出すことがなく、学習時間が節約できます。特にデータベースが大きいときには有効です。 corpora とはgensim に含まれているクラスでこちらを使い特徴語の辞書を作成します。 dictionary.filter_extremes(no_below=3, no_above=0.3) では特徴語として相応しくない単語を除いております。 no_below で指定された数値より下回る頻度で出現した単語は特徴語としてみなされず、また逆に no_above で書かれた割合より多く出現している単語は除かれます。ただ単に出現頻度が高い単語を特徴後とするのはあまり良い考えではないからです。(I He などの単語は出現頻度は高いが重要ではない)

少し長くなってしまったので、次回にランダムフォレストの実装について説明しようと思います。

アプリを大量生産してGoogleを怒らせた話

先日RSSをキーワード入力から登録できるアプリケーションを開発しリリースしました。

f:id:tsurutan:20160817113038p:plain

ところが。。。

f:id:tsurutan:20160823205336p:plain

なんとポリシー違反ということでアプリが停止されてしましました。

露骨な性表現を含むコンテンツに関するポリシーに違反だったのですが、どうやら検索キーワードで性的なものを入力するとヒットしてしまうのが原因だったっぽいです。

しかし、せっかく作ったアプリを公開できないのは勿体ないなあと考え、これを2chまとめアプリとして量産できないかという案が浮かびました。

そこでリリースしたアプリをベースに1日2個、ある分野に特化した2chまとめアプリを開発しリリースしました。

f:id:tsurutan:20160823204701p:plain

順調に開発しリリースしていたのですが、「声優まとめ速報」という9個目のアプリをリリースした後にこのようなメールが届きました。

f:id:tsurutan:20160823205427p:plain

「!?!?!?!?!?!?!?」

最初このメールが来た時、何が何だか分かりませんでした。

このメールにはどうやらyoutubeがバックグラウンド再生される実装がなされているためポリシーに違反するということだったらしい。しかし、そのような機能は実装していないし。。 WebViewを使っているので、そこからyoutubeを再生してバックグラウンド再生されてしまうのだろうか?そして、どうしてこのアプリだけ承認されなかったのか?(他のアプリは全く同じコードで書かれているが、無事リリースされている)

などなど色々と疑問をgoogleに申し出してみたのですが、googleさんからは同じ内容を記されたテンプレートメールが返ってきました。

これはダメだと諦めやや疑問を残しながらも、作業を進め次々とアプリを量産していきました。

そして12個目のアプリ「ゲームまとめ速報」をplay storeにあげた後再びgoogleさんから

f:id:tsurutan:20160823210120p:plain

露骨な性表現を含むコンテンツに関するポリシーに違反をしたためアプリを停止した旨が書かれたメールが届きました。

一応、前回違反を停止されたのを教訓に性表現を含むコンテンツを表示しないように厳選したサイトを登録していたのですが。。

何度アプリを見ても、そのような表現を持ったページにはいかないのに。。

正直このアプリが違反になってしまうと現在リリースされている他のアプリも対象になってしまいます。

そして、アプリを3個以上停止されてしまうと、アカウントも停止されてしまいます。

そう、今私は2個アプリを停止されています。

リーチです。

現在リリースしているアプリで割と利益が出ていたのですが、アカウント停止を喰らうとこのアプリも停止されてしまいます。

これはヤバイと考えた私は、汗水流して量産したアプリケーションを全て公開停止にしました。

勿体なかったのですが、やはりアカウント停止を避けるためには仕方ありません。

そしてこれから私は「あと一回違反するとアカウント停止」というナイフを突きつけられながらアプリを開発するのです。

これに懲りてアプリを大量生産することを辞めます。

そして、今回頻繁にアプリを開発しリリースをしていたことでgoogleさんに目をつけられるようになった気がします。

アプリを絞って地道に開発していった方が精神的にも良いということを身を持って感じました。