├── .gitignore ├── README.md ├── img ├── 1.png ├── 2.png └── 3.png ├── main.py ├── pkg.bat └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | *.jpg 132 | .vscode/ 133 | test_printer.py -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 更新日志 2 | ## 2023/12/18 目前漫画平台机制修改,本工具已经不能正常使用,后续有空更新 3 | - [ 拷贝漫画下载器食用说明](#head1) 4 | - [step1. 确保你的windows电脑安装了chrome浏览器](#head2) 5 | - [step2. 下载适合你的chrome浏览器版本的驱动程序](#head3) 6 | - [step3. 解压chromedriver.exe驱动文件到特定目录](#head4) 7 | - [step4. 将驱动文件目录放到用户/系统环境变量下](#head5) 8 | - [step 5. 双击main.exe使用(允许程序使用网络)](#head6) 9 | 10 | 11 | 12 | # 拷贝漫画下载器食用说明 13 | 14 | ## step1. 确保你的windows电脑安装了chrome浏览器 15 | 16 | 如没有,在下面地址下载安装: 17 | 18 | https://www.google.cn/intl/zh-CN/chrome/ 19 | 20 | 21 | 22 | ## step2. 下载适合你的chrome浏览器版本的驱动程序 23 | 24 | 根据你的chrome浏览器版本(在下图位置找到): 25 | 26 | 27 | 28 | 去下面的网站下载驱动程序: 29 | 30 | http://chromedriver.storage.googleapis.com/index.html 31 | 32 | > 尽量选择与你的浏览器版本一致的驱动,最后几位数字可以不同 33 | > 34 | > 选择后缀名为`win32`的压缩包下载,里面是一个名为**chromedriver.exe**的可执行驱动文件 35 | 36 | 37 | 38 | ## step3. 解压chromedriver.exe驱动文件到特定目录 39 | 40 | - 新建一个全英文路径的文件夹,例如: `D:\Software\chrome_driver\` 41 | - 将上面压缩包中的**chromedriver.exe**解压到目录中 42 | - 最终路径为`D:\Software\chrome_driver\chromedriver.exe` 43 | 44 | 45 | 46 | ## step4. 将驱动文件目录放到用户/系统环境变量下 47 | 48 | 49 | 50 | 1. 搜索or控制面板找到环境变量 51 | 2. 点击【环境变量】 52 | 3. 选中用户栏中的【PATH】,双击打开“编辑环境变量对话框” 53 | 4. 新建or双击底部空白位置,将刚才的驱动目录输入,如图③所示 54 | 5. 点击确定 55 | 6. 重启电脑 56 | 57 | 58 | 59 | ## step 5. 双击main.exe使用(允许程序使用网络) 60 | 考虑到信息展示问题,推荐使用**windows powershell**在`main.exe`目录下打开,然后执行 61 | ``` 62 | .\main.exe 63 | ``` 64 | 65 | ## 备注 66 | 1. 如果下载遇到错误,请尝试关闭电脑的代理工具,再重新打开软件 67 | -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Holy-Shine/copymanga-downloader/142c1628c69d651315920685761c713962945087/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Holy-Shine/copymanga-downloader/142c1628c69d651315920685761c713962945087/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Holy-Shine/copymanga-downloader/142c1628c69d651315920685761c713962945087/img/3.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 颜色约定 3 | 4 | 引导用户选择: 白色 5 | 系统信息输出: 黄色 6 | SUCCESS: 绿色 7 | ERROR: 红色 8 | 9 | 10 | ''' 11 | 12 | 13 | from selenium import webdriver 14 | from selenium.webdriver.support import expected_conditions as EC 15 | from selenium.webdriver.support.ui import WebDriverWait 16 | from selenium.webdriver.common.keys import Keys 17 | from selenium.webdriver.common.by import By 18 | from selenium.webdriver.chrome.options import Options 19 | from prettytable import PrettyTable 20 | 21 | 22 | import os,re 23 | import time 24 | import requests 25 | import urllib3 26 | import wasabi 27 | from wasabi import msg 28 | from alive_progress import alive_bar 29 | 30 | msg.show_color=True 31 | urllib3.disable_warnings() 32 | def waitElementOccur(browser, XPATH, sec=10): 33 | return WebDriverWait(browser,sec).until(EC.presence_of_element_located((By.XPATH, XPATH))) 34 | 35 | def waitElementClickable(browser, XPATH, sec=10): 36 | return WebDriverWait(browser, sec).until(EC.element_to_be_clickable((By.XPATH, XPATH))) 37 | 38 | def showCurPageMange(browser, page_cur, page_total): 39 | '表格展示当前页漫画' 40 | manga_table_cur_page = PrettyTable(field_names=['序号','漫画名', '作者']) 41 | manga_list_ele = browser.find_elements_by_class_name('exemptComicItem') 42 | for i, manga_item_ele in enumerate(manga_list_ele): 43 | manga_name, manga_author = manga_item_ele.text.split('\n') 44 | manga_author = manga_author[3:] 45 | manga_table_cur_page.add_row([str(i+1), manga_name, manga_author]) 46 | 47 | log(manga_table_cur_page.get_string(title=f'''当前漫画页: {page_cur}/{page_total}''')) 48 | return manga_list_ele 49 | 50 | 51 | def jumpPage(browser, page_n): 52 | '跳转漫画页' 53 | ele_to_page = waitElementOccur(browser, '//*[@id="comic"]/div[2]/ul/div/input') 54 | ele_to_page.clear() 55 | ele_to_page.send_keys(str(page_n)) 56 | waitElementClickable(browser, '//*[@id="comic"]/div[2]/ul/li[14]/a').click() 57 | 58 | 59 | def jumpBack(browser): 60 | '关闭当前窗口, 切回上一个窗口' 61 | browser.close() 62 | browser.switch_to.window(browser.window_handles[-1]) 63 | 64 | def mangaIdxParser(term): 65 | '解析是第几话' 66 | pt1 = r'\d+\.\d+' 67 | idx_t = re.findall(pt1, term) 68 | if len(idx_t)>0: 69 | return float(idx_t[0]) 70 | 71 | try: 72 | return int(re.sub('\D', '', term)) 73 | 74 | except Exception: 75 | pass 76 | 77 | return None 78 | 79 | 80 | def scrollBottomToTop(browser): 81 | '''缓慢从页面底部滑动到页面顶部 82 | 83 | 100/0.1s 84 | ''' 85 | step=100 86 | t = 0.1 87 | page_height = browser.execute_script('return document.body.scrollHeight') 88 | 89 | # 滑倒底部 90 | browser.execute_script(f"window.scrollTo(0, {page_height});") 91 | time.sleep(2) 92 | while page_height>100: 93 | 94 | browser.execute_script(f"window.scrollTo(0, {page_height-step});") 95 | page_height-=step 96 | time.sleep(t) 97 | 98 | 99 | 100 | def getMangaStatus(browser, key_word='話'): 101 | 102 | '''获取漫画状态 103 | 104 | return: 105 | { 106 | num: 数量, 107 | range: 范围, 108 | links: 连接元素 109 | range_name: 范围名 110 | } 111 | ''' 112 | status = {} 113 | xpath_base = f'//*[@id="default{key_word}"]/ul' 114 | ele_item_box = browser.find_element(By.XPATH, xpath_base) 115 | ele_item_list = ele_item_box.find_elements_by_xpath('a') 116 | links = [] # 卷/话链接 117 | ranges = [] # 卷/话范围 118 | ranges_name = [] # 卷/话范围对应的名字 119 | for i,ele in enumerate(ele_item_list): 120 | # 找到链接 121 | links.append(ele.get_attribute('href')) 122 | # 填充对应的话/卷/番外标题代表的数字 123 | vol_name = ele.get_attribute('title') 124 | 125 | # title中没有序号则跳过 126 | idx = mangaIdxParser(vol_name) 127 | if idx==None: 128 | continue 129 | 130 | ranges.append(idx) 131 | ranges_name.append(vol_name) 132 | 133 | status['num'] = len(ele_item_list) 134 | status['links'] = links 135 | status['range'] = ranges 136 | status['range_name'] = ranges_name 137 | 138 | return status 139 | 140 | def cleanScreen(): 141 | '清屏' 142 | os.system('cls') 143 | 144 | def colorStr(s, fg='white', bg=None, bold=True): 145 | '''给字符串着色. 146 | s: 目标串 147 | fg: 前景色 148 | bg: 背景色 149 | bold: 是否加粗 150 | 151 | ''' 152 | return wasabi.color(s, fg=fg, bg=bg, bold=bold) 153 | 154 | def log(s, rank=None): 155 | '打印日志信息' 156 | 157 | if rank == None: 158 | s = colorStr(s, fg='yellow') 159 | print(s) 160 | 161 | def Main(): 162 | msg.info('程序初始化中,请稍后...') 163 | chrome_opt = Options() 164 | chrome_opt.add_argument('log-level=2') 165 | chrome_opt.add_argument('--window-size=1920,1080') 166 | chrome_opt.add_argument('log-level=2') 167 | chrome_opt.add_argument('--headless') 168 | chrome_opt.add_experimental_option('excludeSwitches', ['enable-logging']) 169 | 170 | 171 | browser = webdriver.Chrome(options=chrome_opt) 172 | 173 | browser.get(f'''https://copymanga.org''') 174 | 175 | time_cost = 3 176 | msg.good(f'程序初始化完成!{time_cost}秒后进入搜索页') 177 | while True: 178 | time_cost-=1 179 | if time_cost<0: 180 | break 181 | time.sleep(1) 182 | msg.good(f'\033[A程序初始化完成!{time_cost}秒后进入搜索页') 183 | 184 | while True: 185 | try: 186 | cleanScreen() 187 | manga_search_term = input('请输入要搜索的漫画名,按回车【Enter】结束:') 188 | 189 | # 检查检索串是否合法 190 | if len(manga_search_term) == 0: 191 | msg.warn('检索字符串不合法! 按回车返回搜索页') 192 | input() 193 | continue 194 | 195 | msg.info(f'正在检索【{manga_search_term}】,请稍后...') 196 | search_ele = waitElementOccur(browser, '/html/body/header/div[2]/div/div[8]/div/div/div/div/input') 197 | search_ele.clear() 198 | search_ele.send_keys(manga_search_term) 199 | search_ele.send_keys(Keys.ENTER) 200 | 201 | # 涉及跳转,切换窗口 202 | browser.switch_to.window(browser.window_handles[-1]) 203 | # waitElementClickable(browser, '//*[@id="comic"]/div[2]/ul/li[14]/a') 204 | browser.refresh() 205 | time.sleep(3) 206 | if waitElementOccur(browser, '/html/body/main/div[1]/div[3]/h4/span[2]').text=='0': 207 | msg.warn(f'未找到与搜索串【{manga_search_term}】相关的漫画,请按任意键后重新搜索!') 208 | input() 209 | jumpBack(browser) 210 | continue 211 | time_cost = 3 212 | msg.good(f'检索成功!{time_cost}秒后进入搜索结果页') 213 | while True: 214 | time_cost-=1 215 | if time_cost<0: 216 | break 217 | time.sleep(1) 218 | msg.good(f'\033[A检索成功!{time_cost}秒后进入搜索结果页') 219 | try: 220 | page_total_ele = waitElementOccur(browser, '//*[@id="comic"]/div[2]/ul/li[13]') 221 | except Exception: 222 | msg.warn(f'未找到与搜索串【{manga_search_term}】相关的漫画,请按任意键后重新搜索!') 223 | input() 224 | jumpBack(browser) 225 | continue 226 | if not page_total_ele.text.isdigit(): 227 | page_total = 1 228 | else: 229 | page_total = int(page_total_ele.text[1:]) 230 | page_cur = 1 231 | 232 | # 进入检索状态 233 | while True: 234 | cleanScreen() 235 | ele_manga_list = showCurPageMange(browser, page_cur=page_cur, page_total=page_total) 236 | option = input('请选择下一步操作(按回车结束输入):\n[1]直接选择序号\n[2]跳转页\n[3]返回搜索页\n你的选择: ') 237 | 238 | if option not in '123': 239 | msg.warn('输入非法!输入必须是符合要求的序号,请按任意键重新输入!') 240 | input() 241 | continue 242 | 243 | # 分析选项 244 | if option == '1': 245 | '进入漫画选择状态' 246 | # 选择漫画 247 | while True: 248 | idx_manga = input(f'''请选择漫画序号1~{len(ele_manga_list)}, 按回车【Enter】结束输入:''') 249 | if not idx_manga.isdigit() or int(idx_manga) not in range(1, len(ele_manga_list)+1): 250 | msg.warn('输入非法!输入的漫画序号必须在表格的序号范围内,请按任意键后重新输入!') 251 | input() 252 | continue 253 | else: 254 | break 255 | 256 | # 获取选择漫画的基本信息 257 | 258 | msg.info('正在获取漫画信息,请稍后...') 259 | manga_name, manga_author = ele_manga_list[int(idx_manga)-1].text.split('\n') 260 | manga_author = manga_author[3:] 261 | 262 | manga_href_xpath = f'//*[@id="comic"]/div[1]/div[{idx_manga}]/div[1]/a' 263 | waitElementClickable(browser, manga_href_xpath).click() 264 | 265 | # 切换到当前窗口 266 | browser.switch_to.window(browser.window_handles[-1]) 267 | #等待所有可下载选项加载完毕 268 | time.sleep(3) 269 | 270 | 271 | # 检查 话/卷/番外是否可用 272 | hua_xpath = '/html/body/main/div[2]/div[3]/div[1]/div[1]/ul/li[2]/a' 273 | juan_xpath = '/html/body/main/div[2]/div[3]/div[1]/div[1]/ul/li[3]/a' 274 | fanwai_xpath = '/html/body/main/div[2]/div[3]/div[1]/div[1]/ul/li[4]/a' 275 | 276 | n_try = 0 277 | while True: 278 | if n_try>50: 279 | raise Exception 280 | try: 281 | hua_flag = False if 'disabled' in browser.find_element(By.XPATH, hua_xpath).get_attribute('class') else True 282 | juan_flag = False if 'disabled' in browser.find_element(By.XPATH, juan_xpath).get_attribute('class') else True 283 | fanwai_flag = False if 'disabled' in browser.find_element(By.XPATH, fanwai_xpath).get_attribute('class') else True 284 | break 285 | except Exception: 286 | n_try+=1 287 | browser.refresh() 288 | continue 289 | 290 | 291 | # 分析当前话/卷/番外的状态(多少卷) 292 | manga_status = {} 293 | if hua_flag: 294 | manga_status.update({'hua':getMangaStatus(browser)}) 295 | if juan_flag: 296 | manga_status.update({'juan':getMangaStatus(browser, key_word='卷')}) 297 | if fanwai_flag: 298 | manga_status.update({'fanwai':getMangaStatus(browser, key_word='番外')}) 299 | 300 | 301 | cleanScreen() 302 | log(f'当前选择漫画: 【{manga_name}】') 303 | log(f'当前漫画状态:') 304 | 305 | 306 | data = [ 307 | ('1','话',str(min(manga_status['hua']['range']))+'~'+str(max(manga_status['hua']['range'])) if hua_flag else '不可用'), 308 | ('2','卷',str(min(manga_status['juan']['range']))+'~'+str(max(manga_status['juan']['range'])) if juan_flag else '不可用'), 309 | ('3','番外',str(min(manga_status['fanwai']['range']))+'~'+str(max(manga_status['fanwai']['range'])) if fanwai_flag else '不可用') 310 | ] 311 | manga_info = PrettyTable(field_names=['序号','类型', '状态']) 312 | manga_info.add_rows(data) 313 | 314 | log(manga_info.get_string(title=f'''当前漫画状态''')) 315 | 316 | 317 | # log(f'''1.[话]:\t{str(min(manga_status['hua']['range']))+'~'+str(max(manga_status['hua']['range'])) if hua_flag else '不可用'}''') 318 | # log(f'''2.[卷]:\t{str(min(manga_status['juan']['range']))+'~'+str(max(manga_status['juan']['range'])) if juan_flag else '不可用'}''') 319 | # log(f'''3.[番外]:\t{str(min(manga_status['fanwai']['range']))+'~'+str(max(manga_status['fanwai']['range'])) if fanwai_flag else '不可用'}''') 320 | 321 | 322 | # 选择要下载的类型 323 | types = ['话','卷','番外'] 324 | types_en = ['hua','juan','fanwai'] 325 | while True: 326 | dl_type = input('请选择要下载的类型(话/卷/番外前的序号,请尽量不要选择按【卷】下载),按回车【Enter】结束输入, 按x返回上一层:') 327 | if dl_type.lower() == 'x': 328 | break 329 | if not dl_type.isdigit() or int(dl_type) not in range(1,4): 330 | msg.warn('输入非法!输入的序号必须在序号范围内,并且是合法的序号!请按任意键后重新输入!') 331 | input() 332 | continue 333 | if not hua_flag and dl_type == '1' or not juan_flag and dl_type=='2' or not fanwai_flag and dl_type=='3': 334 | msg.warn('输入的下载类型不可用!请按任意键后重新输入!') 335 | input() 336 | continue 337 | dl_type = int(dl_type)-1 338 | 339 | low, high = min(manga_status[types_en[dl_type]]['range']), max(manga_status[types_en[dl_type]]['range']) 340 | # idx_input_legal = True # 序号输入合法性判断 341 | 342 | while True: 343 | dl_range = [] # 下载范围(上下限, 考虑到有.5的存在) 344 | cmd = input(f'''请选择要下载的【{types[dl_type]}】的序号, 也可输入下载的序号范围, 用'-'隔开,例如1-5. 按回车【Enter】结束输入(输入'x'返回上一层下载类型选择):\n''') 345 | 346 | 347 | if cmd.lower() == 'x': 348 | break 349 | elif re.match(r'^\d+\-\d+$', cmd)!=None: 350 | be = cmd.split('-') 351 | if len(be)!=2 or not be[0].isdigit() or not be[1].isdigit() or int(be[0])>=int(be[1]) or int(be[0])high: 352 | msg.warn('请输入合法的序号范围! 请重新选择范围') 353 | continue 354 | 355 | dl_range.extend([float(be[0]), float(be[1])]) 356 | elif cmd.isdigit(): 357 | idx = int(cmd) 358 | if idxhigh: 359 | msg.warn('请确保序号在范围内!请重新选择范围') 360 | continue 361 | dl_range.extend([idx,idx]) 362 | else: 363 | msg.warn('请输入合法的序号! 请重新选择范围') 364 | continue 365 | 366 | #### 下载流程 ######### 367 | msg.info('开始下载...') 368 | for i, manga_idx in enumerate(manga_status[types_en[dl_type]]['range']): 369 | if manga_idx>=dl_range[0] and manga_idx<=dl_range[1]: 370 | manga_dir = f'./{manga_name}_{manga_idx}_{types[dl_type]}' 371 | if not os.path.exists(manga_dir): 372 | os.mkdir(manga_dir) 373 | 374 | msg.info(f'''开始下载【{manga_name}】第{manga_idx}{types[dl_type]}...''') 375 | link = manga_status[types_en[dl_type]]['links'][i] 376 | js = f'''window.open('{link}');''' 377 | browser.execute_script(js) 378 | browser.switch_to.window(browser.window_handles[-1]) 379 | 380 | # 共 n_jpg 张图片 381 | n_jpg = int(waitElementOccur(browser,'/html/body/div[1]/span[2]').text) 382 | 383 | scroll_step=1000 # 每次滑动1000 384 | scroll_status = 0 385 | flag=True 386 | 387 | with alive_bar(n_jpg) as bar: 388 | for ct in range(n_jpg): 389 | bar() 390 | # 找下一张图片 391 | n_try=0 # 尝试次数 392 | while True: 393 | # 尝试获取图片 394 | if n_try == 50: 395 | flag=False 396 | break 397 | try: 398 | ele_jpg = browser.find_element(By.XPATH, f'/html/body/div[2]/div/ul/li[{ct+1}]') 399 | 400 | 401 | scroll_status+=scroll_step 402 | 403 | break 404 | except Exception: 405 | scrollBottomToTop(browser) 406 | n_try+=1 407 | continue 408 | 409 | if flag==False: 410 | break 411 | 412 | img_ele = ele_jpg.find_element(By.XPATH, 'img') 413 | data_src = img_ele.get_attribute('data-src') 414 | image=requests.get(data_src, verify = False) 415 | with open(manga_dir+'/{0:0>3}.jpg'.format(ct+1),'wb') as wf: 416 | wf.write(image.content) 417 | time.sleep(5) 418 | if flag == False: 419 | msg.fail('下载失败...') 420 | 421 | jumpBack(browser) 422 | browser.switch_to.window(browser.window_handles[-1]) 423 | msg.good('所有任务下载完毕!') 424 | while True: 425 | opt = input('请选择接下来的操作:\n[1]继续下载\n[2]返回上一层 \n[3]退出程序\n你的选择:') 426 | if not opt in ["1","2","3"]: 427 | msg.warn('选项非法,请重新选择') 428 | continue 429 | break 430 | if opt == '1': 431 | continue 432 | elif opt=='2': 433 | break 434 | else: 435 | exit(0) 436 | 437 | jumpBack(browser) 438 | 439 | elif option == '2': 440 | if page_total == 1: 441 | msg.info('当前漫画搜索结果仅有1页,无法跳转!') 442 | continue 443 | '跳页' 444 | while True: 445 | n_page = input(f'''请输入要跳转的页码, 范围为1~{page_total}(当前是第{page_cur}页),按回车【Enter】结束输入:''') 446 | if not n_page.isdigit() or int(n_page) not in range(1, page_total+1) or int(n_page)==page_cur: 447 | msg.warn('输入非法!输入必须是符合要求的页码范围,且不能是当前页,请按任意键重新输入!') 448 | input() 449 | continue 450 | else: 451 | break 452 | jumpPage(browser, page_n=n_page) 453 | msg.info('正在跳转,请稍后...') 454 | time.sleep(3) 455 | page_cur = n_page 456 | 457 | elif option == '3': 458 | '返回搜索页' 459 | jumpBack(browser) 460 | break 461 | except Exception as e: 462 | msg.fail('程序遇到了错误, 程序即将退出') 463 | log(colorStr(str(e), fg='red')) 464 | exit(0) 465 | 466 | 467 | if __name__ == '__main__': 468 | Main() -------------------------------------------------------------------------------- /pkg.bat: -------------------------------------------------------------------------------- 1 | call conda remove -y -n installer --all 2 | call conda create -y --name installer python=3.7 3 | call conda activate installer 4 | call pip install -r requirements.txt 5 | call pyinstaller -F main.py 6 | call conda deactivate installer -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | selenium==3.141.0 2 | prettytable==3.0.0 3 | alive_progress==1.6.2 4 | requests==2.26.0 5 | pyinstaller 6 | wasabi --------------------------------------------------------------------------------