apksearch 1.2.0__tar.gz → 1.2.6__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. {apksearch-1.2.0 → apksearch-1.2.6}/PKG-INFO +40 -15
  2. {apksearch-1.2.0 → apksearch-1.2.6}/README.md +37 -12
  3. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch/__init__.py +3 -1
  4. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch/cli.py +82 -14
  5. apksearch-1.2.6/apksearch/sites/apkad.py +109 -0
  6. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch/sites/apkcombo.py +6 -3
  7. apksearch-1.2.6/apksearch/sites/apkfab.py +120 -0
  8. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch/sites/apkpure.py +43 -18
  9. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch.egg-info/PKG-INFO +40 -15
  10. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch.egg-info/SOURCES.txt +3 -0
  11. {apksearch-1.2.0 → apksearch-1.2.6}/pyproject.toml +2 -2
  12. apksearch-1.2.6/tests/test_apkfab.py +38 -0
  13. {apksearch-1.2.0 → apksearch-1.2.6}/LICENSE +0 -0
  14. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch/__main__.py +0 -0
  15. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch/sites/__init__.py +0 -0
  16. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch/sites/apkmirror.py +0 -0
  17. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch/sites/appteka.py +0 -0
  18. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch.egg-info/dependency_links.txt +0 -0
  19. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch.egg-info/entry_points.txt +0 -0
  20. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch.egg-info/requires.txt +0 -0
  21. {apksearch-1.2.0 → apksearch-1.2.6}/apksearch.egg-info/top_level.txt +0 -0
  22. {apksearch-1.2.0 → apksearch-1.2.6}/setup.cfg +0 -0
  23. {apksearch-1.2.0 → apksearch-1.2.6}/tests/test_apkcombo.py +0 -0
  24. {apksearch-1.2.0 → apksearch-1.2.6}/tests/test_apkmirror.py +0 -0
  25. {apksearch-1.2.0 → apksearch-1.2.6}/tests/test_apkpure.py +0 -0
  26. {apksearch-1.2.0 → apksearch-1.2.6}/tests/test_appteka.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: apksearch
3
- Version: 1.2.0
3
+ Version: 1.2.6
4
4
  Summary: Search for apks on varius websites
5
5
  Author-email: Abhi <allinoneallinone00@gmail.com>
6
6
  License: MIT License
@@ -28,7 +28,7 @@ License: MIT License
28
28
  Project-URL: homepage, https://github.com/AbhiTheModder/apksearch.git
29
29
  Project-URL: source, https://github.com/AbhiTheModder/apksearch.git
30
30
  Project-URL: issues, https://github.com/AbhiTheModder/apksearch.git/issues
31
- Classifier: Development Status :: 4 - Beta
31
+ Classifier: Development Status :: 5 - Production/Stable
32
32
  Classifier: Natural Language :: English
33
33
  Classifier: Intended Audience :: Developers
34
34
  Classifier: License :: OSI Approved :: MIT License
@@ -50,31 +50,55 @@ Requires-Dist: pytest>=7.4.3; extra == "dev"
50
50
  Requires-Dist: black>=23.12.1; extra == "dev"
51
51
  Requires-Dist: flake8>=6.1.0; extra == "dev"
52
52
 
53
- # apksearch
53
+ <h1 align="center">apksearch</h1>
54
54
 
55
55
  `apksearch` is a Python library designed to search for APK files on different APK websites, such as APKPure and APKMirror. It allows users to find APKs, check for available versions, and retrieve download links.
56
56
 
57
57
  **The Inspiration:**
58
58
  There were countless occasions when I needed a specific APK for a package name, only to find it unavailable on popular platforms. This led to the tedious task of manually visiting multiple websites and searching one by one.
59
+ <details>
60
+ <summary>screenshot</summary>
61
+ <p align="center">
62
+ <img width="500" src="https://github.com/user-attachments/assets/cd54eaeb-a56b-40b3-835f-b48b1e7772f3"></img><br>
63
+ As you can see, Roblox version <code>2.647.716</code> is not available on APKPure and APKCombo, this helped me avoid going through these sites.
64
+ </p>
65
+ </details>
66
+
67
+ **P.S:** If you're looking for an APK downloader, I highly recommend using [apkeep](https://github.com/EFForg/apkeep).
59
68
 
60
69
  # Features
61
70
 
62
71
  - **Search APKs:** The library provides methods to search for APKs using package names.
63
- - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from APKPure and APKMirror.
72
+ - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from various websites.
64
73
  - **Command-Line Interface:** A CLI is available for users to search for APKs directly from the command line.
65
74
 
75
+ ## Supported Websites
76
+
77
+ - [APKPure](https://apkpure.net/)
78
+ - [APKMirror](https://www.apkmirror.com/)
79
+ - [APKCombo](https://apkcombo.app/)
80
+ - [APKFab](https://apkfab.com/)
81
+ - [Appteka](https://appteka.store/)
82
+ - [APKAD](https://apk.ad/)
83
+
84
+ > [!NOTE]
85
+ > **For site owners:**
86
+ > If you're the owner of a website that's not listed here and you'd like to add support for it, feel free to open an issue or submit a pull request. I'm open to adding more websites to the library.
87
+ > I respect the _value of user engagement and the revenue_ it generates for your site. To honor this, I have deliberately avoided including a download feature in the library, ensuring users will still need to visit your website, maintaining its traffic and engagement.
88
+ > Additionally, I kindly ask that you **refrain from enforcing strict blocking measures**, such as aggressive Cloudflare rules, as the library is designed to work collaboratively rather than disruptively. Thank you!
89
+
66
90
  ## Installation
67
91
 
68
92
  To install the `apksearch` library, use the following command:
69
93
 
70
94
  ```sh
71
- pip install git+https://github.com/AbhiTheModder/apksearch.git
95
+ pip install -U git+https://github.com/AbhiTheModder/apksearch.git
72
96
  ```
73
97
 
74
98
  OR, through pip:
75
99
 
76
100
  ```sh
77
- pip install apksearch
101
+ pip install -U apksearch
78
102
  ```
79
103
 
80
104
  ## Usage
@@ -117,7 +141,7 @@ if result:
117
141
 
118
142
  ### Classes and Methods
119
143
 
120
- #### `APKPure`
144
+ #### `APKPure` | `APKCombo` | `APKFab`
121
145
 
122
146
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
123
147
  - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKPure and returns the title and link if found.
@@ -129,17 +153,11 @@ if result:
129
153
  - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKMirror and returns the title and link if found.
130
154
  - **`find_version(self, apk_link: str, version: str) -> str`**: Finds and returns the download link for the given APK link and version.
131
155
 
132
- #### `AppTeka`
156
+ #### `AppTeka` | `APKAD`
133
157
 
134
158
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
135
159
  - **`search_apk(self, version: str = None) -> None | tuple[str, str]`**: Searches for the APK on AppTeka and returns the title and link if found. If a version is provided, it checks if that version is available and returns the corresponding download link, None otherwise. If no version is provided, it returns the link for the latest version available.
136
160
 
137
- #### `APKCombo`
138
-
139
- - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
140
- - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKCombo and returns the title and link if found.
141
- - **`find_versions(self, apk_link: str) -> list[tuple[str, str]]`**: Finds and returns a list of versions and their download links for the given APK link.
142
-
143
161
  ### Testing
144
162
 
145
163
  The project includes tests for the `sites` classes. To run the tests, use the following command:
@@ -152,6 +170,11 @@ pytest
152
170
 
153
171
  - [ ] Add more websites to search for APKs.
154
172
 
173
+ ## Acknowledgements
174
+
175
+ - [APKUpdater](https://github.com/rumboalla/apkupdater) for APKMirror API.
176
+ - [apkeep](https://github.com/EFForg/apkeep) for APKPure API.
177
+
155
178
  ## License
156
179
 
157
180
  This project is licensed under the MIT License. See the [LICENSE](https://github.com/AbhiTheModder/apksearch/blob/main/LICENSE) file for more details.
@@ -159,3 +182,5 @@ This project is licensed under the MIT License. See the [LICENSE](https://github
159
182
  ## Contributing
160
183
 
161
184
  Contributions are welcome! Please open an issue or submit a pull request on [GitHub](https://github.com/AbhiTheModder/apksearch).
185
+
186
+ If you find this project helpful, please consider giving it a ⭐. Your support is greatly appreciated!
@@ -1,28 +1,52 @@
1
- # apksearch
1
+ <h1 align="center">apksearch</h1>
2
2
 
3
3
  `apksearch` is a Python library designed to search for APK files on different APK websites, such as APKPure and APKMirror. It allows users to find APKs, check for available versions, and retrieve download links.
4
4
 
5
5
  **The Inspiration:**
6
6
  There were countless occasions when I needed a specific APK for a package name, only to find it unavailable on popular platforms. This led to the tedious task of manually visiting multiple websites and searching one by one.
7
+ <details>
8
+ <summary>screenshot</summary>
9
+ <p align="center">
10
+ <img width="500" src="https://github.com/user-attachments/assets/cd54eaeb-a56b-40b3-835f-b48b1e7772f3"></img><br>
11
+ As you can see, Roblox version <code>2.647.716</code> is not available on APKPure and APKCombo, this helped me avoid going through these sites.
12
+ </p>
13
+ </details>
14
+
15
+ **P.S:** If you're looking for an APK downloader, I highly recommend using [apkeep](https://github.com/EFForg/apkeep).
7
16
 
8
17
  # Features
9
18
 
10
19
  - **Search APKs:** The library provides methods to search for APKs using package names.
11
- - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from APKPure and APKMirror.
20
+ - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from various websites.
12
21
  - **Command-Line Interface:** A CLI is available for users to search for APKs directly from the command line.
13
22
 
23
+ ## Supported Websites
24
+
25
+ - [APKPure](https://apkpure.net/)
26
+ - [APKMirror](https://www.apkmirror.com/)
27
+ - [APKCombo](https://apkcombo.app/)
28
+ - [APKFab](https://apkfab.com/)
29
+ - [Appteka](https://appteka.store/)
30
+ - [APKAD](https://apk.ad/)
31
+
32
+ > [!NOTE]
33
+ > **For site owners:**
34
+ > If you're the owner of a website that's not listed here and you'd like to add support for it, feel free to open an issue or submit a pull request. I'm open to adding more websites to the library.
35
+ > I respect the _value of user engagement and the revenue_ it generates for your site. To honor this, I have deliberately avoided including a download feature in the library, ensuring users will still need to visit your website, maintaining its traffic and engagement.
36
+ > Additionally, I kindly ask that you **refrain from enforcing strict blocking measures**, such as aggressive Cloudflare rules, as the library is designed to work collaboratively rather than disruptively. Thank you!
37
+
14
38
  ## Installation
15
39
 
16
40
  To install the `apksearch` library, use the following command:
17
41
 
18
42
  ```sh
19
- pip install git+https://github.com/AbhiTheModder/apksearch.git
43
+ pip install -U git+https://github.com/AbhiTheModder/apksearch.git
20
44
  ```
21
45
 
22
46
  OR, through pip:
23
47
 
24
48
  ```sh
25
- pip install apksearch
49
+ pip install -U apksearch
26
50
  ```
27
51
 
28
52
  ## Usage
@@ -65,7 +89,7 @@ if result:
65
89
 
66
90
  ### Classes and Methods
67
91
 
68
- #### `APKPure`
92
+ #### `APKPure` | `APKCombo` | `APKFab`
69
93
 
70
94
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
71
95
  - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKPure and returns the title and link if found.
@@ -77,17 +101,11 @@ if result:
77
101
  - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKMirror and returns the title and link if found.
78
102
  - **`find_version(self, apk_link: str, version: str) -> str`**: Finds and returns the download link for the given APK link and version.
79
103
 
80
- #### `AppTeka`
104
+ #### `AppTeka` | `APKAD`
81
105
 
82
106
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
83
107
  - **`search_apk(self, version: str = None) -> None | tuple[str, str]`**: Searches for the APK on AppTeka and returns the title and link if found. If a version is provided, it checks if that version is available and returns the corresponding download link, None otherwise. If no version is provided, it returns the link for the latest version available.
84
108
 
85
- #### `APKCombo`
86
-
87
- - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
88
- - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKCombo and returns the title and link if found.
89
- - **`find_versions(self, apk_link: str) -> list[tuple[str, str]]`**: Finds and returns a list of versions and their download links for the given APK link.
90
-
91
109
  ### Testing
92
110
 
93
111
  The project includes tests for the `sites` classes. To run the tests, use the following command:
@@ -100,6 +118,11 @@ pytest
100
118
 
101
119
  - [ ] Add more websites to search for APKs.
102
120
 
121
+ ## Acknowledgements
122
+
123
+ - [APKUpdater](https://github.com/rumboalla/apkupdater) for APKMirror API.
124
+ - [apkeep](https://github.com/EFForg/apkeep) for APKPure API.
125
+
103
126
  ## License
104
127
 
105
128
  This project is licensed under the MIT License. See the [LICENSE](https://github.com/AbhiTheModder/apksearch/blob/main/LICENSE) file for more details.
@@ -107,3 +130,5 @@ This project is licensed under the MIT License. See the [LICENSE](https://github
107
130
  ## Contributing
108
131
 
109
132
  Contributions are welcome! Please open an issue or submit a pull request on [GitHub](https://github.com/AbhiTheModder/apksearch).
133
+
134
+ If you find this project helpful, please consider giving it a ⭐. Your support is greatly appreciated!
@@ -2,5 +2,7 @@ from .sites.apkpure import APKPure
2
2
  from .sites.apkmirror import APKMirror
3
3
  from .sites.appteka import AppTeka
4
4
  from .sites.apkcombo import APKCombo
5
+ from .sites.apkfab import APKFab
6
+ from .sites.apkad import APKAD
5
7
 
6
- __all__ = ["APKPure", "APKMirror", "AppTeka", "APKCombo"]
8
+ __all__ = ["APKPure", "APKMirror", "AppTeka", "APKCombo", "APKFab", "APKAD"]
@@ -1,6 +1,7 @@
1
1
  import argparse
2
2
 
3
- from apksearch import APKPure, APKMirror, AppTeka, APKCombo
3
+ from collections.abc import Callable
4
+ from apksearch import APKPure, APKMirror, AppTeka, APKCombo, APKFab, APKAD
4
5
  from requests.exceptions import ConnectionError, ConnectTimeout
5
6
 
6
7
  # Color codes
@@ -11,6 +12,21 @@ YELLOW = "\033[93m"
11
12
  NC = "\033[0m"
12
13
 
13
14
 
15
+ def search(
16
+ func: Callable[[str, str], object],
17
+ pkg_name: str,
18
+ version: str | None,
19
+ log_err: bool = False,
20
+ ) -> None:
21
+ try:
22
+ func(pkg_name, version)
23
+ except Exception as exc:
24
+ if log_err:
25
+ print(f"Error in {func.__name__}: {exc}")
26
+ else:
27
+ pass
28
+
29
+
14
30
  def search_apkpure(pkg_name: str, version: str | None) -> None:
15
31
  apkpure = APKPure(pkg_name)
16
32
  try:
@@ -22,20 +38,64 @@ def search_apkpure(pkg_name: str, version: str | None) -> None:
22
38
  title, apk_link = result_apkpure
23
39
  print(f"{BOLD}APKPure:{NC} Found {GREEN}{title}{NC}") if title else None
24
40
  print(f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}") if not version else None
25
- versions: list[tuple[str, str]] = apkpure.find_versions(apk_link)
26
41
  if version:
27
- for version_tuple in versions:
28
- if version_tuple[0] == version:
29
- print(
30
- f" ╰─> {BOLD}Version: {GREEN}{version}{NC} - {YELLOW}{version_tuple[1]}{NC}"
31
- )
32
- break
33
- else:
34
- print(f"{BOLD}APKPure:{NC} Version {RED}{version}{NC} not found!")
42
+ versions: list[tuple[str, str]] = apkpure.find_versions(apk_link)
43
+ if versions:
44
+ for version_tuple in versions:
45
+ if version_tuple[0] == version:
46
+ print(
47
+ f" ╰─> {BOLD}Version: {GREEN}{version}{NC} - {YELLOW}{version_tuple[1]}{NC}"
48
+ )
49
+ break
50
+ else:
51
+ print(f"{BOLD}APKPure:{NC} Version {RED}{version}{NC} not found!")
35
52
  else:
36
53
  print(f"{BOLD}APKPure:{NC} No Results!")
37
54
 
38
55
 
56
+ def search_apkfab(pkg_name: str, version: str | None) -> None:
57
+ apkfab = APKFab(pkg_name)
58
+ try:
59
+ result_apkfab: tuple[str, str] | None = apkfab.search_apk()
60
+ except (ConnectionError, ConnectTimeout):
61
+ result_apkfab = None
62
+ print(f"{RED}Failed to resolve 'apkfab.com'!{NC}")
63
+ if result_apkfab:
64
+ title, apk_link = result_apkfab
65
+ print(f"{BOLD}APKFab:{NC} Found {GREEN}{title}{NC}") if title else None
66
+ print(f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}") if not version else None
67
+ if version:
68
+ versions: list[tuple[str, str]] = apkfab.find_versions(apk_link)
69
+ if versions:
70
+ for version_tuple in versions:
71
+ if version_tuple[0] == version:
72
+ print(
73
+ f" ╰─> {BOLD}Version: {GREEN}{version}{NC} - {YELLOW}{version_tuple[1]}{NC}"
74
+ )
75
+ break
76
+ else:
77
+ print(f"{BOLD}APKFab:{NC} Version {RED}{version}{NC} not found!")
78
+ else:
79
+ print(f"{BOLD}APKFab:{NC} No Results!")
80
+
81
+
82
+ def search_apkad(pkg_name: str, version: str | None) -> None:
83
+ apkad = APKAD(pkg_name)
84
+ try:
85
+ result_apkad: tuple[str, str] | None = apkad.search_apk()
86
+ except (ConnectionError, ConnectTimeout):
87
+ result_apkad = None
88
+ print(f"{RED}Failed to resolve 'api.apk.ad'!{NC}")
89
+ if result_apkad:
90
+ title, apk_link = result_apkad
91
+ print(f"{BOLD}APKAD:{NC} Found {GREEN}{title}{NC}") if title else None
92
+ print(
93
+ f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}"
94
+ ) if not version else print(" ╰─> Doesn't support version search!")
95
+ else:
96
+ print(f"{BOLD}APKAD:{NC} No Results!")
97
+
98
+
39
99
  def search_apkcombo(pkg_name: str, version: str | None) -> None:
40
100
  apkcombo = APKCombo(pkg_name)
41
101
  try:
@@ -113,19 +173,27 @@ def main():
113
173
  )
114
174
  parser.add_argument("pkg_name", help="The package name of the APK")
115
175
  parser.add_argument("--version", help="The version of the APK", required=False)
176
+ parser.add_argument(
177
+ "--log_err", help="Enable error logs", action="store_true", required=False
178
+ )
116
179
  args = parser.parse_args()
117
180
 
118
181
  pkg_name = args.pkg_name
119
182
  version = args.version
183
+ log_err = args.log_err
120
184
  print(f"{BOLD}Searching for {YELLOW}{pkg_name}{NC}...")
121
185
  # Initiate search on apkpure
122
- search_apkpure(pkg_name, version)
186
+ search(search_apkpure, pkg_name, version, log_err)
123
187
  # Initiate search on apkmirror
124
- search_apkmirror(pkg_name, version)
188
+ search(search_apkmirror, pkg_name, version, log_err)
125
189
  # Initiate search on appteka
126
- search_appteka(pkg_name, version)
190
+ search(search_appteka, pkg_name, version, log_err)
127
191
  # Initiate search on apkcombo
128
- search_apkcombo(pkg_name, version)
192
+ search(search_apkcombo, pkg_name, version, log_err)
193
+ # Initiate search on apkfab
194
+ search(search_apkfab, pkg_name, version, log_err)
195
+ # Initiate search on apkad
196
+ search(search_apkad, pkg_name, version, log_err)
129
197
 
130
198
 
131
199
  if __name__ == "__main__":
@@ -0,0 +1,109 @@
1
+ import json
2
+ from bs4 import BeautifulSoup
3
+ import requests
4
+
5
+
6
+ class APKAD:
7
+ """
8
+ This class provides methods to search for an APK on APKAD based on package name,
9
+ and to find available versions and their download links for a given APK link.
10
+
11
+ Parameters:
12
+ pkg_name (str): The package name of the APK to search for.
13
+
14
+ Attributes:
15
+ pkg_name (str): The package name of the APK to search for.
16
+ base_url (str): The base URL of the APKAD website.
17
+ search_url (str): The URL used to search for APKs on APKAD.
18
+ headers (dict): The headers used for making HTTP requests.
19
+ session (requests.Session): The session object used for making HTTP requests.
20
+
21
+ Methods:
22
+ search_apk() -> None | tuple[str, str]:
23
+ Searches for the APK on APKAD and returns the title and link if found.
24
+ """
25
+
26
+ def __init__(self, pkg_name: str):
27
+ self.pkg_name = pkg_name
28
+ self.base_url = "https://downloader.apk.ad"
29
+ self.api_url = "https://api.apk.ad"
30
+ self.search_url = (
31
+ self.api_url
32
+ + f"/get?hl=en&package={self.pkg_name}&device=phone&arch=arm64-v8a&vc=&device_id="
33
+ )
34
+ self.headers = {
35
+ "accept": "text/event-stream",
36
+ "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
37
+ "cache-control": "no-cache",
38
+ "dnt": "1",
39
+ "origin": "https://downloader.apk.ad",
40
+ "pragma": "no-cache",
41
+ "priority": "u=1, i",
42
+ "referer": "https://downloader.apk.ad/",
43
+ "sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
44
+ "sec-ch-ua-mobile": "?0",
45
+ "sec-ch-ua-platform": '"Windows"',
46
+ "sec-fetch-dest": "empty",
47
+ "sec-fetch-mode": "cors",
48
+ "sec-fetch-site": "same-site",
49
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0",
50
+ }
51
+ self.session = requests.Session()
52
+
53
+ def search_apk(self) -> None | tuple[str, str]:
54
+ """
55
+ Searches for the APK on APKAD and returns the title and link if found.
56
+
57
+ Returns:
58
+ None: If no matching APK is found.
59
+ tuple[str, str]: A tuple containing the title and link of the matching APK if found.
60
+ """
61
+ url = self.search_url
62
+ response = self.session.get(url, headers=self.headers, stream=True)
63
+ stream_response = ""
64
+ for line in response.iter_lines():
65
+ if line:
66
+ line_response = line.decode("utf-8")
67
+ if '"progress":100' in line_response:
68
+ line_response = line_response[6:]
69
+ stream_response += line_response
70
+ break
71
+ if stream_response:
72
+ data = json.loads(stream_response)
73
+ html_body = data["html"]
74
+ soup = BeautifulSoup(html_body, "html.parser")
75
+ if not soup:
76
+ return None
77
+ title = soup.find("li", {"class": "_title"})
78
+ if title:
79
+ title = title.text.strip()
80
+ button = soup.find(
81
+ "button", {"onclick": True, "id": "downloadButtonapk"}
82
+ )["onclick"]
83
+ if button:
84
+ zip_args = [
85
+ arg.strip("'")
86
+ for arg in button.split("zip(")[1].split(")")[0].split(",")
87
+ ]
88
+ h = zip_args[0] # hash
89
+ p = zip_args[-1] # type
90
+ token = zip_args[1] # token
91
+ ip = zip_args[2] # ip
92
+ google_id = zip_args[3] # package_name
93
+ t = zip_args[4] # time
94
+ apk_url = f"https://zip.apk.ad/compress?h={h}&p={p}&token={token}&ip={ip}&google_id={google_id}&t={t}"
95
+ apk_url_response = self.session.get(
96
+ url=apk_url, headers=self.headers, stream=True
97
+ )
98
+ for line in apk_url_response.iter_lines():
99
+ if line:
100
+ line_response = line.decode("utf-8")
101
+ if "File is ready for download." in line_response:
102
+ line_response = json.loads(line_response)
103
+ line_html = line_response["html"]
104
+ line_soup = BeautifulSoup(line_html, "html.parser")
105
+ download_link = line_soup.find("a")["href"]
106
+ if download_link.endswith("\n"):
107
+ download_link = download_link[:-1]
108
+ return title, download_link
109
+ return None
@@ -28,7 +28,7 @@ class APKCombo:
28
28
  def __init__(self, pkg_name: str):
29
29
  self.pkg_name = pkg_name
30
30
  self.base_url = "https://apkcombo.app"
31
- self.search_url = self.base_url + "/search/"
31
+ self.search_url = self.base_url + "/search"
32
32
  self.headers = {
33
33
  "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
34
34
  "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
@@ -59,7 +59,7 @@ class APKCombo:
59
59
  tuple[str, str]: A tuple containing the title and link of the matching APK if found.
60
60
  """
61
61
  pkg_name = self.pkg_name
62
- url = self.search_url + pkg_name
62
+ url = self.search_url + "/" + pkg_name
63
63
  response: requests.Response = self.session.get(
64
64
  url, headers=self.headers, allow_redirects=False
65
65
  )
@@ -72,7 +72,10 @@ class APKCombo:
72
72
  else:
73
73
  raise Exception(f"Error: {response.status_code}")
74
74
  soup = BeautifulSoup(response.text, "html.parser")
75
- title = soup.find("div", {"class": "app_name"}).text.strip()
75
+ try:
76
+ title = soup.find("div", {"class": "app_name"}).text.strip()
77
+ except AttributeError:
78
+ return None
76
79
  apk_link = url
77
80
  return title, apk_link
78
81
 
@@ -0,0 +1,120 @@
1
+ from bs4 import BeautifulSoup
2
+ import requests
3
+
4
+
5
+ class APKFab:
6
+ """
7
+ This class provides methods to search for an APK on APKFab based on package name,
8
+ and to find available versions and their download links for a given APK link.
9
+
10
+ Parameters:
11
+ pkg_name (str): The package name of the APK to search for.
12
+
13
+ Attributes:
14
+ pkg_name (str): The package name of the APK to search for.
15
+ base_url (str): The base URL of the APKFab website.
16
+ search_url (str): The URL used to search for APKs on APKFab.
17
+ headers (dict): The headers used for making HTTP requests.
18
+ session (requests.Session): The session object used for making HTTP requests.
19
+
20
+ Methods:
21
+ search_apk() -> None | tuple[str, str]:
22
+ Searches for the APK on APKFab and returns the title and link if found.
23
+
24
+ find_versions(apk_link: str) -> list[tuple[str, str]]:
25
+ Finds and returns a list of versions and their download links for the given APK link.
26
+ """
27
+
28
+ def __init__(self, pkg_name: str):
29
+ self.pkg_name = pkg_name
30
+ self.base_url = "https://apkfab.com"
31
+ self.search_url = self.base_url + "/search?q="
32
+ self.headers = {
33
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
34
+ "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
35
+ "cache-control": "no-cache",
36
+ "dnt": "1",
37
+ "pragma": "no-cache",
38
+ "priority": "u=0, i",
39
+ "referer": "https://apkfab.com/search",
40
+ "sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
41
+ "sec-ch-ua-mobile": "?0",
42
+ "sec-ch-ua-platform": '"Windows"',
43
+ "sec-fetch-dest": "document",
44
+ "sec-fetch-mode": "navigate",
45
+ "sec-fetch-site": "same-origin",
46
+ "sec-fetch-user": "?1",
47
+ "upgrade-insecure-requests": "1",
48
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0",
49
+ }
50
+ self.session = requests.Session()
51
+
52
+ def search_apk(self) -> None | tuple[str, str]:
53
+ """
54
+ Searches for the APK on APKFab and returns the title and link if found.
55
+
56
+ Returns:
57
+ None: If no matching APK is found.
58
+ tuple[str, str]: A tuple containing the title and link of the matching APK if found.
59
+ """
60
+ pkg_name = self.pkg_name
61
+ url = self.search_url + pkg_name
62
+ response: requests.Response = self.session.get(url, headers=self.headers)
63
+ soup = BeautifulSoup(response.text, "html.parser")
64
+ search_result = soup.find("div", {"class": "search-white"})
65
+ if search_result:
66
+ container = search_result.find("div", {"class": "container"})
67
+ if container:
68
+ lists = container.find("div", {"class": "list-template lists"})
69
+ if lists:
70
+ items = lists.find_all("div", {"class": "list"})
71
+ if items:
72
+ item = items[0]
73
+ apk_title = item.find("div", {"class": "title"}).text
74
+ apk_link = item.find("a")["href"]
75
+ package_name = apk_link.split("/")[-1]
76
+ if package_name == pkg_name:
77
+ return apk_title, apk_link
78
+ return None
79
+
80
+ def find_versions(self, apk_link: str) -> list[tuple[str, str]]:
81
+ """
82
+ Finds and returns a list of versions and their download links for the given APK link.
83
+
84
+ Parameters:
85
+ apk_link (str): The link to the APK on the APKFab website.
86
+
87
+ Returns:
88
+ list[tuple[str, str]]: A list of tuples, where each tuple contains the version number
89
+ and its corresponding download link. If no versions are found, an empty list is returned.
90
+ """
91
+ versions_info = []
92
+ if apk_link.startswith(self.base_url):
93
+ url = apk_link + "/versions"
94
+ response: requests.Response = self.session.get(url, headers=self.headers)
95
+ soup = BeautifulSoup(response.text, "html.parser")
96
+ versions_list = soup.find("div", {"class": "version_history"})
97
+
98
+ if versions_list:
99
+ versions = versions_list.find_all("div", {"class": "list"})
100
+ if versions:
101
+ for version in versions:
102
+ package_info = version.find(
103
+ "div", {"class": "package_info open_info"}
104
+ )
105
+ if package_info:
106
+ title = package_info.find("div", {"class": "title"})
107
+ apk_version = title.find(
108
+ "span", {"class": "version"}
109
+ ).text.strip()
110
+ dl = version.find(
111
+ "div", {"class": "v_h_button button_down"}
112
+ )
113
+ if dl:
114
+ download_link = dl.find("a")["href"]
115
+ versions_info.append((apk_version, download_link))
116
+ else:
117
+ # Some versions have multiple variants
118
+ versions_info.append((apk_version, url))
119
+
120
+ return versions_info
@@ -29,6 +29,8 @@ class APKPure:
29
29
  def __init__(self, pkg_name: str):
30
30
  self.pkg_name = pkg_name
31
31
  self.base_url = "https://apkpure.net"
32
+ self.cdn_url = "https://d.cdnpure.com/b/APK/"
33
+ self.cdn_version = "?version="
32
34
  self.search_url = self.base_url + "/search?q="
33
35
  self.headers = {
34
36
  "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
@@ -72,6 +74,29 @@ class APKPure:
72
74
  apk_package_name = apk_item["data-dt-pkg"]
73
75
  if apk_package_name == pkg_name:
74
76
  return apk_title, apk_link
77
+ # If site search resulted 0 results, try cdn link
78
+ # https://github.com/AbhiTheModder/apksearch/issues/2
79
+ url = self.cdn_url + pkg_name + self.cdn_version + "latest"
80
+ response: requests.Response = self.session.get(
81
+ url, headers=self.headers, allow_redirects=False
82
+ )
83
+ try:
84
+ location = response.headers.get("Location")
85
+ except AttributeError:
86
+ return None
87
+ if location:
88
+ if location == "https://apkpure.com":
89
+ return None
90
+ response: requests.Response = self.session.head(
91
+ location, allow_redirects=False
92
+ )
93
+ try:
94
+ content = response.headers.get("Content-Disposition")
95
+ except AttributeError:
96
+ return None
97
+ if content:
98
+ apk_title = content.split("filename=")[1].strip('"').split("_")[0]
99
+ return apk_title, location
75
100
  return None
76
101
 
77
102
  def find_versions(self, apk_link: str) -> list[tuple[str, str]]:
@@ -85,24 +110,24 @@ class APKPure:
85
110
  list[tuple[str, str]]: A list of tuples, where each tuple contains the version number
86
111
  and its corresponding download link. If no versions are found, an empty list is returned.
87
112
  """
88
- url = apk_link + "/versions"
89
- response: requests.Response = self.session.get(url, headers=self.headers)
90
- soup = BeautifulSoup(response.text, "html.parser")
91
- versions_list = soup.find("ul", {"class": "version-list"})
92
113
  versions_info = []
114
+ if apk_link.startswith(self.base_url):
115
+ url = apk_link + "/versions"
116
+ response: requests.Response = self.session.get(url, headers=self.headers)
117
+ soup = BeautifulSoup(response.text, "html.parser")
118
+ versions_list = soup.find("ul", {"class": "version-list"})
93
119
 
94
- if versions_list:
95
- versions = versions_list.find_all(
96
- "li", {"class": re.compile("^version dt-version-item.*")}
97
- )
98
- for version in versions:
99
- version_icon = version.find("a", {"class": "dt-version-icon"})
100
- version_info = version.find("div", {"class": "version-info"})
101
- if version_icon and version_info:
102
- version_number = version_info.find(
103
- "span", {"class": "name one-line"}
104
- ).text
105
- download_url = self.base_url + version_icon["href"]
106
- versions_info.append((version_number, download_url))
107
-
120
+ if versions_list:
121
+ versions = versions_list.find_all(
122
+ "li", {"class": re.compile("^version dt-version-item.*")}
123
+ )
124
+ for ver in versions:
125
+ version_icon = ver.find("a", {"class": "dt-version-icon"})
126
+ version_info = ver.find("div", {"class": "version-info"})
127
+ if version_icon and version_info:
128
+ version_number = version_info.find(
129
+ "span", {"class": "name one-line"}
130
+ ).text
131
+ download_url = self.base_url + version_icon["href"]
132
+ versions_info.append((version_number, download_url))
108
133
  return versions_info
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: apksearch
3
- Version: 1.2.0
3
+ Version: 1.2.6
4
4
  Summary: Search for apks on varius websites
5
5
  Author-email: Abhi <allinoneallinone00@gmail.com>
6
6
  License: MIT License
@@ -28,7 +28,7 @@ License: MIT License
28
28
  Project-URL: homepage, https://github.com/AbhiTheModder/apksearch.git
29
29
  Project-URL: source, https://github.com/AbhiTheModder/apksearch.git
30
30
  Project-URL: issues, https://github.com/AbhiTheModder/apksearch.git/issues
31
- Classifier: Development Status :: 4 - Beta
31
+ Classifier: Development Status :: 5 - Production/Stable
32
32
  Classifier: Natural Language :: English
33
33
  Classifier: Intended Audience :: Developers
34
34
  Classifier: License :: OSI Approved :: MIT License
@@ -50,31 +50,55 @@ Requires-Dist: pytest>=7.4.3; extra == "dev"
50
50
  Requires-Dist: black>=23.12.1; extra == "dev"
51
51
  Requires-Dist: flake8>=6.1.0; extra == "dev"
52
52
 
53
- # apksearch
53
+ <h1 align="center">apksearch</h1>
54
54
 
55
55
  `apksearch` is a Python library designed to search for APK files on different APK websites, such as APKPure and APKMirror. It allows users to find APKs, check for available versions, and retrieve download links.
56
56
 
57
57
  **The Inspiration:**
58
58
  There were countless occasions when I needed a specific APK for a package name, only to find it unavailable on popular platforms. This led to the tedious task of manually visiting multiple websites and searching one by one.
59
+ <details>
60
+ <summary>screenshot</summary>
61
+ <p align="center">
62
+ <img width="500" src="https://github.com/user-attachments/assets/cd54eaeb-a56b-40b3-835f-b48b1e7772f3"></img><br>
63
+ As you can see, Roblox version <code>2.647.716</code> is not available on APKPure and APKCombo, this helped me avoid going through these sites.
64
+ </p>
65
+ </details>
66
+
67
+ **P.S:** If you're looking for an APK downloader, I highly recommend using [apkeep](https://github.com/EFForg/apkeep).
59
68
 
60
69
  # Features
61
70
 
62
71
  - **Search APKs:** The library provides methods to search for APKs using package names.
63
- - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from APKPure and APKMirror.
72
+ - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from various websites.
64
73
  - **Command-Line Interface:** A CLI is available for users to search for APKs directly from the command line.
65
74
 
75
+ ## Supported Websites
76
+
77
+ - [APKPure](https://apkpure.net/)
78
+ - [APKMirror](https://www.apkmirror.com/)
79
+ - [APKCombo](https://apkcombo.app/)
80
+ - [APKFab](https://apkfab.com/)
81
+ - [Appteka](https://appteka.store/)
82
+ - [APKAD](https://apk.ad/)
83
+
84
+ > [!NOTE]
85
+ > **For site owners:**
86
+ > If you're the owner of a website that's not listed here and you'd like to add support for it, feel free to open an issue or submit a pull request. I'm open to adding more websites to the library.
87
+ > I respect the _value of user engagement and the revenue_ it generates for your site. To honor this, I have deliberately avoided including a download feature in the library, ensuring users will still need to visit your website, maintaining its traffic and engagement.
88
+ > Additionally, I kindly ask that you **refrain from enforcing strict blocking measures**, such as aggressive Cloudflare rules, as the library is designed to work collaboratively rather than disruptively. Thank you!
89
+
66
90
  ## Installation
67
91
 
68
92
  To install the `apksearch` library, use the following command:
69
93
 
70
94
  ```sh
71
- pip install git+https://github.com/AbhiTheModder/apksearch.git
95
+ pip install -U git+https://github.com/AbhiTheModder/apksearch.git
72
96
  ```
73
97
 
74
98
  OR, through pip:
75
99
 
76
100
  ```sh
77
- pip install apksearch
101
+ pip install -U apksearch
78
102
  ```
79
103
 
80
104
  ## Usage
@@ -117,7 +141,7 @@ if result:
117
141
 
118
142
  ### Classes and Methods
119
143
 
120
- #### `APKPure`
144
+ #### `APKPure` | `APKCombo` | `APKFab`
121
145
 
122
146
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
123
147
  - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKPure and returns the title and link if found.
@@ -129,17 +153,11 @@ if result:
129
153
  - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKMirror and returns the title and link if found.
130
154
  - **`find_version(self, apk_link: str, version: str) -> str`**: Finds and returns the download link for the given APK link and version.
131
155
 
132
- #### `AppTeka`
156
+ #### `AppTeka` | `APKAD`
133
157
 
134
158
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
135
159
  - **`search_apk(self, version: str = None) -> None | tuple[str, str]`**: Searches for the APK on AppTeka and returns the title and link if found. If a version is provided, it checks if that version is available and returns the corresponding download link, None otherwise. If no version is provided, it returns the link for the latest version available.
136
160
 
137
- #### `APKCombo`
138
-
139
- - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
140
- - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKCombo and returns the title and link if found.
141
- - **`find_versions(self, apk_link: str) -> list[tuple[str, str]]`**: Finds and returns a list of versions and their download links for the given APK link.
142
-
143
161
  ### Testing
144
162
 
145
163
  The project includes tests for the `sites` classes. To run the tests, use the following command:
@@ -152,6 +170,11 @@ pytest
152
170
 
153
171
  - [ ] Add more websites to search for APKs.
154
172
 
173
+ ## Acknowledgements
174
+
175
+ - [APKUpdater](https://github.com/rumboalla/apkupdater) for APKMirror API.
176
+ - [apkeep](https://github.com/EFForg/apkeep) for APKPure API.
177
+
155
178
  ## License
156
179
 
157
180
  This project is licensed under the MIT License. See the [LICENSE](https://github.com/AbhiTheModder/apksearch/blob/main/LICENSE) file for more details.
@@ -159,3 +182,5 @@ This project is licensed under the MIT License. See the [LICENSE](https://github
159
182
  ## Contributing
160
183
 
161
184
  Contributions are welcome! Please open an issue or submit a pull request on [GitHub](https://github.com/AbhiTheModder/apksearch).
185
+
186
+ If you find this project helpful, please consider giving it a ⭐. Your support is greatly appreciated!
@@ -11,11 +11,14 @@ apksearch.egg-info/entry_points.txt
11
11
  apksearch.egg-info/requires.txt
12
12
  apksearch.egg-info/top_level.txt
13
13
  apksearch/sites/__init__.py
14
+ apksearch/sites/apkad.py
14
15
  apksearch/sites/apkcombo.py
16
+ apksearch/sites/apkfab.py
15
17
  apksearch/sites/apkmirror.py
16
18
  apksearch/sites/apkpure.py
17
19
  apksearch/sites/appteka.py
18
20
  tests/test_apkcombo.py
21
+ tests/test_apkfab.py
19
22
  tests/test_apkmirror.py
20
23
  tests/test_apkpure.py
21
24
  tests/test_appteka.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "apksearch"
7
- version = "1.2.0"
7
+ version = "1.2.6"
8
8
  description = "Search for apks on varius websites"
9
9
  authors = [{ name = "Abhi", email = "allinoneallinone00@gmail.com" }]
10
10
  license = { file = "LICENSE" }
@@ -12,7 +12,7 @@ readme = "README.md"
12
12
  requires-python = ">=3.10"
13
13
  dependencies = ["beautifulsoup4>=4.12.3", "requests>=2.32.3"]
14
14
  classifiers = [
15
- "Development Status :: 4 - Beta",
15
+ "Development Status :: 5 - Production/Stable",
16
16
  "Natural Language :: English",
17
17
  "Intended Audience :: Developers",
18
18
  "License :: OSI Approved :: MIT License",
@@ -0,0 +1,38 @@
1
+ from apksearch.sites.apkfab import APKFab
2
+
3
+
4
+ def test_search_apk():
5
+ query = "com.roblox.client"
6
+ apkfab = APKFab(query)
7
+ result = apkfab.search_apk()
8
+
9
+ assert result is not None, "No APK found for the query."
10
+ assert isinstance(result, tuple), "Result should be a tuple."
11
+ assert len(result) == 2, "Tuple should contain two elements."
12
+ assert isinstance(result[0], str), "First element of the tuple should be a string."
13
+ assert isinstance(result[1], str), "Second element of the tuple should be a string."
14
+
15
+
16
+ def test_find_versions():
17
+ query = "com.roblox.client"
18
+ apkfab = APKFab(query)
19
+ result = apkfab.search_apk()
20
+
21
+ if result:
22
+ apk_link = result[1]
23
+ versions = apkfab.find_versions(apk_link)
24
+
25
+ assert isinstance(versions, list), "Versions should be a list."
26
+ assert len(versions) > 0, "No versions found."
27
+ assert all(
28
+ isinstance(version, tuple) for version in versions
29
+ ), "Each version should be a tuple."
30
+ assert all(
31
+ len(version) == 2 for version in versions
32
+ ), "Each version tuple should contain two elements."
33
+ assert all(
34
+ isinstance(version[0], str) for version in versions
35
+ ), "First element of each version tuple should be a string."
36
+ assert all(
37
+ isinstance(version[1], str) for version in versions
38
+ ), "Second element of each version tuple should be a string."
File without changes
File without changes