|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | + |
| 3 | +__title__ = "ProxyGrablin" |
| 4 | +__description__ = "A simple proxy scraper and proxy checker with GUI." |
| 5 | +__author__ = "DrPython3" |
| 6 | +__contact__ = "https://github.com/DrPython3" |
| 7 | +__date__ = "2024-10-03" |
| 8 | +__version__ = "0.1" |
| 9 | + |
| 10 | +""" |
| 11 | +ProxyGrablin by DrPython3 (C) 2024 |
| 12 | +
|
| 13 | +A simple and easy to use proxy scraper and checker with a GUI. |
| 14 | +
|
| 15 | +If you want to support this project, please consider a donation or tip me a coffee: |
| 16 | +
|
| 17 | +(BTC): 16KLyV8C1S9NGkPeaPTGZxahvh1uw9aQCv |
| 18 | +(LTC): LboqWKdYjmVGu44kWkSSFTadVmzSu4B3sE |
| 19 | +
|
| 20 | +Every support is appreciated! |
| 21 | +
|
| 22 | +And do not forget to check the repository for Updates. |
| 23 | +""" |
| 24 | + |
| 25 | +# TODO: Add threading-support. |
| 26 | +# TODO: Check logs and reduce to minimum. |
| 27 | +# TODO: Integrate live output in GUI. |
| 28 | + |
| 29 | +# Needed modules: |
| 30 | +import sys |
| 31 | +try: |
| 32 | + from PyQt5.QtWidgets import QApplication |
| 33 | + from PyQt5.QtWidgets import QWidget |
| 34 | + from PyQt5.QtWidgets import QPushButton |
| 35 | + from PyQt5.QtWidgets import QVBoxLayout |
| 36 | + from PyQt5.QtWidgets import QHBoxLayout |
| 37 | + from PyQt5.QtWidgets import QLabel |
| 38 | + from PyQt5.QtWidgets import QFileDialog |
| 39 | + from PyQt5.QtWidgets import QInputDialog |
| 40 | + from PyQt5.QtWidgets import QMessageBox |
| 41 | + from PyQt5.QtWidgets import QTextEdit |
| 42 | + from PyQt5.QtWidgets import QComboBox |
| 43 | + from PyQt5.QtWidgets import QSpinBox |
| 44 | + from PyQt5.QtGui import QPixmap |
| 45 | + from inc_scraper import scraper |
| 46 | + from inc_checker import checker |
| 47 | + from inc_etc import writer |
| 48 | + from inc_etc import logging |
| 49 | +except: |
| 50 | + sys.exit("[ERROR] Cannot import needed modules.\n\n") |
| 51 | + |
| 52 | +# ProxyGrablin: |
| 53 | +class ProxyGrablin(QWidget): |
| 54 | + def __init__(self): |
| 55 | + super().__init__() |
| 56 | + # Needed variables: |
| 57 | + self.test_url = "http://www.google.com" # Standard URL for proxy checker |
| 58 | + self.selected_proxy_type = "http" # Standard proxy type for proxy checker |
| 59 | + self.timeout_value = 3 # Standard timeout for proxy checker |
| 60 | + self.initUI() |
| 61 | + |
| 62 | + def initUI(self): |
| 63 | + # GUI setup: |
| 64 | + main_layout = QHBoxLayout() |
| 65 | + control_layout = QVBoxLayout() |
| 66 | + |
| 67 | + # Logo: |
| 68 | + self.label = QLabel(self) |
| 69 | + pixmap = QPixmap("logo.png") |
| 70 | + self.label.setPixmap(pixmap) |
| 71 | + self.label.setScaledContents(True) |
| 72 | + self.label.resize(pixmap.width(), pixmap.height()) |
| 73 | + control_layout.addWidget(self.label) |
| 74 | + |
| 75 | + self.label = QLabel("Simple Proxy Scraper & Checker v0.1", self) |
| 76 | + control_layout.addWidget(self.label) |
| 77 | + |
| 78 | + # Scraper button: |
| 79 | + self.scraper_button = QPushButton("Scrape Proxies", self) |
| 80 | + self.scraper_button.clicked.connect(self.proxy_scraper) |
| 81 | + control_layout.addWidget(self.scraper_button) |
| 82 | + |
| 83 | + # Test URL: |
| 84 | + self.label_url = QLabel(f"Test URL: {self.test_url}", self) |
| 85 | + control_layout.addWidget(self.label_url) |
| 86 | + |
| 87 | + # Test URL setting: |
| 88 | + self.set_url_button = QPushButton("Change test URL", self) |
| 89 | + self.set_url_button.clicked.connect(self.set_test_url) |
| 90 | + control_layout.addWidget(self.set_url_button) |
| 91 | + |
| 92 | + # Set proxy type for checker: |
| 93 | + self.proxy_type_label = QLabel("Set proxy type:", self) |
| 94 | + control_layout.addWidget(self.proxy_type_label) |
| 95 | + |
| 96 | + self.proxy_type_setting = QComboBox(self) |
| 97 | + self.proxy_type_setting.addItems(["http", "https", "socks4", "socks5"]) |
| 98 | + self.proxy_type_setting.currentTextChanged.connect(self.set_proxy_type) |
| 99 | + control_layout.addWidget(self.proxy_type_setting) |
| 100 | + |
| 101 | + # Set timeout for checker: |
| 102 | + self.timeout_label = QLabel("Set default timeout (sec):", self) |
| 103 | + control_layout.addWidget(self.timeout_label) |
| 104 | + |
| 105 | + self.timeout_setting = QSpinBox(self) |
| 106 | + self.timeout_setting.setMinimum(1) |
| 107 | + self.timeout_setting.setMaximum(60) |
| 108 | + self.timeout_setting.setValue(self.timeout_value) |
| 109 | + self.timeout_setting.valueChanged.connect(self.set_timeout) |
| 110 | + control_layout.addWidget(self.timeout_setting) |
| 111 | + |
| 112 | + # Checker button: |
| 113 | + self.checker_button = QPushButton("Check Proxies", self) |
| 114 | + self.checker_button.clicked.connect(self.proxy_checker) |
| 115 | + control_layout.addWidget(self.checker_button) |
| 116 | + |
| 117 | + # Add Control_Layout to Main_Layout: |
| 118 | + main_layout.addLayout(control_layout) |
| 119 | + |
| 120 | + # Area for text output: |
| 121 | + self.result_display = QTextEdit(self) |
| 122 | + self.result_display.setReadOnly(True) |
| 123 | + main_layout.addWidget(self.result_display) |
| 124 | + |
| 125 | + # Build GUI: |
| 126 | + self.setLayout(main_layout) |
| 127 | + self.setWindowTitle("ProxyGrablin v0.1") |
| 128 | + self.show() |
| 129 | + |
| 130 | + def proxy_scraper(self): |
| 131 | + """ |
| 132 | + Scrapes potential proxy IPs from given URLs and saves all found unique |
| 133 | + IP addresses to a text file. URLs are read from a text file, too. |
| 134 | + URL file must provide one URL per line. |
| 135 | + :return: None |
| 136 | + """ |
| 137 | + scraped_ips = [] |
| 138 | + unique_ips = [] |
| 139 | + sources_to_scrape = 0 |
| 140 | + sources_scraped = 0 |
| 141 | + logging("Scraper started.") |
| 142 | + self.result_display.clear() |
| 143 | + self.result_display.append("Scraper started.") |
| 144 | + # Get file with URL list: |
| 145 | + urls, _ = QFileDialog.getOpenFileName(self, "Choose URL list for scraper", "", "Text Files (*.txt)") |
| 146 | + if urls: |
| 147 | + logging("Importing URL list.") |
| 148 | + # Import URL list from file: |
| 149 | + with open(urls, "r") as input_file: |
| 150 | + sources = [line.strip() for line in input_file.readlines()] |
| 151 | + # Scrape proxies from URLs: |
| 152 | + if sources: |
| 153 | + sources_to_scrape = len(sources) |
| 154 | + logging(f"Found {str(sources_to_scrape)} URLs to scrape.") |
| 155 | + for source in sources: |
| 156 | + sources_scraped += 1 |
| 157 | + print(f"Scraping {str(sources_scraped)} of {str(sources_to_scrape)}: {source}") |
| 158 | + result = scraper(source) |
| 159 | + scraped_ips.extend(result) |
| 160 | + else: |
| 161 | + logging("No URLs to scrape.") |
| 162 | + self.result_display.append("No URLs to scrape.") |
| 163 | + # Remove duplicates from scraped data and fill unique_ips list: |
| 164 | + if len(scraped_ips) > 0: |
| 165 | + logging("Removing duplicate proxies.") |
| 166 | + print("Removing duplicate proxies.") |
| 167 | + ip_pool = set() |
| 168 | + for ip in scraped_ips: |
| 169 | + if ip not in ip_pool: |
| 170 | + unique_ips.append(ip) |
| 171 | + ip_pool.add(ip) |
| 172 | + else: |
| 173 | + continue |
| 174 | + try: |
| 175 | + del ip_pool |
| 176 | + except: |
| 177 | + pass |
| 178 | + # Save unique_ips list to file: |
| 179 | + if len(unique_ips) > 0: |
| 180 | + logging("Saving proxies to file.") |
| 181 | + output_file, _ = QFileDialog.getSaveFileName(self, "Save scraped IPs", "", "Text files (*.txt)") |
| 182 | + if output_file: |
| 183 | + writer(unique_ips, output_file) |
| 184 | + logging("Scraped proxies saved to file.") |
| 185 | + self.result_display.append("Scraped IPs saved to file.") |
| 186 | + print("Scraped proxies saved to file.") |
| 187 | + else: |
| 188 | + logging("Output file not specified.") |
| 189 | + self.result_display.append("Output file not specified.") |
| 190 | + else: |
| 191 | + logging("No proxies found.") |
| 192 | + self.result_display.append("No proxies found.") |
| 193 | + else: |
| 194 | + logging("No URLs to scrape.") |
| 195 | + self.result_display.append("No URLs to scrape.") |
| 196 | + |
| 197 | + def set_test_url(self): |
| 198 | + """ |
| 199 | + Used to set the URL which the proxy checker uses to test given proxies. |
| 200 | + :return: None |
| 201 | + """ |
| 202 | + logging("Setting new test URL.") |
| 203 | + new_url, ok = QInputDialog.getText(self, "Enter URL for checker", "Enter the URL for the checker:") |
| 204 | + if ok and new_url: |
| 205 | + self.test_url = new_url |
| 206 | + QMessageBox.information(self, "Test URL", f"New test URL set: {self.test_url}") |
| 207 | + logging("New test URL set.") |
| 208 | + self.result_display.append(f"New test URL set: {self.test_url}") |
| 209 | + else: |
| 210 | + QMessageBox.warning(self, "Invalid URL", "Entered URL is invalid, test URL not changed.") |
| 211 | + logging("Entered URL is invalid, test URL not changed.") |
| 212 | + self.result_display.append("Entered URL is invalid, test URL not changed.") |
| 213 | + self.label_url.setText(f"Test URL: {self.test_url}") |
| 214 | + |
| 215 | + def set_proxy_type(self, proxy_type): |
| 216 | + """ |
| 217 | + Used to set the proxy type the checker will test for. |
| 218 | + :param proxy_type: Proxy type used for the checker. |
| 219 | + :return: None |
| 220 | + """ |
| 221 | + self.selected_proxy_type = proxy_type |
| 222 | + logging(f"Proxy type to use set to {proxy_type}") |
| 223 | + self.result_display.append(f"Proxy type set to {proxy_type}") |
| 224 | + |
| 225 | + def set_timeout(self, value): |
| 226 | + """ |
| 227 | + Used to set the default timeout for the proxy checker. |
| 228 | + :param value: timeout in secounds |
| 229 | + :return: None |
| 230 | + """ |
| 231 | + self.timeout_value = value |
| 232 | + logging(f"Timeout value changed to {str(value)}") |
| 233 | + self.result_display.append(f"Timeout value changed to {str(value)}") |
| 234 | + self.timeout_label.setText(f"Timeout value changed to {str(value)}") |
| 235 | + |
| 236 | + def proxy_checker(self): |
| 237 | + """ |
| 238 | + Starts the proxy checker. IPs will be checked one by one. Working proxies |
| 239 | + are saved to a text file. The IPs to check have to be provided in a text file |
| 240 | + with one IP per line. |
| 241 | + :return: None |
| 242 | + """ |
| 243 | + working_proxies = [] |
| 244 | + amount_ips = 0 |
| 245 | + amount_proxies = 0 |
| 246 | + self.result_display.clear() |
| 247 | + logging("Proxy Checker started.") |
| 248 | + self.result_display.append("Proxy Checker started - this will take some time. Please, be patient!") |
| 249 | + ips, _ = QFileDialog.getOpenFileName(self, "Choose file with proxy IPs", "", "Text files (*.txt)") |
| 250 | + # Get IPs from file: |
| 251 | + if ips: |
| 252 | + with open(ips, "r") as input_file: |
| 253 | + ips_to_check = [line.strip() for line in input_file.readlines()] |
| 254 | + amount_ips = len(ips_to_check) |
| 255 | + print(f"{str(amount_ips)} loaded for checking.") |
| 256 | + logging(f"{amount_ips} loaded for checking.") |
| 257 | + logging("Checking IPs ...") |
| 258 | + # Check every IP for working proxy: |
| 259 | + if ips_to_check: |
| 260 | + for ip in ips_to_check: |
| 261 | + result = checker(ip, self.selected_proxy_type, self.test_url, self.timeout_value) |
| 262 | + if "alive" in result: |
| 263 | + working_proxies.append(ip) |
| 264 | + print(result) |
| 265 | + else: |
| 266 | + self.result_display.append("No IPs to check.") |
| 267 | + logging("No IPs to check.") |
| 268 | + # Save working proxies to file: |
| 269 | + if working_proxies: |
| 270 | + amount_proxies = len(working_proxies) |
| 271 | + logging(f"Saving {str(amount_proxies)} {self.selected_proxy_type.upper()} proxies to file.") |
| 272 | + output_file, _ = QFileDialog.getSaveFileName(self, f"Save working {self.selected_proxy_type.upper()} proxies", "", "Text files (*.txt)") |
| 273 | + if output_file: |
| 274 | + writer(working_proxies, output_file) |
| 275 | + logging("Proxies saved to file.") |
| 276 | + else: |
| 277 | + logging("No output file specified.") |
| 278 | + # Show working proxies in GUI: |
| 279 | + self.result_display.clear() |
| 280 | + self.result_display.append("WORKING PROXIES:\n" + "-" * 25) |
| 281 | + for proxy in working_proxies: |
| 282 | + self.result_display.append(proxy) |
| 283 | + else: |
| 284 | + self.result_display.append("No working proxies found.") |
| 285 | + logging("No working proxies found.") |
| 286 | + else: |
| 287 | + logging("No file to load IPs from.") |
| 288 | + self.result_display.append("No file to load IPs from.") |
| 289 | + |
| 290 | +# Main: |
| 291 | +if __name__ == "__main__": |
| 292 | + app = QApplication(sys.argv) |
| 293 | + ex = ProxyGrablin() |
| 294 | + sys.exit(app.exec_()) |
| 295 | + |
| 296 | +# DrPython3 (C) 2024 |
0 commit comments