Skip to content

Instantly share code, notes, and snippets.

@Higgs1
Last active October 19, 2016 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Higgs1/7c33128a38e4f7b4b28c to your computer and use it in GitHub Desktop.
Save Higgs1/7c33128a38e4f7b4b28c to your computer and use it in GitHub Desktop.
Super Basic Python 3 Google Play Downloader with Python-Requests
# Read title! Should be able to download any apk of any version, as long
# as it's free. Paid apps are not supported, even if you've paid for them
# (should be easy to implement, but I haven't bought any apps yet).
# PyPI "requests"
import requests
import io
# Step 0. Google Constants + Inputs
url_root = 'https://android.clients.google.com/'
# Enter your login details here
email = 'johnsmith@example.com'
password = 'Password'
android_id = 'FFFFFFFFFFFFFFFF'
# APK name you want to download here.
packname = 'com.google.android.gms'
# APK version you want, or None for latest.
version = None
def rbitshift(v, b):
return v >> b, v & (2**b - 1)
def read_uleint(s, l = 4):
b = s.read(l)
if not b:
raise EOFError
return int.from_bytes(b, 'little')
def read_ulelong(s):
return read_uleint(s, 8)
def read_uleb128(s):
c, d, v = 1, 0, 0
while c:
c, b = rbitshift(read_uleint(s, 1), 7)
v += b << d
d += 7
return v
def read_pb_bytes(s):
return s.read(read_uleb128(s))
pb_codecs = [
read_uleb128, # 0
read_ulelong, # 1
read_pb_bytes, # 2
None, None,
read_uleint # 5
]
def read_pb_key(s):
return rbitshift(read_uleb128(s), 3)
def read_pb_field(s):
k, t = read_pb_key(s)
return k, pb_codecs[t](s)
def parse_pb_dict(data):
data, d = io.BytesIO(data), {}
try:
while True:
d.update([read_pb_field(data)])
except EOFError:
pass
return d
def parse_pb_path(v, *path):
if len(path):
return parse_pb_path(parse_pb_dict(v)[path[0]], *path[1:])
return v
def parse_utf8(s):
return s.decode('UTF-8')
# Step 1. Login to Google
session = requests.Session()
data = session.post(
url_root + 'auth',
data = {
"Email": email,
"Passwd": password,
"service": 'androidmarket',
}).text
auth = dict(map(lambda s: s.split('='), data.split()))['Auth']
session.headers = {
'Authorization': 'GoogleLogin auth=' + auth,
'User-Agent': 'Android- (api=1,versionCode=8011000)',
'X-DFE-Device-Id': android_id,
}
# Step 2. Get Latest Version
if not version:
data = session.get(
url_root + 'fdfe/details',
params = {
'doc': packname
}).content
version = parse_pb_path(data, 1, 2, 4, 13, 1, 3)
# Step 3. Get Download URL + Auth
data = session.post(
url_root + 'fdfe/purchase',
data = {
'doc': packname,
'vc': version,
#'ot': 1, # not needed?
}).content
tmp = parse_pb_path(data, 1, 4, 39, 8)
url = parse_pb_path(tmp, 3)
mda = parse_pb_path(tmp, 5, 2)
# Step 4. Download APK file
apk = requests.get(
parse_utf8(url),
cookies = {
'MarketDA': str(int(mda))
}
).content
# 'apk' is now your apk file ready to read or save to file!
# Alternatively, open as a zip file to read:
import zipfile
zf = zipfile.ZipFile(io.BytesIO(apk))
@Higgs1
Copy link
Author

Higgs1 commented Oct 19, 2016

Currently no longer works - Google changed the interface slightly - the APK version number is no longer stored in 1/2/4/13/1/3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment