From a641502ea1d7a5482b1133021016e0e5d798f1e6 Mon Sep 17 00:00:00 2001 From: Nikhil Badyal <59223300+nikhilbadyal@users.noreply.github.com> Date: Sat, 26 Aug 2023 19:06:39 +0530 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Added=20apkmonk=20scrapper=20(#324)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++-- src/downloader/apkmonk.py | 93 +++++++++++++++++++++++++++++++++++++++ src/downloader/factory.py | 11 ++++- src/downloader/sources.py | 2 + src/exceptions.py | 4 ++ 5 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 src/downloader/apkmonk.py diff --git a/README.md b/README.md index 2a077e1..318e464 100644 --- a/README.md +++ b/README.md @@ -202,18 +202,21 @@ You can use any of the following methods to build. ``` You can also provide DL to the clean apk instead of providing DL_SOURCES as mentioned in this [note](#app-dl). Supported Scrappers are: - 1. APKMIRROR - Supports downloading any versions + 1. APKMIRROR - Supports downloading any available version 1. Link Format - https://www.apkmirror.com/apk//app-name/ 2. Example Link - https://www.apkmirror.com/apk/google-inc/youtube/ - 2. UPTODOWN - Supports downloading any versions + 2. UPTODOWN - Supports downloading any available version 1. Link Format - https://.en.uptodown.com/android 2. Example Link - https://spotify.en.uptodown.com/android - 3. APKSOS - Supports downloading any versions + 3. APKSOS - Supports downloading any available version 1. Link Format - https://apksos.com/download-app/ 2. Example Link - https://apksos.com/download-app/com.expensemanager 4. APKPURE - Supports downloading only latest version 1. Link Format - https://d.apkpure.com/b/APK/?version=latest 2. Example Link - https://d.apkpure.com/b/APK/com.google.android.youtube?version=latest + 5. APKMonk - Supports downloading any available version + 1. Link Format - https://www.apkmonk.com/app// + 2. Example Link - https://www.apkmonk.com/app//
Please verify the source of original APKs yourself with links provided. I'm not responsible for any damage caused.If you know any better/safe source to download clean. Open a discussion. diff --git a/src/downloader/apkmonk.py b/src/downloader/apkmonk.py new file mode 100644 index 0000000..bad28f4 --- /dev/null +++ b/src/downloader/apkmonk.py @@ -0,0 +1,93 @@ +"""APK Monk Downloader Class.""" +import re +from typing import Any, Self, Tuple + +import requests +from bs4 import BeautifulSoup + +from src.app import APP +from src.downloader.download import Downloader +from src.downloader.sources import APK_MONK_BASE_URL +from src.downloader.utils import status_code_200 +from src.exceptions import APKMonkAPKDownloadError +from src.utils import bs4_parser, request_header + + +class ApkMonk(Downloader): + """Files downloader.""" + + def extract_download_link(self: Self, page: str, app: str) -> Tuple[str, str]: + """Function to extract the download link from apkmonk html page. + + :param page: Url of the page + :param app: Name of the app + """ + file_name = f"{app}.apk" + r = requests.get(page, headers=request_header, allow_redirects=True, timeout=60) + if r.status_code != status_code_200: + msg = f"Unable to connect with {page}.Reason - {r.text}" + raise APKMonkAPKDownloadError( + msg, + url=page, + ) + soup = BeautifulSoup(r.text, bs4_parser) + download_scripts = soup.find_all("script", type="text/javascript") + key_value_pattern = r'\{"pkg":"([^"]+)","key":"([^"]+)"\}' + url = None + for script in download_scripts: + if match := re.search(key_value_pattern, script.text): + pkg_value = match.group(1) + key_value = match.group(2) + url = f"{APK_MONK_BASE_URL}/down_file?pkg={pkg_value}&key={key_value}" + break + if not url: + msg = "Unable to scrap link" + raise APKMonkAPKDownloadError( + msg, + url=page, + ) + r = requests.get(url, headers=request_header, allow_redirects=True, timeout=60) + if r.status_code != status_code_200: + msg = f"Unable to connect with {page}.Reason - {r.text}" + raise APKMonkAPKDownloadError( + msg, + url=page, + ) + final_download_url = r.json()["url"] + self._download(final_download_url, file_name) + return file_name, final_download_url + + def specific_version(self: Self, app: APP, version: str, main_page: str = "") -> Tuple[str, str]: + """Function to download the specified version of app from apkmirror. + + :param app: Name of the application + :param version: Version of the application to download + :param main_page: Version of the application to download + :return: Version of downloaded apk + """ + r = requests.get(app.download_source, headers=request_header, allow_redirects=True, timeout=60) + soup = BeautifulSoup(r.text, bs4_parser) + version_table = soup.find_all(class_="striped") + for version_row in version_table: + version_links = version_row.find_all("a") + for link in version_links: + app_version = link.text + if app_version == app.app_version: + download_link = link["href"] + return self.extract_download_link(APK_MONK_BASE_URL + download_link, app.app_name) + msg = "Unable to scrap link" + raise APKMonkAPKDownloadError( + msg, + url=app.download_source, + ) + + def latest_version(self: Self, app: APP, **kwargs: Any) -> Tuple[str, str]: + """Function to download whatever the latest version of app from apkmonkP. + + :param app: Name of the application + :return: Version of downloaded apk + """ + r = requests.get(app.download_source, headers=request_header, allow_redirects=True, timeout=60) + soup = BeautifulSoup(r.text, bs4_parser) + latest_download_url = soup.find(id="download_button")["href"] # type: ignore[index] + return self.extract_download_link(latest_download_url, app.app_name) # type: ignore[arg-type] diff --git a/src/downloader/factory.py b/src/downloader/factory.py index 956d3e8..d1c0a97 100644 --- a/src/downloader/factory.py +++ b/src/downloader/factory.py @@ -1,11 +1,18 @@ """Downloader Factory.""" from src.config import RevancedConfig from src.downloader.apkmirror import ApkMirror +from src.downloader.apkmonk import ApkMonk from src.downloader.apkpure import ApkPure from src.downloader.apksos import ApkSos from src.downloader.download import Downloader from src.downloader.github import Github -from src.downloader.sources import APK_MIRROR_BASE_URL, APK_PURE_BASE_URL, APKS_SOS_BASE_URL, GITHUB_BASE_URL +from src.downloader.sources import ( + APK_MIRROR_BASE_URL, + APK_MONK_BASE_URL, + APK_PURE_BASE_URL, + APKS_SOS_BASE_URL, + GITHUB_BASE_URL, +) from src.downloader.uptodown import UptoDown from src.exceptions import DownloadError @@ -32,5 +39,7 @@ class DownloaderFactory(object): return UptoDown(config) if apk_source.startswith(APK_MIRROR_BASE_URL): return ApkMirror(config) + if apk_source.startswith(APK_MONK_BASE_URL): + return ApkMonk(config) msg = "No download factory found." raise DownloadError(msg) diff --git a/src/downloader/sources.py b/src/downloader/sources.py index c2500d7..551444c 100644 --- a/src/downloader/sources.py +++ b/src/downloader/sources.py @@ -15,6 +15,8 @@ APK_COMBO_BASE_URL = "https://apkcombo.com" APK_COMBO_GENERIC_URL = APK_COMBO_BASE_URL + "/genericApp/{}" not_found_icon = "https://img.icons8.com/bubbles/500/android-os.png" revanced_api = "https://releases.revanced.app/patches" +APK_MONK_BASE_URL = "https://www.apkmonk.com" +APK_MONK_APK_URL = APK_MONK_BASE_URL + "/app/{}/" apk_sources = { "backdrops": f"{APK_MIRROR_BASE_APK_URL}/backdrops/backdrops-wallpapers/", "bacon": f"{APK_MIRROR_BASE_APK_URL}/onelouder-apps/baconreader-for-reddit/", diff --git a/src/exceptions.py b/src/exceptions.py index 4d486ef..5651e39 100644 --- a/src/exceptions.py +++ b/src/exceptions.py @@ -46,6 +46,10 @@ class APKMirrorAPKDownloadError(APKDownloadError): """Exception raised when downloading an APK from apkmirror failed.""" +class APKMonkAPKDownloadError(APKDownloadError): + """Exception raised when downloading an APK from apkmonk failed.""" + + class APKMirrorAPKNotFoundError(APKDownloadError): """Exception raised when apk doesn't exist on APKMirror."""