iOS/Androidアプリのレビューを取得

App Store、Google Playにユーザーが書いたアプリのレビューを取得する方法を記述。

# App Store上のiOSアプリのレビュー

RSSから取得できる。

## 手順

### iTunes Connect でURLを確認

RSSのURLは形式が決まっている模様だが公式のドキュメントは見つからない。
一応iTunes ConnectでURLを確認できる。

1. iTunes Connect からアプリを選択。
2. アクティビティ を選択。
3. 評価とレビュー を選択。
4. RSSアイコンを選択し、RSSリンクをコピー。

### URLの形式

`https://itunes.apple.com/COUNTRYCODE/rss/customerreviews/id=APPID/sortBy=mostRecent/xml`

- COUNTRYCODE: 国のコード。例：`jp`
- APPID: アプリのID。例：`284882215`

### URL一例

例：FacebookアプリのレビューのRSSのURL：
`https://itunes.apple.com/jp/rss/customerreviews/id=284882215/sortBy=mostRecent/xml`

### RSSの内容

Atom形式（XML）のRSSデータになっている。
先頭`entry`はアプリ自体の情報が入っているので、2個目以降の`entry`がレビュー内容になる。

```xml:FacebookアプリのRSS
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:im="http://itunes.apple.com/rss" xml:lang="ja">
	<id>https://itunes.apple.com/jp/rss/customerreviews/id=284882215/sortBy=mostRecent/xml</id>
	<title>iTunes Store: カスタマーレビュー</title>
	<updated>2017-02-28T23:11:37-07:00</updated>
	<link rel="alternate" type="text/html" href="https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewGrouping?cc=jp&amp;id=1000" />
	<link rel="self" href="https://itunes.apple.com/jp/rss/customerreviews/id=284882215/sortBy=mostRecent/xml" />
	<link rel="first" href="https://itunes.apple.com/jp/rss/customerreviews/page=1/id=284882215/sortby=mostrecent/xml?urlDesc=/customerreviews/id=284882215/sortBy=mostRecent/xml" />
	<link rel="last" href="https://itunes.apple.com/jp/rss/customerreviews/page=10/id=284882215/sortby=mostrecent/xml?urlDesc=/customerreviews/id=284882215/sortBy=mostRecent/xml" />
	<link rel="previous" href="https://itunes.apple.com/jp/rss/customerreviews/page=1/id=284882215/sortby=mostrecent/xml?urlDesc=/customerreviews/id=284882215/sortBy=mostRecent/xml" />
	<link rel="next" href="https://itunes.apple.com/jp/rss/customerreviews/page=2/id=284882215/sortby=mostrecent/xml?urlDesc=/customerreviews/id=284882215/sortBy=mostRecent/xml" />
	<icon>http://itunes.apple.com/favicon.ico</icon>
	<author>
		<name>iTunes Store</name>
		<uri>http://www.apple.com/jp/itunes/</uri>
	</author>
	<rights>Copyright 2008 Apple Inc.</rights>
	<entry>
		<updated>2017-02-28T23:11:37-07:00</updated>
		<id im:id="284882215" im:bundleId="com.facebook.Facebook">https://itunes.apple.com/jp/app/facebook/id284882215?mt=8&amp;uo=2</id>
		<title>Facebook - Facebook, Inc.</title>
		<im:name>Facebook</im:name>
		<link rel="alternate" type="text/html" href="https://itunes.apple.com/jp/app/facebook/id284882215?mt=8&amp;uo=2" />
		<im:contentType term="Application" label="アプリケーション" />
		<category im:id="6005" term="Social Networking" scheme="https://itunes.apple.com/jp/genre/ios-%E3%82%BD%E3%83%BC%E3%82%B7%E3%83%A3%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AD%E3%83%B3%E3%82%B0/id6005?mt=8&amp;uo=2" label="ソーシャルネットワーキング" />
		<im:artist href="https://itunes.apple.com/jp/developer/facebook-inc/id284882218?mt=8&amp;uo=2">Facebook, Inc.</im:artist>
		<im:price amount="0.00000" currency="JPY">入手</im:price>
		<im:image height="53">http://is1.mzstatic.com/image/thumb/Purple122/v4/27/94/74/2794747d-f2f1-2efa-b128-9e7269279037/mzl.inxlxixd.png/53x53bb-85.png</im:image>
		<im:image height="75">http://is5.mzstatic.com/image/thumb/Purple122/v4/27/94/74/2794747d-f2f1-2efa-b128-9e7269279037/mzl.inxlxixd.png/75x75bb-85.png</im:image>
		<im:image height="100">http://is1.mzstatic.com/image/thumb/Purple122/v4/27/94/74/2794747d-f2f1-2efa-b128-9e7269279037/mzl.inxlxixd.png/100x100bb-85.png</im:image>
		<rights>&amp;#169; Facebook, Inc.</rights>
		<im:releaseDate label="2008年7月11日 ">2008-07-11T00:00:00-07:00</im:releaseDate>
		<content type="html">省略</content>
	</entry>
	<entry>
		<updated>2017-02-28T00:47:00-07:00</updated>
		<id>1554278848</id>
		<title>はぁ？なめてんの？</title>
		<content type="text">てかまず登録させてくれない。【エラー 現在、ユーザ登録が処理できません。しばらくしてからもう1度実行してください】…はぁ？そのまま６ヵ月待ってるんですけど？ 速く登録させて ☆星一つも付けたくない&#x1f44e;&#x1f3fb;&#x1f44e;&#x1f3fb;</content>
		<im:contentType term="Application" label="アプリケーション" />
		<im:voteSum>0</im:voteSum>
		<im:voteCount>0</im:voteCount>
		<im:rating>1</im:rating>
		<im:version>81.0</im:version>
		<author>
			<name>&#x1f49e;&#x1f34b;Lemon&#x1f34b;&#x1f49e;＆❤️&#x1f36b;Kitkat&#x1f36b;❤️</name>
			<uri>https://itunes.apple.com/jp/reviews/id539129390</uri>
		</author>
		<link rel="related" href="https://itunes.apple.com/jp/review?id=284882215&amp;type=Purple%20Software" />
		<content type="html">省略</content>
	</entry>

	<entry>
...レビューごと
	</entry>
...
</feed>
```

## サンプルスクリプト

PHPでレビューを取得する一例。

```php:PHPでレビュー取得関数一例
<?php

mb_regex_encoding("UTF-8");
date_default_timezone_set('Asia/Tokyo');
set_time_limit(1200);

define('APPSTORE_REVIEWS_UPDATE', 'update');
define('APPSTORE_REVIEWS_ENTRIES', 'entries');

define('APPSTORE_REVIEW_TITLE', 'title');
define('APPSTORE_REVIEW_ID', 'id');
define('APPSTORE_REVIEW_UPDATE', 'update');
define('APPSTORE_REVIEW_CONTENT', 'content');
define('APPSTORE_REVIEW_RATING', 'rating');
define('APPSTORE_REVIEW_VERSION', 'version');
define('APPSTORE_REVIEW_AUTHOR', 'author');

function appstore_get_reviews($countryCode, $appId)
{
	$url = 'https://itunes.apple.com/' . $countryCode . '/rss/customerreviews/id=' . $appId . '/sortBy=mostRecent/xml';
	$curl = curl_init($url);
	curl_setopt($curl, CURLOPT_FRESH_CONNECT, TRUE);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
	curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 60);
	$result = curl_exec($curl);
	curl_close($curl);
	if ($result === FALSE)
		return NULL;
	
	$reviews = [];
	$entryArray = [];

	$rootNode = new SimpleXMLElement($result);
	$entriesNode = $rootNode->entry;
	$entryCount = count($entriesNode);
	for ($entryIndex = 1; $entryIndex < $entryCount; $entryIndex++)
	{
		$entryNode = $entriesNode[$entryIndex];
		$entry = [];
		$entry[APPSTORE_REVIEW_ID] = (string)$entryNode->id;
		$entry[APPSTORE_REVIEW_UPDATE] = new DateTime((string)$entryNode->updated);
		$entry[APPSTORE_REVIEW_TITLE] = (string)$entryNode->title;
		$entry[APPSTORE_REVIEW_CONTENT] = '';
		
		if (is_array($entryNode->content))
		{
			foreach ($entryNode->content as $contentNode)
			{
				if ($contentNode['type'] == 'text')
				{
					$entry[APPSTORE_REVIEW_CONTENT] = (string)$entryNode->content[0];
					break;
				}
			}
		}
		else if ($entryNode->content['type'] == 'text')
		{
			$entry[APPSTORE_REVIEW_CONTENT] = (string)$entryNode->content;
		}
		
		$imNodes = $entryNode->children('im', TRUE);
 		$entry[APPSTORE_REVIEW_RATING] = (string)$imNodes->rating;
 		$entry[APPSTORE_REVIEW_VERSION] = (string)$imNodes->version;
 		$entry[APPSTORE_REVIEW_AUTHOR] = (string)$entryNode->author->name;
		
		array_push($entryArray, $entry);
	}

	$reviews[APPSTORE_REVIEWS_UPDATE] = new DateTime((string)$rootNode->updated);
	$reviews[APPSTORE_REVIEWS_ENTRIES] = $entryArray;

	return $reviews;
}
```


# Google Play上のAndroidアプリのレビュー

Developer Console からCSV形式の月次レポートが Google Cloud Storage に保存される。
記載されたレビューがこのレポートに反映されるのは1日〜2日後くらい。
このレポートを`gsutil`でダウンロードできる。

## 手順

### レポートのURI取得
Developer Console の `レポート` > `レビュー` にて、アプリを選択すると、そのアプリのURIが表示される。
例： `gs://pubsite_prod_rev_123123123132123/reviews`

### gsutilをインストール
`gsutil`は Google Cloud SDK に含まれている。
Google Cloud SDK はこちらからインストールできる：
　https://cloud.google.com/sdk/downloads

インストール手順は

```bash
$ gcloud init
```
まで行えば使えるようになる。

Google Cloud SDK を展開したディレクトリに対して`bin/gsutil`が`gsutil`になるので、環境変数PATHに`bin`の絶対パスを追加しておくと便利。

### gsutilでレポートを取得

#### レポートの一覧を表示する

Google Cloud Storage に上がっているレポートの一覧は`gsutil ls`でリスティングできる。

```bash:レポート一覧
$ gsutil ls gs://pubsite_prod_rev_123123123132123/reviews
gs://pubsite_prod_rev_123123123132123/reviews/reviews_jp.runo.testapp_201605.csv
gs://pubsite_prod_rev_123123123132123/reviews/reviews_jp.runo.testapp_201606.csv
# （表略）
```

#### レポートをダウンロードする

Google Cloud Storage に上がっているレポートの一覧は`gsutil cp`でコピーできる。

```bash:レポートを/tmp/にコピーする例
$ gsutil cp gs://pubsite_prod_rev_123123123132123/reviews/reviews_jp.runo.testapp_201605.csv /tmp/
```
レポートの形式はUTF16のCSV。

#### レポートの内容

```text:CSV（UTF16）の先頭2行の例
Package Name,App Version Code,App Version Name,Reviewer Language,Device,Review Submit Date and Time,Review Submit Millis Since Epoch,Review Last Update Date and Time,Review Last Update Millis Since Epoch,Star Rating,Review Title,Review Text,Developer Reply Date and Time,Developer Reply Millis Since Epoch,Developer Reply Text,Review Link
jp.runo.testapp,33,1.11.5,ja,SH-04G,2016-07-31T05:29:36Z,2976903,2016-07-31T05:30:53Z,3053851,3,バグ,アプリがバグってます,,,,https://play.google.com/apps/publish?...

...
```

## サンプルスクリプト

PHPでレビューを取得する一例。

```php:PHPでレビュー取得関数一例
<?php

mb_regex_encoding("UTF-8");
date_default_timezone_set('Asia/Tokyo');
set_time_limit(1200);

function gsutil_command($command)
{
	$line = exec('$HOME/google-cloud-sdk/bin/gsutil ' . $command . ' 2>&1', $output, $exitcode);
	if ($exitcode != 0)
	{
		echo("gsutil: returned $exitcode\n");
		var_dump($output);
	}
	return $exitcode;
}

function googleplay_getreviews($bucketId, $packageName, $year, $month)
{
	$inputPath = sprintf('gs://%s/reviews/reviews_%s_%s%02d.csv', $bucketId, $packageName, $year, $month);

	$outputPath = tempnam("/tmp", "reviews");

	 if (gsutil_command('cp ' . $inputPath . ' ' . $outputPath) != 0)
	 	return NULL;

	$contentUtf16 = @file_get_contents($outputPath);
	@unlink($outputPath);

	$contentUtf8 = mb_convert_encoding($contentUtf16, 'UTF-8', 'UTF-16');
	$outputPath = tempnam("/tmp", "reviews");
	@file_put_contents($outputPath, $contentUtf8);
	
	// 読み込む
	$result = [];
	$file = new SplFileObject($outputPath);
	$file->setFlags(SplFileObject::READ_CSV);
	$header = TRUE;
	foreach ($file as $line)
	{
		if ($header) // 先頭行だけスキップ
		{
			$header = FALSE;
			continue;
		}
		if (!isset($line[15])) // 不完全なデータ
			continue;

		array_unshift($result, $line);
	} 

	@unlink($outputPath);
	return $result;
}
```

# 参考

- [iOSレビューを監視する方法 - Stack Overflow](http://stackoverflow.com/questions/1090282/api-to-monitor-iphone-app-store-reviews)
- [月次レポートのダウンロードと書き出し - Google Play デベロッパー ヘルプ](https://support.google.com/googleplay/android-developer/answer/6135870?hl=ja)