2011/11/01

[Java] 讀取 App Store 的 Sales and Trends Report

參考文件: iTunes Connect Sales and Trends Guide (PDF)
在第三章 [Downloading, Reading and Understanding Sales and Trends Data] 有提到一個 Auto-Ingest Tool.
因為此 class 可在網路上找到 Source Code, 所以就能瞭解此程式到 App Store 抓 Report 的做法.
此程式的關鍵就在於 Apple 有放出一個 URL (https://reportingitc.apple.com/autoingestion.tft) 讓需要的人可透過簡單的 HTTP POST 來取得一個 .gz 檔.
取得此 .gz 檔後, 可再自行解壓縮以得到一個 .txt 的 CSV 資料檔, 最後再將此 CSV 格式檔匯入資料庫中.
首先, 先知道要對此 URL 傳送的參數, 此參數說明一樣可在參考文件 3.2 節 [Parameters Definitions] 中找到.
以下簡略地整理參考文件中的參數說明並列表:

參數名
範例
說明
USERNAME xxxxx Apple ID
PASSWORD ***** 密碼
VNDNUMBER 8####### Vendor ID
TYPEOFREPORT Sales 目前僅提供輸入 Sales
DATETYPE Daily or Weekly 日報(14天內)或週報(13 週內)
REPORTTYPE Summary or Opt-In 報表類型
REPORTDATE YYYYMMDD 欲查詢的日期. 週報請給週日的日期
*要注意的是, 並非每個人都能使用參數 report_subtype 中的 Opt-In 功能, 所以要先到 web 上確認報表查詢介面中有無 [Opt-In Report] 這個項目可使用 (當然官網也是有說這個功能會在什麼情況下出現).
程式部分可以說不用參考到其他特別的 3rd 套件. 當然, 若是想用 HttpClient 包裝傳送的參數, 或是用套件來解 .gz 檔也都可以.
範例如下:
  1. 程式中需要 import 的 class 如下:
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.net.URL;
    import java.net.URLEncoder;
    import java.util.zip.GZIPInputStream;
    import javax.net.ssl.HttpsURLConnection;
    
  2. 取得 Report 的主程式:
    public static void main(String[] args) {
      String strAppleID = "xxxxx";
      String strPassword = "*****";
      String strVenderID = "8#######";
      String strTypeOfReport = "Sales";
      String strDateType = "Daily";
      String strReportType = "Summary";
      String strReportDate = "20111030";
      HttpsURLConnection objConnection = null;
      try {
        // 組合 POST 的參數
        StringBuffer sbParameter = new StringBuffer("USERNAME=").append(URLEncoder.encode(strAppleID, "UTF-8"));
        sbParameter.append("&PASSWORD=").append(URLEncoder.encode(strPassword, "UTF-8"));
        sbParameter.append("&VNDNUMBER=").append(URLEncoder.encode(strVenderID, "UTF-8"));
        sbParameter.append("&TYPEOFREPORT=").append(URLEncoder.encode(strTypeOfReport, "UTF-8"));
        sbParameter.append("&DATETYPE=").append(URLEncoder.encode(strDateType, "UTF-8"));
        sbParameter.append("&REPORTTYPE=").append(URLEncoder.encode(strReportType, "UTF-8"));
        sbParameter.append("&REPORTDATE=").append(URLEncoder.encode(strReportDate, "UTF-8"));
        // 建立連線與設定 Request
        objConnection = (HttpsURLConnection)(new URL("https://reportingitc.apple.com/autoingestion.tft?").openConnection());
        objConnection.setRequestMethod("POST");
        objConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        objConnection.setDoOutput(true);
        // do POST
        OutputStreamWriter oswWriter = new OutputStreamWriter(objConnection.getOutputStream());
        oswWriter.write(sbParameter.toString());
        oswWriter.flush();
        oswWriter.close();
        if (objConnection.getHeaderField("ERRORMSG") != null) {
          // 如果有錯誤訊息產生, 輸出錯誤訊息
          System.out.println(objConnection.getHeaderField("ERRORMSG"));
        } else if (objConnection.getHeaderField("filename") != null) {
          // 如果有回傳檔案名稱, 進行檔案處理
          getFile(objConnection);
        }
      } catch (Exception ex) {
        ex.printStackTrace(System.out);
      }
      finally {
        // 回收 Connection
        if (objConnection != null) {
          objConnection.disconnect();
          objConnection = null;
        }
      }
    }
    
  3. 在 Step2 中, 我將處理檔案的部分另外寫一個 getFile() 的函式以方便處理. 參考如下:
    private static void getFile(HttpsURLConnection objConnection)
        throws IOException {
      String strFileName = objConnection.getHeaderField("filename");
      // 設定 .gz 檔案的輸出位置
      String strGzFileName = "d:\\" + strFileName;
      int i = 0;       
      BufferedInputStream bisInput = new BufferedInputStream(objConnection.getInputStream());
      BufferedOutputStream bosOutput = new BufferedOutputStream(new FileOutputStream(strGzFileName));
      byte[] arrayOfByte = new byte[1024];
    
      while ((i = bisInput.read(arrayOfByte)) != -1) {
        bosOutput.write(arrayOfByte, 0, i);
      }
      bisInput.close();
      bosOutput.close();
      // 設定 解壓後的 .txt 檔案的輸出位置
      String strCVSFileName = "d:\\" + strFileName.substring(0, strFileName.length() - 3);
      GZIPInputStream inGZip = new GZIPInputStream(new FileInputStream(strGzFileName));
      FileOutputStream outTxt = new FileOutputStream(strCVSFileName);
      while ((i = inGZip.read(arrayOfByte)) != -1) {
        outTxt.write(arrayOfByte, 0, i);
      }
      inGZip.close();
      outTxt.close();
    }
    
  4. 若執行無誤, 應該會產生兩個檔案,  一個是下載回來的 .gz 檔, 一個是解壓縮後的 .txt 檔.
最後整理一下可能出現的錯誤訊息:
  • 當 Apple ID 或 password 輸入錯誤時: Error :Your Apple ID or password was entered incorrectly.
  • 當 Vender ID 輸入錯誤時: Please enter a valid vendor number.
  • 當 REPORTTYPE 輸入錯誤時(包含無 Opt-In 但卻指定 Opt-In): Auto ingestion is not available for this selection.
  • 當設定 DATETYPE 為 Weekly, 但日期卻指定錯誤或超過有效期: Weekly reports are available only for past 13 weeks, please enter a weekend date within past 13 weeks.
  • 當設定 DATETYPE 為 Daily, 但日期卻指定錯誤或超過有效期: Daily reports are available only for past 14 days, please enter a date within past 14 days.

沒有留言: