IbisAppSales
前のエントリ(これ)で、書いたiTunes Connectに接続してスクレイピングして、iPhoneのダウンロード数をダウンロードするプログラムが書けました。
GPLで公開するので、使ってください。
インストール
- Apache HttpClientのダウンロード
- このページから、HttpClient 4.0 (GA)>Binary with dependenciesをダウンロード
- Eclipseで下記IbisAppSales.javaをソースとしたJavaアプリケーションプロジェクトを作成
- プロジェクトの設定で、コンパイラを1.6に設定
- プロジェクトの設定のビルドパスのライブラリで、外部JARとして、HttpClientを展開したフォルダのlibフォルダにある、httpcore-4.0.1.jar、httpclient-4.0.jar、commons-logging-1.1.1.jarの3つを追加
- ビルド
使い方
コマンドライン引数で、UserIDとPasswordを渡すと、iTunes ConnectのSales/Trend Reportの最新のDailyレポートがダウンロードされて、gz展開されます。
IbisAppSales.java
import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.zip.GZIPInputStream; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.params.ClientPNames; import org.apache.http.client.params.CookiePolicy; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; /* ファイル概要: * Created on 2009/07/10 by ibis inc. Eiji Kamiya * Licence : GPL */ /** * クラス概要: * iTunes Connectに接続してSales/Trend Reportの最新のDailyレポートを * ダウンロードするコマンド. * @author ibis inc. Eiji Kamiya */ public class IbisAppSales { static final String BASE_URL="https://itunesconnect.apple.com"; static final String LOGIN_PAGE_URL="/WebObjects/iTunesConnect.woa"; DefaultHttpClient httpClient; HttpContext httpContext; /** * @param args */ public static void main(String[] args) { if(args.length<2){ System.err.println("Usage : java IbisAppSales [UserID] [Password]"); return; } IbisAppSales app=new IbisAppSales(); app.run(args[0],args[1]); } //コンストラクタ public IbisAppSales(){ httpClient = new DefaultHttpClient(); httpContext=new BasicHttpContext(); httpClient.getParams().setParameter( ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY); } //メイン void run(String userId, String password){ try { String html=openLoginPage(); html=clickSignIn(html,userId,password); html=clickTrendReport(html); html=refreshToReport(html); html=selectDailyOption(html); html=clickDownload(html); System.out.println("Success!"); } catch (Exception e) { e.printStackTrace(); } } //ログインページ表示 private String openLoginPage() throws IOException { return doGet(BASE_URL+LOGIN_PAGE_URL); } //ログインページ //ログインする private String clickSignIn(String html,String userId,String password) throws IOException { //FROMのaction先を探す String action=findValue(html,"<form name=\"appleConnectForm\" method=\"post\" action=\""); List<NameValuePair> param = new ArrayList <NameValuePair>(); param.add(new BasicNameValuePair("theAccountName", userId)); param.add(new BasicNameValuePair("theAccountPW", password)); return doPost(BASE_URL+action,param); } //メインメニューページ //Trend Reportリンクをクリックする private String clickTrendReport(String html) throws IOException { String link=findValue(html, "<td valign=\"top\" align=\"center\"><a href=\""); return doGet(BASE_URL+link); } //Trend Reportクリック後のクッションページ //リフレッシュする private String refreshToReport(String html) throws IOException{ String link=findValue(html,"<META HTTP-EQUIV=\"refresh\" Content=\"0;URL="); return doGet(link); } //レポートオプション選択ページ //Report PeriodプルダウンでDailyを選択 private String selectDailyOption(String html) throws IOException { //FROMのaction先を探す String action=findValue(html,"<form method=\"post\" name=\"frmVendorPage\" action=\""); //input hidden wosidを探す String wosid=findValue(html,"<input type=\"hidden\" name=\"wosid\" value=\""); List<NameValuePair> param = new ArrayList <NameValuePair>(); param.add(new BasicNameValuePair("wosid", wosid)); //hidden wosid param.add(new BasicNameValuePair("11.7","Summary")); //Report Typeプルダウン=Summary param.add(new BasicNameValuePair("11.9", "Daily")); //Report Periodプルダウン=Daily param.add(new BasicNameValuePair("hiddenDayOrWeekSelection","Daily")); param.add(new BasicNameValuePair("hiddenSubmitTypeName", "ShowDropDown")); String baseUrl=httpContext.getAttribute("http.target_host").toString(); return doPost(baseUrl+action,param); } //レポートオプション選択ページ //ダウンロードボタンクリック private String clickDownload(String html) throws IOException { //FROMのaction先を探す String action=findValue(html,"<form method=\"post\" name=\"frmVendorPage\" action=\""); //input hidden wosidを探す String wosid=findValue(html,"<input type=\"hidden\" name=\"wosid\" value=\""); //日付選択プルダウンの先頭の日付を得る String date=findValue(html,"name=\"11.11.1\"><option value=\""); List<NameValuePair> param = new ArrayList <NameValuePair>(); param.add(new BasicNameValuePair("wosid", wosid)); //hidden wosid param.add(new BasicNameValuePair("11.7","Summary")); //Report Typeプルダウン=Summary param.add(new BasicNameValuePair("11.9", "Daily")); //Report Periodプルダウン=Daily param.add(new BasicNameValuePair("11.11.1", date)); //Dayプルダウン param.add(new BasicNameValuePair("hiddenDayOrWeekSelection","Daily")); param.add(new BasicNameValuePair("hiddenSubmitTypeName", "Download")); param.add(new BasicNameValuePair("download", "Download")); //Submitボタン Download String baseUrl=httpContext.getAttribute("http.target_host").toString(); return doPost(baseUrl+action,param); } //GETリクエストを投げる private String doGet(String url) throws IOException { System.out.println("GET "+url); HttpGet httpGet = new HttpGet(url); HttpResponse response = httpClient.execute(httpGet,httpContext); return receiveResponse(response); } //POSTリクエストを投げる private String doPost(String url,List<NameValuePair> param) throws ClientProtocolException, IOException { System.out.println("POST "+url); HttpPost httpPost = new HttpPost(url); httpPost.setEntity(new UrlEncodedFormEntity(param, HTTP.UTF_8)); HttpResponse response = httpClient.execute(httpPost,httpContext); return receiveResponse(response); } //レスポンスを受け取った後の処理 private String receiveResponse(HttpResponse response) throws IOException{ String ret=""; HttpEntity entity = response.getEntity(); System.out.println("Response Status: " + response.getStatusLine()); if(entity==null){ return ""; } String contentType=response.getFirstHeader("Content-Type").getValue(); contentType=contentType.toLowerCase(); if(contentType.startsWith("text/html")){//HTMLのとき ret=EntityUtils.toString(entity); System.out.println(ret); } else { Header disposition=response.getFirstHeader("Content-Disposition"); if(disposition!=null){//Content-Dispositionがあるとき String fileName=disposition.getValue(); if(fileName.startsWith("filename=")){ fileName=fileName.substring(9);//filename=をカット } InputStream in=null; if(contentType.equalsIgnoreCase("application/x-gzip")){//GZIPのとき in=new GZIPInputStream(entity.getContent());//展開する if(fileName.endsWith(".gz")){//拡張子が.gzのとき fileName=fileName.substring(0,fileName.length()-3); } } else { in=entity.getContent(); } System.out.println("Download "+fileName); save(in,fileName); } } return ret; } //InputStreamを読んで、ファイルとして保存する private void save(InputStream in, String fileName) throws IOException{ BufferedOutputStream bos=null; byte[] buf=new byte[4096]; int len; try { bos=new BufferedOutputStream( new FileOutputStream(fileName)); while((len=in.read(buf))!=-1){ bos.write(buf,0,len); } } finally { if(bos!=null){ try{ bos.close(); } catch(IOException e){} } } } //HTMLをスクレイピングして、targetの次の文字から"までを取得 private String findValue(String html,String target) throws IOException{ int idx=html.indexOf(target); if(idx==-1){ throw new IOException("Can't find "+target); } int idx2=html.indexOf("\"", idx+target.length()); if(idx2==-1){ throw new IOException("Can't find \""); } return html.substring(idx+target.length(),idx2); } }
IbisAppSales.bat
REM USAGE IbisAppSales.bat [UserId] [Password] java -classpath ../../lib/httpcomponents-client-4.0/lib/httpclient-4.0.jar;../../lib/httpcomponents-client-4.0/lib/httpcore-4.0.1.jar;../../lib/httpcomponents-client-4.0/lib/commons-logging-1.1.1.jar;. IbisAppSales %1 %2
私は、この後、ダウンロードしたtxtファイルをパースして、DBサーバにINSERTするコードを書く予定。これはアイビス社内のバックヤードシステムに強く依存するのでそのコードは公開しません。ダウンロードしたレポートをメールで送るなども便利でしょう。各自テキトーに改良してください。