Skip to content

Instantly share code, notes, and snippets.

@arulrajnet
Last active February 15, 2024 01:06
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 14 You must be signed in to fork a gist
  • Save arulrajnet/2424bc1ffc40324f3786 to your computer and use it in GitHub Desktop.
Save arulrajnet/2424bc1ffc40324f3786 to your computer and use it in GitHub Desktop.
Python Script to download the Chrome Extensions (CRX) file directly from the google chrome web store.
# -*- coding: utf-8 -*-
"""
Python Script to download the Chrome Extensions (CRX) file directly from the google chrome web store.
Referred from http://chrome-extension-downloader.com/how-does-it-work.php
"""
from __future__ import division
import argparse
import requests
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
import sys
__author__ = 'arul'
class ChromeAppDownloader():
CRX_URL = "https://clients2.google.com/service/update2/crx?" \
"response=redirect&prodversion=38.0&x=id%3D~~~~%26installsource%3Dondemand%26uc"
USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36"
global headers
headers = {
"User-Agent": USER_AGENT,
"Referer": "https://chrome.google.com",
}
def __init__(self):
pass
def download(self, _download_url_, _file_name_):
"""
Download the given URL into given filename.
:param _download_url_:
:param _file_name_:
:return:
"""
try:
# Download as Stream
r = requests.get(url=_download_url_, headers=headers, stream=True)
redirects = r.history
_fname_ = None
if len(redirects) > 0:
redirect_header = redirects[-1].headers
if "location" in redirect_header:
loc = redirect_header["location"]
uparse = urlparse.urlparse(loc)
splits = uparse.path.split("/")
_fname_ = splits[-1]
if _fname_:
_file_name_ = _fname_.replace("extension", _file_name_)
else:
_file_name_ += ".crx"
r_headers = r.headers
content_length = None
if "content-length" in r_headers:
content_length = int(r_headers["content-length"])
if content_length:
print("Downloading %s. File Size %s " % (_file_name_, self.byte_to_human(content_length)))
else:
print("Downloading %s " % _file_name_)
chunk_size = 16 * 1024
dowloaded_bytes = 0
with open(_file_name_, 'wb') as fd:
for chunk in r.iter_content(chunk_size):
fd.write(chunk)
dowloaded_bytes += len(chunk)
sys.stdout.write("\r" + self.byte_to_human(dowloaded_bytes))
sys.stdout.flush()
except Exception as e:
raise ValueError("Error in downloading %s " % _download_url_, e)
def parse(self, chrome_store_url):
"""
Validate the given input is chrome store URL or not.
Returning app ID and app Name from the URL
:param chrome_store_url:
:return:
"""
try:
# Try to validate the URL
uparse = urlparse.urlparse(chrome_store_url)
if uparse.netloc != "chrome.google.com":
raise ValueError("Not a valid URL %s" % chrome_store_url)
splits = uparse.path.split("/")
if not (len(splits) == 4 and uparse.path.startswith("/webstore/detail/")):
raise ValueError("Not a valid URL %s" % chrome_store_url)
except Exception as e:
pass
return splits[-1], splits[-2]
def byte_to_human(self, len_in_byte):
"""
Converts byte into human readable format.
:param len_in_byte:
:return:
"""
in_kb = len_in_byte / 1024
in_mb = in_kb / 1024
in_gb = in_mb / 1024
if in_kb < 1024:
return "%.2f KB" % in_kb
if in_mb < 1024:
return "%.2f MB" % in_mb
if in_gb > 1:
return "%.2f GB" % in_gb
if __name__ == '__main__':
# Getting webstore URL from User
parser = argparse.ArgumentParser(description='Download CRX file from Google Chrome Webstore.')
parser.add_argument('-u', '--url', help='URL of the chrome store', required=True)
args = parser.parse_args()
downloader = ChromeAppDownloader()
try:
file_name = None
# Validating and building app ID, file name for the given URL
chrome_app_id, file_name = downloader.parse(chrome_store_url=args.url)
if chrome_app_id:
download_url = downloader.CRX_URL.replace("~~~~", chrome_app_id)
# Downloading the CRX file
downloader.download(download_url, file_name)
except Exception as e:
print(e)
@arulrajnet
Copy link
Author

arulrajnet commented Nov 25, 2014

Installation

To use the above program, You have to install below modules.

requests - To make the HTTP request.

argparse - Get an input from user

Command to Install

pip install requests argparse

How to use

python ChromeAppDownloader.py 
-u https://chrome.google.com/webstore/detail/google-maps/lneaknkopdijkpnocmklfnjbeapigfbh

Update

Revision 2

  • Download percentage added
  • Remove beautifulsoup and lxml dependencies

Revision 3

  • Python 3 support
  • Issues fixed

@MarcWeber
Copy link

it still works ignoring the # if not (len(splits) == 4 and uparse.path.startswith("/webstore/detail/")):
assertion.

@TechComet
Copy link

not work

local variable 'splits' referenced before assignment

@arulrajnet
Copy link
Author

not work

local variable 'splits' referenced before assignment

@TechComet Issue fixed.

@Bogdan107
Copy link

Bogdan107 commented Sep 23, 2021

not work
downloads file with 0 bytes size

P.S.
Problem resolved by https://github.com/tonystark93/crx-download

@curbengh
Copy link

curbengh commented Mar 1, 2022

Changing these constants works for me

CRX_URL = "https://clients2.google.com/service/update2/crx?response=redirect&prodversion=98.0.4758.102&acceptformat=crx2,crx3&x=id%3D~~~~%26uc&nacl_arch=x86-64"
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"

However, the downloaded crx seems to include signature which needs to be removed before it can be extracted.

https://github.com/tonystark93/crx-download/blob/e51e1dbd3b96c777b4fd7cce247b724b6eca7ccf/background.js#L74-L96

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