【问题标题】:Getting versionCode and VersionName from Google Play从 Google Play 获取 versionCode 和 VersionName
【发布时间】:2017-12-21 02:36:32
【问题描述】:

我正在寻找一种方法,如何通过 PC 中的 java 应用程序从 google play 获取应用程序 versionCode 和 VersionName 以及包名。 我已经看到:https://androidquery.appspot.com/ 但它不再工作了,https://code.google.com/archive/p/android-market-api/ 也开始出现问题并且停止工作,它需要设备 ID。 你能帮我一些简单的解决方案或API吗? 非常重要,我需要 versionCode 和 VersionName 并且 VersionName 通过解析 html google play 应用程序站点相对容易获得。 versionCode 很重要。

【问题讨论】:

  • 您的应用想要这个吗?还是 Play 商店中的任何应用?
  • 对于我目前工作的公司的应用程序。每个应用程序都使用不同的密钥进行签名。所以我实际上喜欢游戏商店中的任何应用程序..

标签: java android google-play


【解决方案1】:

没有官方的 Google Play API,Playstore 使用内部 protobuf API,该 API 未记录且未开放。恕我直言,你可以:

  • 使用对 API 进行逆向工程的开源库
  • 已提取此信息的废弃 apk 下载站点(很可能通过相同的 protobuf Google Play API)

请注意,有一个Google Play developer API,但您不能列出您的 apk、版本、应用程序。它主要用于管理应用分发、评论、编辑等。

Google play 内部 API

play-store-api Java library

此库使用 Google Play Store protobuf API(未记录和封闭的 API),并且需要电子邮件/密码来生成可重复使用的令牌以使用 API:

GplaySearch googlePlayInstance = new GplaySearch();

DetailsResponse response = googlePlayInstance.getDetailResponse("user@gmail.com",
        "password", "com.facebook.katana");

AppDetails appDetails = response.getDocV2().getDetails().getAppDetails();

System.out.println("version name : " + appDetails.getVersionString());
System.out.println("version code : " + appDetails.getVersionCode());

用这个方法:

public DetailsResponse getDetailResponse(String email,
                                         String password,
                                         String packageName) throws IOException, ApiBuilderException {
    // A device definition is required to log in
    // See resources for a list of available devices
    Properties properties = new Properties();
    try {
        properties.load(getClass().getClassLoader().getSystemResourceAsStream("device-honami" +
                ".properties"));
    } catch (IOException e) {
        System.out.println("device-honami.properties not found");
        return null;
    }
    PropertiesDeviceInfoProvider deviceInfoProvider = new PropertiesDeviceInfoProvider();
    deviceInfoProvider.setProperties(properties);
    deviceInfoProvider.setLocaleString(Locale.ENGLISH.toString());

    // Provide valid google account info
    PlayStoreApiBuilder builder = new PlayStoreApiBuilder()
            .setDeviceInfoProvider(deviceInfoProvider)
            .setHttpClient(new OkHttpClientAdapter())
            .setEmail(email)
            .setPassword(password);
    GooglePlayAPI api = builder.build();

    // We are logged in now
    // Save and reuse the generated auth token and gsf id,
    // unless you want to get banned for frequent relogins
    api.getToken();
    api.getGsfId();

    // API wrapper instance is ready
    return api.details(packageName);
}

device-honami.properties 是识别设备特征所需的设备属性文件。你有一些 device.properties 文件示例here

OkHttpClientAdapter可以找到here

用于运行此示例的依赖项:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
dependencies {
    compile 'com.github.yeriomin:play-store-api:0.19'
    compile 'com.squareup.okhttp3:okhttp:3.8.1'
}

废弃第三方 apk 下载站点

http://apk-dl.com

您可以从http://apk-dl.com(当然是非官方的)获取版本名称和版本代码,方法是使用jsoup 抓取页面以获得所需的包名称:

String packageName = "com.facebook.katana";

Document doc = Jsoup.connect("http://apk-dl.com/" + packageName).get();
Elements data = doc.select(".file-list .mdl-menu__item");

if (data.size() > 0) {
    System.out.println("full text : " + data.get(0).text());
    Pattern pattern = Pattern.compile("(.*)\\s+\\((\\d+)\\)");
    Matcher matcher = pattern.matcher(data.get(0).text());
    if (matcher.find()) {
        System.out.println("version name : " + matcher.group(1));
        System.out.println("version code : " + matcher.group(2));
    }
}

https://apkpure.com

另一种可能性是废弃https://apkpure.com

String packageName = "com.facebook.katana";

Elements data = Jsoup.connect("https://apkpure.com/search?q=" + packageName)
        .userAgent("Mozilla")
        .get().select(".search-dl .search-title a");

if (data.size() > 0) {

    Elements data2 = Jsoup.connect("https://apkpure.com" + data.attr("href"))
            .userAgent("Mozilla")
            .get().select(".faq_cat dd p");

    if (data2.size() > 0) {
        System.out.println(data2.get(0).text());

        Pattern pattern = Pattern.compile("Version:\\s+(.*)\\s+\\((\\d+)\\)");
        Matcher matcher = pattern.matcher(data2.get(0).text());
        if (matcher.find()) {
            System.out.println("version name : " + matcher.group(1));
            System.out.println("version code : " + matcher.group(2));
        }
    }
}

https://api-apk.evozi.com

另外,https://api-apk.evozi.com 有一个内部 JSON api,但是:

  • 有时它不起作用(返回Ops, APK Downloader got access denied when trying to download),主要用于非流行应用
  • 它具有防止抓取机器人的机制(在 JS 中使用随机变量名生成的随机令牌)

下面是返回版本名称和代码https://api-apk.evozi.comFWIW:

String packageName = "com.facebook.katana";

String data = Jsoup.connect("https://apps.evozi.com/apk-downloader")
        .userAgent("Mozilla")
        .execute().body();

String token = "";
String time = "";

Pattern varPattern = Pattern.compile("dedbadfbadc:\\s+(\\w+),");
Pattern timePattern = Pattern.compile("t:\\s+(\\w+),");

Matcher varMatch = varPattern.matcher(data);
Matcher timeMatch = timePattern.matcher(data);

if (varMatch.find()) {
    Pattern tokenPattern = Pattern.compile("\\s*var\\s*" + varMatch.group(1) + "\\s*=\\s*'(.*)'.*");
    Matcher tokenMatch = tokenPattern.matcher(data);

    if (tokenMatch.find()) {
        token = tokenMatch.group(1);
    }
}

if (timeMatch.find()) {
    time = timeMatch.group(1);
}

HttpClient httpclient = HttpClients.createDefault();
HttpPost httppost = new HttpPost("https://api-apk.evozi.com/download");

List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("t", time));
params.add(new BasicNameValuePair("afedcfdcbdedcafe", packageName));
params.add(new BasicNameValuePair("dedbadfbadc", token));
params.add(new BasicNameValuePair("fetch", "false"));
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));

HttpResponse response = httpclient.execute(httppost);

JsonElement element = new JsonParser().parse(EntityUtils.toString(response.getEntity()));
JsonObject result = element.getAsJsonObject();

if (result.has("version") && result.has("version_code")) {
    System.out.println("version name : " + result.get("version").getAsString());
    System.out.println("version code : " + result.get("version_code").getAsInt());
} else {
    System.out.println(result);
}

实施

您可以在直接与您的 Java 应用程序通信的后端实现它,这样,如果上述方法之一失败,您可以维护检索版本代码/名称的过程。

如果您只对自己的应用感兴趣,更简洁的解决方案是:

  • 设置一个存储您当前所有应用版本名称/版本代码的后端
  • 您公司中的所有开发人员/发布者都可以共享一个发布任务(gradle 任务),该任务将使用Google Play developer API 发布 apk,并且该 gradle 任务将包括调用您的后端来存储版本代码/版本名称条目该应用程序已发布。主要目标是自动化整个发布过程,并将应用元数据存储在您身边。

【讨论】:

  • 非常感谢您的回复,但有没有什么方法可以在不使用那些 3RD 派对网站的情况下做到这一点,或者您知道他们是如何做到的?
  • 非常感谢您的帮助,第一个解决方案完美地满足了我们的需求。再次感谢您!
【解决方案2】:

除了使用 JSoup,我们还可以通过模式匹配从 playStore 获取应用版本。

要匹配来自 google playstore 的最新模式,即 &lt;div class="BgcNfc"&gt;Current Version&lt;/div&gt;&lt;span class="htlgb"&gt;&lt;div&gt;&lt;span class="htlgb"&gt;X.X.X&lt;/span&gt;&lt;/div&gt; 我们首先必须匹配上面的节点序列,然后从上面的序列中获取版本值。下面是相同的代码sn-p:

    private String getAppVersion(String patternString, String inputString) {
        try{
            //Create a pattern
            Pattern pattern = Pattern.compile(patternString);
            if (null == pattern) {
                return null;
            }

            //Match the pattern string in provided string
            Matcher matcher = pattern.matcher(inputString);
            if (null != matcher && matcher.find()) {
                return matcher.group(1);
            }

        }catch (PatternSyntaxException ex) {

            ex.printStackTrace();
        }

        return null;
    }


    private String getPlayStoreAppVersion(String appUrlString) {
        final String currentVersion_PatternSeq = "<div[^>]*?>Current\\sVersion</div><span[^>]*?>(.*?)><div[^>]*?>(.*?)><span[^>]*?>(.*?)</span>";
        final String appVersion_PatternSeq = "htlgb\">([^<]*)</s";
        String playStoreAppVersion = null;

        BufferedReader inReader = null;
        URLConnection uc = null;
        StringBuilder urlData = new StringBuilder();

        final URL url = new URL(appUrlString);
        uc = url.openConnection();
        if(uc == null) {
           return null;
        }
        uc.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6");
        inReader = new BufferedReader(new InputStreamReader(uc.getInputStream()));
        if (null != inReader) {
            String str = "";
            while ((str = inReader.readLine()) != null) {
                           urlData.append(str);
            }
        }

        // Get the current version pattern sequence 
        String versionString = getAppVersion (currentVersion_PatternSeq, urlData.toString());
        if(null == versionString){ 
            return null;
        }else{
            // get version from "htlgb">X.X.X</span>
            playStoreAppVersion = getAppVersion (appVersion_PatternSeq, versionString);
        }

        return playStoreAppVersion;
    }

我通过这个解决了这个问题。希望对您有所帮助。

【讨论】:

    【解决方案3】:

    Jsoup 耗时太长,效率低下,简而言之就是模式匹配的简单方法:

    public class PlayStoreVersionChecker {
    
    public String playStoreVersion = "0.0.0";
    
    OkHttpClient client = new OkHttpClient();
    
    private String execute(String url) throws IOException {
        okhttp3.Request request = new Request.Builder()
                .url(url)
                .build();
    
        Response response = client.newCall(request).execute();
        return response.body().string();
    }
    
    public String getPlayStoreVersion() {
        try {
            String html = execute("https://play.google.com/store/apps/details?id=" + APPIDHERE!!! + "&hl=en");
            Pattern blockPattern = Pattern.compile("Current Version.*([0-9]+\\.[0-9]+\\.[0-9]+)</span>");
            Matcher blockMatch = blockPattern.matcher(html);
            if(blockMatch.find()) {
                Pattern versionPattern = Pattern.compile("[0-9]+\\.[0-9]+\\.[0-9]+");
                Matcher versionMatch = versionPattern.matcher(blockMatch.group(0));
                if(versionMatch.find()) {
                    playStoreVersion = versionMatch.group(0);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return playStoreVersion;
    }
    
    
    }
    

    【讨论】:

    • 什么是'okhttp3'?
    • @DeboraCarnevali okhttp3 是一个用于 http 请求的 android 库,非常简单
    • APPID 应该写什么?是这个应用的包名吗?
    【解决方案4】:
    public class Store {
    
        private Document document;
        private final static String baseURL = "https://play.google.com/store/apps/details?id=";
    
        public static void main(String[] args) {
    
        }
    
        public Store(String packageName) {
            try {
                document = Jsoup.connect(baseURL + packageName).userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0").get();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    
        public String getTitle() {
            return document.select("h1.AHFaub > span").text();
        }
    
        public String getDeveloper() {
            return document.selectFirst("span.UAO9ie > a").text();
        }
    
        public String getCategory() {
            Elements elements = document.select("span.UAO9ie > a");
            for (Element element : elements) {
                if (element.hasAttr("itemprop")) {
                    return element.text();
                }
            }
            return null;
        }
    
        public String getIcon() {
            return document.select("div.xSyT2c > img").attr("src");
        }
    
        public String getBigIcon() {
            return document.select("div.xSyT2c > img").attr("srcset").replace(" 2x", "");
        }
    
        public List<String> getScreenshots() {
            List<String> screenshots = new ArrayList<>();
            Elements img = document.select("div.u3EI9e").select("button.Q4vdJd").select("img");
            for (Element src : img) {
                if (src.hasAttr("data-src")) {
                    screenshots.add(src.attr("data-src"));
                } else {
                    screenshots.add(src.attr("src"));
                }
            }
            return screenshots;
        }
    
        public List<String> getBigScreenshots() {
            List<String> screenshots = new ArrayList<>();
            Elements img = document.select("div.u3EI9e").select("button.Q4vdJd").select("img");
            for (Element src : img) {
                if (src.hasAttr("data-src")) {
                    screenshots.add(src.attr("data-srcset").replace(" 2x", ""));
                } else {
                    screenshots.add(src.attr("srcset").replace(" 2x", ""));
                }
            }
            return screenshots;
        }
    
        public String getDescription() {
            return document.select("div.DWPxHb > span").text();
        }
    
        public String getRatings() {
            return document.select("div.BHMmbe").text();
        }
    }
    

    进口

    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import org.jsoup.Jsoup;
    import org.jsoup.nodes.Document;
    import org.jsoup.nodes.Element;
    import org.jsoup.select.Elements;
    

    此脚本将返回

    类别(例如个性化) 开发者名称 应用程序图标 应用名称 屏幕截图(缩略图和完整预览) 说明

    您也可以查看完整源代码here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-26
      • 2015-02-07
      • 1970-01-01
      • 1970-01-01
      • 2017-10-12
      • 2012-11-08
      相关资源
      最近更新 更多