如何从Google Play商店获取应用市场版本信息?

51
如何从Google Play商店中获取应用程序版本信息,以便在Play Store应用程序更新时提示用户强制/推荐更新应用程序,即在用户使用旧版本应用程序的情况下。我已经查看了andorid-market-api,但这不是官方方式,并且需要从Google进行OAuth登录身份验证。我还查看了Android Query,它提供了应用程序内版本检查,但在我的情况下无法正常工作。
我发现以下两个替代方法:
  • 使用服务器API存储版本信息
  • 使用Google标签并在应用程序中访问它,这不是首选方法。
是否有其他简单的方法可以实现?

2
我更喜欢你的第一种方式... - Niranj Patel
@CapDroid 还有其他方法可以做到吗?特别是在独立应用程序的情况下。 - ravidl
我不知道 :( 大多数时候我都是按照你的第一种方式 :) - Niranj Patel
采取第一种方法虽然很有趣,但如果还有其他选项的话,那就值得去了解一下。这是个好问题! - Konstantin Loginov
1
可能是在Android市场上以编程方式检查我的应用程序版本的重复。 - Ciro Santilli OurBigBook.com
显示剩余5条评论
22个回答

51
我建议不要使用库,而是创建一个新的类。
public class VersionChecker extends AsyncTask<String, String, String>{

String newVersion;

@Override
protected String doInBackground(String... params) {

    try {
        newVersion = Jsoup.connect("https://play.google.com/store/apps/details?id=" + "package name" + "&hl=en")
                .timeout(30000)
                .userAgent("Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6")
                .referrer("http://www.google.com")
                .get()
                .select("div.hAyfc:nth-child(4) > span:nth-child(2) > div:nth-child(1) > span:nth-child(1)")
                .first()
                .ownText();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return newVersion;
}
  1. 在您的活动中:

    VersionChecker versionChecker = new VersionChecker();
    String latestVersion = versionChecker.execute().get();

这就是全部。


3
这是一个hack。但它是一个非常好的hack,不需要太多的努力,特别是考虑到Google让人难以使用他们的API。+1. :) - Brad Montgomery
2
不错的技巧,但自从这个出现了就不再必要了:https://github.com/googlesamples/android-play-publisher-api/tree/master/v2/java - Sandro Wiggers
4
这只是坏了。如果您使用正则表达式,可以使用<div[^>]*?>Current Version</div><div><span[^>]*?>(.*?)</span></div> - vincent
1
对于Android用户,在Gradle文件中添加以下依赖项:compile 'org.jsoup:jsoup:1.11.3'。 - Vrajesh
2
这也会通过用户的移动数据连接下载大量HTML数据 (截至今天为止,达到了780千字节)。并不是每个人都在加利福尼亚州并拥有无限的数据计划。这段代码不仅不够健壮,而且对用户来说也是一个巨大的麻烦。 - ge0rg
显示剩余15条评论

21

当前解决方案

虽然这种方法非常hacky,但是这是我能够快速想出来的最好的解决方法。在我尝试的所有应用程序中都似乎可以使用。这是一种PHP解决方案,但你可以为任何其他语言选择preg_match()正则表达式部分。

public function getAndroidVersion(string $storeUrl): string
{
    $html = file_get_contents($storeUrl);
    $matches = [];
    preg_match('/\[\[\[\"\d+\.\d+\.\d+/', $html, $matches);
    if (empty($matches) || count($matches) > 1) {
        throw new Exception('Could not fetch Android app version info!');
    }
    return substr(current($matches), 4);
}

2022年5月之前的解决方法(已不再适用)

使用PHP后端。这个方法已经运作了一年。似乎谷歌不经常更改他们的DOM。

public function getAndroidVersion(string $storeUrl): string
{
    $dom = new DOMDocument();
    $dom->loadHTML(file_get_contents($storeUrl));
    libxml_use_internal_errors(false);
    $elements = $dom->getElementsByTagName('span');

    $depth = 0;
    foreach ($elements as $element) {
        foreach ($element->attributes as $attr) {
            if ($attr->nodeName === 'class' && $attr->nodeValue === 'htlgb') {
                $depth++;
                if ($depth === 7) {
                    return preg_replace('/[^0-9.]/', '', $element->nodeValue);
                    break 2;
                }
            }
        }
    }
}

甚至更旧的版本(已经失效)

这里是jQuery版本,如果其他人需要版本号,可以使用它。

    $.get("https://play.google.com/store/apps/details?id=" + packageName + "&hl=en", function(data){
        console.log($('<div/>').html(data).contents().find('div[itemprop="softwareVersion"]').text().trim());
    });

我需要使用Ionic,但是当我使用它时遇到了一些CORS问题。 - Anuj
6
这非常脆弱。我怎么知道的?我接手的一个应用程序就是这样做的,今天开始严重崩溃。 - Drarok
我在这个命令的输出中没有看到 softwareVersion。它可能已经改变了。 - Gi0rgi0s
1
@Gi0rgi0s,这个已经有一段时间没有工作了。不过他们的DOM一直很稳定。我一直在解析DOM。目前正在寻找类:htlgb。 - Firze
感谢您提供的解决方案,由于Google在2022年5月更新了UI界面,您为我节省了很多时间。 - Martinedo
正则表达式用于 C#:var rx = new Regex(@"""\d+.\d+.\d", RegexOptions.Compiled); - Suchith

14

我怀疑请求应用程序版本的主要原因是提示用户进行更新。我不赞成抓取响应,因为这可能会在未来版本中破坏功能。

如果应用程序的最低版本为5.0,则可以按照文档https://developer.android.com/guide/app-bundle/in-app-updates实现应用内更新。

如果请求应用程序版本的原因不同,仍然可以使用appUpdateManager检索版本并进行任何想做的操作(例如将其存储在偏好设置中)。

例如,我们可以将文档片段修改为以下内容:

// Creates instance of the manager.
val appUpdateManager = AppUpdateManagerFactory.create(context)

// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo

// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
    val version = appUpdateInfo.availableVersionCode()
    //do something with version. If there is not a newer version it returns an arbitary int
}

最佳解决方案,这是获取应用程序更新信息的官方方式。 - amoljdv06

8
使用这段代码是完美运行的。
public void forceUpdate(){
    PackageManager packageManager = this.getPackageManager();
    PackageInfo packageInfo = null;
    try {
        packageInfo =packageManager.getPackageInfo(getPackageName(),0);
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    String currentVersion = packageInfo.versionName;
    new ForceUpdateAsync(currentVersion,TodayWork.this).execute();
}

public class ForceUpdateAsync extends AsyncTask<String, String, JSONObject> {

    private String latestVersion;
    private String currentVersion;
    private Context context;
    public ForceUpdateAsync(String currentVersion, Context context){
        this.currentVersion = currentVersion;
        this.context = context;
    }

    @Override
    protected JSONObject doInBackground(String... params) {

        try {
            latestVersion = Jsoup.connect("https://play.google.com/store/apps/details?id=" + context.getPackageName()+ "&hl=en")
                    .timeout(30000)
                    .userAgent("Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6")
                    .referrer("http://www.google.com")
                    .get()
                    .select("div.hAyfc:nth-child(3) > span:nth-child(2) > div:nth-child(1) > span:nth-child(1)")
                    .first()
                    .ownText();
            Log.e("latestversion","---"+latestVersion);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return new JSONObject();
    }

    @Override
    protected void onPostExecute(JSONObject jsonObject) {
        if(latestVersion!=null){
            if(!currentVersion.equalsIgnoreCase(latestVersion)){
                // Toast.makeText(context,"update is available.",Toast.LENGTH_LONG).show();
                if(!(context instanceof SplashActivity)) {
                    if(!((Activity)context).isFinishing()){
                        showForceUpdateDialog();
                    }
                }
            }
        }
        super.onPostExecute(jsonObject);
    }

    public void showForceUpdateDialog(){

        context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + context.getPackageName())));
    }

}

7

5

2022年的工作

JS示例(可移植到任何其他语言):

import { JSDOM } from 'jsdom'; 
import axios from 'axios';

const res = await axios('https://play.google.com/store/apps/details?id=com.yourapp');
const dom = new JSDOM(res.data);
const scripts = Array.from(dom.window.document.querySelectorAll('script'));
const script = scripts.find(s => s.textContent && s.textContent.includes('/store/apps/developer'));
const versionStringRegex = /"[0-9]+\.[0-9]+\.[0-9.]+"/g;
const matches = script.textContent.match(versionStringRegex);
const match = matches[0];
const version = match.replace(/"/g, '');

console.log(version); // '1.2.345'

不幸的是,应用商店最近改变了它们的DOM布局,以至于版本号只有在您打开模态框时才可见。

然而,在其中一个脚本标签中嵌入的元数据中,终将呈现到该模态框中的数据确实存在。它没有以任何方式键入,而是仅嵌入在一个数组中,因此此解决方案依赖于同一附近的一些数据,这些数据不太可能改变。


我认为你的解决方案可以适用于新的Play Console更新,但我想要相同答案的Android本地代码。你能否提供给我Android本地代码,这样我就可以检查一下。 - ravi152
@cooper-maruyama 什么是versionStringRegex? - Larry Aasen
@ravi152 不,我打算在Flutter的Dart中复现这个。 - Larry Aasen
@Larry Aasen 好的,那请分享 Dart 代码,我会将其转换为 Android 原生代码。 - ravi152
目前textContent中包含'/store/apps/dev'而不是'/store/apps/developer'。 - poloapolo
显示剩余2条评论

4
除了使用JSoup,我们还可以通过模式匹配从Google Play商店获取应用程序版本。
为了匹配Google Play商店的最新模式,即 <div class="BgcNfc">Current Version</div><span class="htlgb"><div><span class="htlgb">X.X.X</span></div> 我们首先要匹配上述节点序列,然后从该序列中获取版本值。以下是相应的代码片段:
    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;
    }

我通过这种方式解决了这个问题。这也可以解决Google在PlayStore中最新的更改。希望能有所帮助。


如果您允许,我将发布使用AsynsTask的完整代码。 - Сергей
不依赖于“当前版本”这个词语更加健壮,因为用户可能有不同的区域设置偏好。 - Beeno Tung

3
这个解决方案的完整源代码:https://dev59.com/D1sX5IYBdhLWcg3wCLyx#50479184
import android.os.AsyncTask;
import android.support.annotation.Nullable;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class GooglePlayAppVersion extends AsyncTask<String, Void, String> {

    private final String packageName;
    private final Listener listener;
    public interface Listener {
        void result(String version);
    }

    public GooglePlayAppVersion(String packageName, Listener listener) {
        this.packageName = packageName;
        this.listener = listener;
    }

    @Override
    protected String doInBackground(String... params) {
        return getPlayStoreAppVersion(String.format("https://play.google.com/store/apps/details?id=%s", packageName));
    }

    @Override
    protected void onPostExecute(String version) {
        listener.result(version);
    }

    @Nullable
    private static String getPlayStoreAppVersion(String appUrlString) {
        String
              currentVersion_PatternSeq = "<div[^>]*?>Current\\sVersion</div><span[^>]*?>(.*?)><div[^>]*?>(.*?)><span[^>]*?>(.*?)</span>",
              appVersion_PatternSeq = "htlgb\">([^<]*)</s";
        try {
            URLConnection connection = new URL(appUrlString).openConnection();
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6");
            try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
                StringBuilder sourceCode = new StringBuilder();
                String line;
                while ((line = br.readLine()) != null) sourceCode.append(line);

                // Get the current version pattern sequence
                String versionString = getAppVersion(currentVersion_PatternSeq, sourceCode.toString());
                if (versionString == null) return null;

                // get version from "htlgb">X.X.X</span>
                return getAppVersion(appVersion_PatternSeq, versionString);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Nullable
    private static String getAppVersion(String patternString, String input) {
        try {
            Pattern pattern = Pattern.compile(patternString);
            if (pattern == null) return null;
            Matcher matcher = pattern.matcher(input);
            if (matcher.find()) return matcher.group(1);
        } catch (PatternSyntaxException e) {
            e.printStackTrace();
        }
        return null;
    }

}

使用方法:

new GooglePlayAppVersion(getPackageName(), version -> 
    Log.d("TAG", String.format("App version: %s", version)
).execute();

3

2022年我的Xamarin应用将采用C#解决方案。

var url = $"https://play.google.com/store/apps/details?id={_packageName}";
using (var request = new HttpRequestMessage(HttpMethod.Get, url))
{
   using (var handler = new HttpClientHandler())
   {
      using (var client = new HttpClient(handler))
      {
         using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead))
         {
            try
            {
               if (response.IsSuccessStatusCode)
               {
                  var content = response.Content == null ? null : await response.Content.ReadAsStringAsync();
                  var versionMatch = Regex.Match(content, @"\[\[""\d+.\d+.\d+""\]\]"); //look for pattern [["X.Y.Z"]]
                  if (versionMatch.Groups.Count == 1)
                  {
                     var versionMatchGroup = versionMatch.Groups.FirstOrDefault();
                     if (versionMatchGroup.Success)
                        return versionMatch.Value.Replace("[", "").Replace("]", "").Replace("\"", "");
                  }
               }
            }
            catch
            { }
         }
      }
   }
}
return null;

3

使用服务器API存储版本信息

就像你所说的那样。这是一种检测更新的简单方法。在每个API调用中传递您的版本信息。当Playstore更新时,在服务器上更改版本。一旦服务器版本高于已安装应用程序版本,您可以在API响应中返回状态代码/消息,该代码/消息可以被处理并显示更新消息。 如果您使用此方法,还可以像WhatsApp一样阻止用户使用非常旧的应用程序。

或者,您可以使用推送通知,这很容易做...另外


我正在寻找除了服务器API之外的解决方案。感谢您的回复! - ravidl
2
尝试使用此 Google 官方 API:https://github.com/googlesamples/android-play-publisher-api/tree/master/v2/java - Sandro Wiggers
如何在Cakephp 2.0中使用 - chintan mahant
每个API调用对我来说似乎都有些过度了。 - Ian Warburton
不要添加新的API调用。在API中添加版本信息。 参考 https://github.com/mindhaq/restapi-versioning-spring - shijin
但是如果您使用此方法,更新需要由Google团队进行审核,那么您如何知道何时更改API中的版本? - Lukas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接