├── .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
--------------------------------------------------------------------------------