From 87243af580dfbbd9314b6c2814400ff48f633379 Mon Sep 17 00:00:00 2001 From: xing-yv Date: Mon, 16 Oct 2023 16:41:48 +0800 Subject: [PATCH 1/3] del setup edition --- src/setup/fanqie_batch.py | 228 ----------------------- src/setup/fanqie_chapter.py | 195 -------------------- src/setup/fanqie_debug.py | 204 --------------------- src/setup/fanqie_normal.py | 182 ------------------- src/setup/fanqie_update.py | 176 ------------------ src/setup/function.py | 347 ------------------------------------ src/setup/main.py | 39 ---- 7 files changed, 1371 deletions(-) delete mode 100644 src/setup/fanqie_batch.py delete mode 100644 src/setup/fanqie_chapter.py delete mode 100644 src/setup/fanqie_debug.py delete mode 100644 src/setup/fanqie_normal.py delete mode 100644 src/setup/fanqie_update.py delete mode 100644 src/setup/function.py delete mode 100644 src/setup/main.py diff --git a/src/setup/fanqie_batch.py b/src/setup/fanqie_batch.py deleted file mode 100644 index d06f327..0000000 --- a/src/setup/fanqie_batch.py +++ /dev/null @@ -1,228 +0,0 @@ -""" -作者:星隅(xing-yv) - -版权所有(C)2023 星隅(xing-yv) - -本软件根据GNU通用公共许可证第三版(GPLv3)发布; -你可以在以下位置找到该许可证的副本: -https://www.gnu.org/licenses/gpl-3.0.html - -根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 -请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 - -本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 - -免责声明: -该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和版权持有人无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 - -请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 - -无论您对程序进行了任何操作,请始终保留此信息。 -""" - -# 导入必要的模块 -import requests -from bs4 import BeautifulSoup -from urllib.parse import urljoin -import re -import os -import time -import datetime - - -def fanqie_b(encoding, user_agent, path_choice, data_folder): - - if not os.path.exists("urls.txt"): - print("url.txt文件不存在") - return "file does not exist" - - try: - # 打开url.txt文件 - with open("urls.txt", "r") as file: - lines = file.readlines() - - # 检查文件是否为空 - if not lines or all(not line.strip() for line in lines): - print("urls.txt文件为空") - return - else: - # 检查每行是否包含"/page/",并且不是空行 - for line in lines: - line = line.strip() - if line and "/page/" not in line: - print(f"语法错误:第{line}行") - return "file syntax is incorrect" - - print("urls.txt文件内容符合要求") - - # 定义文件夹路径 - folder_path = None - # 如果用户选择自定义路径 - if path_choice == 1: - import tkinter as tk - from tkinter import filedialog - # 创建一个Tkinter窗口,但不显示它 - root = tk.Tk() - root.withdraw() - - print("您选择了自定义保存路径,请您在弹出窗口中选择保存文件夹。") - - while True: - - # 弹出文件对话框以选择保存位置和文件名 - folder_path = filedialog.askdirectory() - - # 检查用户是否取消了对话框 - if not folder_path: - # 用户取消了对话框,提示重新选择 - print("您没有选择保存文件夹,请重新选择!") - continue - else: - print("已选择保存文件夹") - break - - # 对于文件中的每个url,执行函数 - for url in lines: - url = url.strip() # 移除行尾的换行符 - if url: # 如果url不为空(即,跳过空行) - download_novels(url, encoding, user_agent, path_choice, folder_path, data_folder) - time.sleep(1) - - except Exception as e: - print(f"发生错误:{str(e)}") - return f"发生错误:{str(e)}" - - -# 定义批量模式用来下载番茄小说的函数 -def download_novels(url, encoding, user_agent, path_choice, folder_path, data_folder): - - headers = { - "User-Agent": user_agent - } - - # 获取网页源码 - response = requests.get(url, headers=headers) - html = response.text - - # 解析网页源码 - soup = BeautifulSoup(html, "html.parser") - - # 获取小说标题 - title = soup.find("h1").get_text() - # , class_ = "info-name" - print(f"\n开始 《{title}》 的下载") - # 获取小说信息 - info = soup.find("div", class_="page-header-info").get_text() - - # 获取小说简介 - intro = soup.find("div", class_="page-abstract-content").get_text() - - # 拼接小说内容字符串 - content = f"""使用 @星隅(xing-yv) 所作开源工具下载 -开源仓库地址:https://github.com/xing-yv/fanqie-novel-download -Gitee:https://gitee.com/xingyv1024/fanqie-novel-download/ -任何人无权限制您访问本工具,如果有向您提供代下载服务者未事先告知您工具的获取方式,请向作者举报:xing_yv@outlook.com - -{title} -{info} -{intro} -""" - - # 获取所有章节链接 - chapters = soup.find_all("div", class_="chapter-item") - - chapter_id = None - - # 遍历每个章节链接 - for chapter in chapters: - # 获取章节标题 - chapter_title = chapter.find("a").get_text() - - # 获取章节网址 - chapter_url = urljoin(url, chapter.find("a")["href"]) - - # 获取章节 id - chapter_id = re.search(r"/(\d+)", chapter_url).group(1) - - # 构造 api 网址 - api_url = f"https://novel.snssdk.com/api/novel/book/reader/full/v1/?device_platform=android&parent_enterfrom=novel_channel_search.tab.&aid=2329&platform_id=1&group_id={chapter_id}&item_id={chapter_id}" - - # 尝试获取章节内容 - chapter_content = None - retry_count = 1 - while retry_count < 4: # 设置最大重试次数 - # 获取 api 响应 - api_response = requests.get(api_url, headers=headers) - - # 解析 api 响应为 json 数据 - api_data = api_response.json() - - if "data" in api_data and "content" in api_data["data"]: - chapter_content = api_data["data"]["content"] - break # 如果成功获取章节内容,跳出重试循环 - else: - if retry_count == 1: - print(f"{chapter_title} 获取失败,正在尝试重试...") - print(f"第 ({retry_count}/3) 次重试获取章节内容") - retry_count += 1 # 否则重试 - - if retry_count == 4: - print(f"无法获取章节内容: {chapter_title},跳过。") - continue # 重试次数过多后,跳过当前章节 - - # 提取文章标签中的文本 - chapter_text = re.search(r"
([\s\S]*?)
", chapter_content).group(1) - - # 将

标签替换为换行符 - chapter_text = re.sub(r"

", "\n", chapter_text) - - # 去除其他 html 标签 - chapter_text = re.sub(r"", "", chapter_text) - - # 在小说内容字符串中添加章节标题和内容 - content += f"\n\n\n{chapter_title}\n{chapter_text}" - - # 打印进度信息 - print(f"已获取 {chapter_title}") - - # 保存小说更新源文件 - upd_file_path = os.path.join(data_folder, f"{title}.upd") - # 获取当前系统时间 - current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - # 创建要写入元信息文件的内容 - new_content = f"{current_time}\n{url}\n{chapter_id}\n{encoding}" - # 打开文件并完全覆盖内容 - with open(upd_file_path, "w") as file: - file.write(new_content) - - # 根据编码转换小说内容字符串为二进制数据 - data = content.encode(encoding, errors='ignore') - - # 根据main.py中用户选择的路径方式,选择自定义路径或者默认 - - file_path = None - - if path_choice == 1: - - # 使用用户选择的文件夹路径和默认文件名来生成完整的文件路径 - - file_path = os.path.join(folder_path, f"{title}.txt") - - elif path_choice == 0: - - # 在程序文件夹下新建output文件夹,并把文件放入 - - output_folder = "output" - - os.makedirs(output_folder, exist_ok=True) - - file_path = os.path.join(output_folder, f"{title}.txt") - - # 保存文件 - - with open(file_path, "wb") as f: - f.write(data) - - # 打印完成信息 - - print(f"已保存{title}.txt") diff --git a/src/setup/fanqie_chapter.py b/src/setup/fanqie_chapter.py deleted file mode 100644 index 00468c0..0000000 --- a/src/setup/fanqie_chapter.py +++ /dev/null @@ -1,195 +0,0 @@ -""" -作者:星隅(xing-yv) - -版权所有(C)2023 星隅(xing-yv) - -本软件根据GNU通用公共许可证第三版(GPLv3)发布; -你可以在以下位置找到该许可证的副本: -https://www.gnu.org/licenses/gpl-3.0.html - -根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 -请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 - -本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 - -免责声明: -该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和版权持有人无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 - -请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 - -无论您对程序进行了任何操作,请始终保留此信息。 -""" - -# 导入必要的模块 -import requests -from bs4 import BeautifulSoup -from urllib.parse import urljoin -import re -import os - -# 定义文件夹路径 -folder_path = "" - - -# 定义分章节保存模式用来下载番茄小说的函数 -def fanqie_c(url, encoding, user_agent, path_choice): - - get_folder_path(path_choice) - - headers = { - "User-Agent": user_agent - } - - # 获取网页源码 - response = requests.get(url, headers=headers) - html = response.text - - # 解析网页源码 - soup = BeautifulSoup(html, "html.parser") - - # 获取小说标题 - title = soup.find("h1").get_text() - # , class_ = "info-name" - - # 获取小说信息 - info = soup.find("div", class_="page-header-info").get_text() - - # 获取小说简介 - intro = soup.find("div", class_="page-abstract-content").get_text() - - # 拼接小说内容字符串 - introduction = f"""使用 @星隅(xing-yv) 所作开源工具下载 -开源仓库地址:https://github.com/xing-yv/fanqie-novel-download -Gitee:https://gitee.com/xingyv1024/fanqie-novel-download/ -任何人无权限制您访问本工具,如果有向您提供代下载服务者未事先告知您工具的获取方式,请向作者举报:xing_yv@outlook.com - -{title} -{info} -{intro} -""" - # 转换简介内容格式 - introduction_data = introduction.encode(encoding, errors='ignore') - - # 获取所有章节链接 - chapters = soup.find_all("div", class_="chapter-item") - - # 定义简介路径 - introduction_use = False - introduction_path = None - - # 遍历每个章节链接 - for chapter in chapters: - # 获取章节标题 - chapter_title = chapter.find("a").get_text() - - # 获取章节网址 - chapter_url = urljoin(url, chapter.find("a")["href"]) - - # 获取章节 id - chapter_id = re.search(r"/(\d+)", chapter_url).group(1) - - # 构造 api 网址 - api_url = f"https://novel.snssdk.com/api/novel/book/reader/full/v1/?device_platform=android&parent_enterfrom=novel_channel_search.tab.&aid=2329&platform_id=1&group_id={chapter_id}&item_id={chapter_id}" - - # 尝试获取章节内容 - chapter_content = None - retry_count = 1 - while retry_count < 4: # 设置最大重试次数 - # 获取 api 响应 - api_response = requests.get(api_url, headers=headers) - - # 解析 api 响应为 json 数据 - api_data = api_response.json() - - if "data" in api_data and "content" in api_data["data"]: - chapter_content = api_data["data"]["content"] - break # 如果成功获取章节内容,跳出重试循环 - else: - if retry_count == 1: - print(f"{chapter_title} 获取失败,正在尝试重试...") - print(f"第 ({retry_count}/3) 次重试获取章节内容") - retry_count += 1 # 否则重试 - - if retry_count == 4: - print(f"无法获取章节内容: {chapter_title},跳过。") - continue # 重试次数过多后,跳过当前章节 - - # 提取文章标签中的文本 - chapter_text = re.search(r"

([\s\S]*?)
", chapter_content).group(1) - - # 将

标签替换为换行符 - chapter_text = re.sub(r"

", "\n", chapter_text) - - # 去除其他 html 标签 - chapter_text = re.sub(r"", "", chapter_text) - - # 在章节内容字符串中添加章节标题和内容 - content_all = f"{chapter_title}\n{chapter_text}" - - # 转换章节内容格式 - data = content_all.encode(encoding, errors='ignore') - - # 重置file_path - file_path = None - - # 根据用户选择生成最终文件路径 - if path_choice == 1: - - # 使用用户选择的文件夹路径和默认文件名来生成完整的文件路径 - - file_path = os.path.join(folder_path, f"{title}", f"{chapter_title}.txt") - if introduction_use is False: - introduction_path = os.path.join(folder_path, f"{title}", "简介.txt") - - elif path_choice == 0: - - # 在程序文件夹下新建output文件夹,并定义文件路径 - - output_folder = "output" - - os.makedirs(output_folder, exist_ok=True) - - file_path = os.path.join(output_folder, f"{title}", f"{chapter_title}.txt") - - if introduction_use is False: - introduction_path = os.path.join(output_folder, f"{title}", "简介.txt") - - if introduction_use is False: - with open(introduction_path, "wb") as f: - f.write(introduction_data) - print("简介已保存") - # 将简介保存标记为已完成 - introduction_use = True - - with open(file_path, "wb") as f: - f.write(data) - - # 打印进度信息 - print(f"已获取: {chapter_title}") - - -def get_folder_path(path_choice): - global folder_path - # 如果用户选择自定义路径 - if path_choice == 1: - import tkinter as tk - from tkinter import filedialog - # 创建一个Tkinter窗口,但不显示它 - root = tk.Tk() - root.withdraw() - - print("您选择了自定义保存路径,请您在弹出窗口中选择保存文件夹。") - - while True: - - # 弹出文件对话框以选择保存位置和文件名 - folder_path = filedialog.askdirectory() - - # 检查用户是否取消了对话框 - if not folder_path: - # 用户取消了对话框,提示重新选择 - print("您没有选择保存文件夹,请重新选择!") - continue - else: - print("已选择保存文件夹") - break diff --git a/src/setup/fanqie_debug.py b/src/setup/fanqie_debug.py deleted file mode 100644 index 687f489..0000000 --- a/src/setup/fanqie_debug.py +++ /dev/null @@ -1,204 +0,0 @@ -""" -作者:星隅(xing-yv) - -版权所有(C)2023 星隅(xing-yv) - -本软件根据GNU通用公共许可证第三版(GPLv3)发布; -你可以在以下位置找到该许可证的副本: -https://www.gnu.org/licenses/gpl-3.0.html - -根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 -请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 - -本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 - -免责声明: -该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和版权持有人无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 - -请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 - -无论您对程序进行了任何操作,请始终保留此信息。 -""" - -# 导入必要的模块 -import requests -from bs4 import BeautifulSoup -from urllib.parse import urljoin -import re -import datetime -import os - - -# 定义调试模式用来下载番茄小说的函数 -def fanqie_d(url, encoding, user_agent, path_choice, data_folder): - - headers = { - "User-Agent": user_agent - } - - # 获取网页源码 - response = requests.get(url, headers=headers) - html = response.text - - # 解析网页源码 - soup = BeautifulSoup(html, "html.parser") - - # 获取小说标题 - title = soup.find("h1").get_text() - - print(f"[DEBUG]已获取小说标题") - - # 获取小说信息 - info = soup.find("div", class_="page-header-info").get_text() - - # if mode == 1: - print(f"[DEBUG]已获取小说信息") - - # 获取小说简介 - intro = soup.find("div", class_="page-abstract-content").get_text() - - # if mode == 1: - print(f"[DEBUG]已获取小说简介") - - # 拼接小说内容字符串 - content = f"""使用 @星隅(xing-yv) 所作开源工具下载 -开源仓库地址:https://github.com/xing-yv/fanqie-novel-download -Gitee:https://gitee.com/xingyv1024/fanqie-novel-download/ -任何人无权限制您访问本工具,如果有向您提供代下载服务者未事先告知您工具的获取方式,请向作者举报:xing_yv@outlook.com - -{title} -{info} -{intro} -""" - - # if mode == 1: - print(f"[DEBUG]已拼接小说简介字符串") - - # 获取所有章节链接 - chapters = soup.find_all("div", class_="chapter-item") - - # if mode == 1: - print(f"[DEBUG]已获取所有章节链接") - - chapter_id = None - - # 遍历每个章节链接 - for chapter in chapters: - # 获取章节标题 - chapter_title = chapter.find("a").get_text() - print(f"[DEBUG]正在获取章节:{chapter_title}") - - # 获取章节网址 - chapter_url = urljoin(url, chapter.find("a")["href"]) - - # 获取章节 id - chapter_id = re.search(r"/(\d+)", chapter_url).group(1) - - print(f"[DEBUG]章节id:{chapter_id}") - - # 构造 api 网址 - api_url = f"https://novel.snssdk.com/api/novel/book/reader/full/v1/?device_platform=android&parent_enterfrom=novel_channel_search.tab.&aid=2329&platform_id=1&group_id={chapter_id}&item_id={chapter_id}" - - print(f"[DEBUG]api网址:{api_url}") - - # 尝试获取章节内容 - chapter_content = None - retry_count = 1 - while retry_count < 4: # 设置最大重试次数 - # 获取 api 响应 - api_response = requests.get(api_url, headers=headers) - - print(f"[DEBUG]HTTP状态码:{api_response}") - - # 解析 api 响应为 json 数据 - api_data = api_response.json() - - if "data" in api_data and "content" in api_data["data"]: - chapter_content = api_data["data"]["content"] - break # 如果成功获取章节内容,跳出重试循环 - else: - if retry_count == 1: - print(f"{chapter_title} 获取失败,正在尝试重试...") - print(f"第 ({retry_count}/3) 次重试获取章节内容") - retry_count += 1 # 否则重试 - - if retry_count == 4: - print(f"无法获取章节内容: {chapter_title},跳过。") - continue # 重试次数过多后,跳过当前章节 - - # 提取文章标签中的文本 - chapter_text = re.search(r"

([\s\S]*?)
", chapter_content).group(1) - - # 将

标签替换为换行符 - chapter_text = re.sub(r"

", "\n", chapter_text) - - # 去除其他 html 标签 - chapter_text = re.sub(r"", "", chapter_text) - - # 在小说内容字符串中添加章节标题和内容 - content += f"\n\n\n{chapter_title}\n{chapter_text}" - - # 打印进度信息 - print(f"已获取 {chapter_title}") - - # 保存小说更新源文件 - upd_file_path = os.path.join(data_folder, f"{title}.upd") - # 获取当前系统时间 - current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - # 创建要写入元信息文件的内容 - new_content = f"{current_time}\n{url}\n{chapter_id}\n{encoding}" - # 打开文件并完全覆盖内容 - with open(upd_file_path, "w") as file: - file.write(new_content) - print("[DEBUG]已保存.upd更新元数据文件") - - # 根据编码转换小说内容字符串为二进制数据 - data = content.encode(encoding, errors='ignore') - - # 根据main.py中用户选择的路径方式,选择自定义路径或者默认 - if path_choice == 1: - import tkinter as tk - from tkinter import filedialog - # 创建一个Tkinter窗口,但不显示它 - root = tk.Tk() - root.withdraw() - print("[DEBUG]已创建tkinter隐形窗口") - - print("您选择了自定义保存路径,请您在弹出窗口中选择路径。") - - # 设置默认文件名和扩展名 - default_extension = ".txt" - default_filename = f"{title}" - - while True: - - # 弹出文件对话框以选择保存位置和文件名 - file_path = filedialog.asksaveasfilename( - defaultextension=default_extension, - filetypes=[("Text Files", "*" + default_extension)], - initialfile=default_filename - ) - - # 检查用户是否取消了对话框 - if not file_path: - # 用户取消了对话框,提示重新选择 - print("您没有选择路径,请重新选择!") - continue - - # 用户选择了文件路径,保存文件并退出循环 - with open(file_path, "wb") as f: - f.write(data) - - # 打印完成信息 - print(f"已保存") - break # 退出循环 - - elif path_choice == 0: - # 定义文件名 - filename = title + ".txt" - - # 保存文件 - with open(filename, "wb") as f: - f.write(data) - # 打印完成信息 - print(f"已保存{filename}") diff --git a/src/setup/fanqie_normal.py b/src/setup/fanqie_normal.py deleted file mode 100644 index e9dac79..0000000 --- a/src/setup/fanqie_normal.py +++ /dev/null @@ -1,182 +0,0 @@ -""" -作者:星隅(xing-yv) - -版权所有(C)2023 星隅(xing-yv) - -本软件根据GNU通用公共许可证第三版(GPLv3)发布; -你可以在以下位置找到该许可证的副本: -https://www.gnu.org/licenses/gpl-3.0.html - -根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 -请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 - -本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 - -免责声明: -该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和版权持有人无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 - -请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 - -无论您对程序进行了任何操作,请始终保留此信息。 -""" - -# 导入必要的模块 -import requests -from bs4 import BeautifulSoup -from urllib.parse import urljoin -import re -import datetime -import os - - -# 定义正常模式用来下载番茄小说的函数 -def fanqie_n(url, encoding, user_agent, path_choice, data_folder): - - headers = { - "User-Agent": user_agent - } - - # 获取网页源码 - response = requests.get(url, headers=headers) - html = response.text - - # 解析网页源码 - soup = BeautifulSoup(html, "html.parser") - - # 获取小说标题 - title = soup.find("h1").get_text() - # , class_ = "info-name" - - # 获取小说信息 - info = soup.find("div", class_="page-header-info").get_text() - - # 获取小说简介 - intro = soup.find("div", class_="page-abstract-content").get_text() - - # 拼接小说内容字符串 - content = f"""使用 @星隅(xing-yv) 所作开源工具下载 -开源仓库地址:https://github.com/xing-yv/fanqie-novel-download -Gitee:https://gitee.com/xingyv1024/fanqie-novel-download/ -任何人无权限制您访问本工具,如果有向您提供代下载服务者未事先告知您工具的获取方式,请向作者举报:xing_yv@outlook.com - -{title} -{info} -{intro} -""" - - # 获取所有章节链接 - chapters = soup.find_all("div", class_="chapter-item") - - chapter_id = None - - # 遍历每个章节链接 - for chapter in chapters: - # 获取章节标题 - chapter_title = chapter.find("a").get_text() - - # 获取章节网址 - chapter_url = urljoin(url, chapter.find("a")["href"]) - - # 获取章节 id - chapter_id = re.search(r"/(\d+)", chapter_url).group(1) - - # 构造 api 网址 - api_url = f"https://novel.snssdk.com/api/novel/book/reader/full/v1/?device_platform=android&parent_enterfrom=novel_channel_search.tab.&aid=2329&platform_id=1&group_id={chapter_id}&item_id={chapter_id}" - - # 尝试获取章节内容 - chapter_content = None - retry_count = 1 - while retry_count < 4: # 设置最大重试次数 - # 获取 api 响应 - api_response = requests.get(api_url, headers=headers) - - # 解析 api 响应为 json 数据 - api_data = api_response.json() - - if "data" in api_data and "content" in api_data["data"]: - chapter_content = api_data["data"]["content"] - break # 如果成功获取章节内容,跳出重试循环 - else: - if retry_count == 1: - print(f"{chapter_title} 获取失败,正在尝试重试...") - print(f"第 ({retry_count}/3) 次重试获取章节内容") - retry_count += 1 # 否则重试 - - if retry_count == 4: - print(f"无法获取章节内容: {chapter_title},跳过。") - continue # 重试次数过多后,跳过当前章节 - - # 提取文章标签中的文本 - chapter_text = re.search(r"

([\s\S]*?)
", chapter_content).group(1) - - # 将

标签替换为换行符 - chapter_text = re.sub(r"

", "\n", chapter_text) - - # 去除其他 html 标签 - chapter_text = re.sub(r"", "", chapter_text) - - # 在小说内容字符串中添加章节标题和内容 - content += f"\n\n\n{chapter_title}\n{chapter_text}" - - # 打印进度信息 - print(f"已获取 {chapter_title}") - - # 保存小说更新源文件 - upd_file_path = os.path.join(data_folder, f"{title}.upd") - # 获取当前系统时间 - current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - # 创建要写入元信息文件的内容 - new_content = f"{current_time}\n{url}\n{chapter_id}\n{encoding}" - # 打开文件并完全覆盖内容 - with open(upd_file_path, "w") as file: - file.write(new_content) - - # 根据编码转换小说内容字符串为二进制数据 - data = content.encode(encoding, errors='ignore') - - # 根据main.py中用户选择的路径方式,选择自定义路径或者默认 - if path_choice == 1: - import tkinter as tk - from tkinter import filedialog - # 创建一个Tkinter窗口,但不显示它 - root = tk.Tk() - root.withdraw() - - print("您选择了自定义保存路径,请您在弹出窗口中选择路径。") - - # 设置默认文件名和扩展名 - default_extension = ".txt" - default_filename = f"{title}" - - while True: - - # 弹出文件对话框以选择保存位置和文件名 - file_path = filedialog.asksaveasfilename( - defaultextension=default_extension, - filetypes=[("Text Files", "*" + default_extension)], - initialfile=default_filename - ) - - # 检查用户是否取消了对话框 - if not file_path: - # 用户取消了对话框,提示重新选择 - print("您没有选择路径,请重新选择!") - continue - - # 用户选择了文件路径,保存文件并退出循环 - with open(file_path, "wb") as f: - f.write(data) - - # 打印完成信息 - print(f"已保存") - break # 退出循环 - - elif path_choice == 0: - # 定义文件名 - filename = title + ".txt" - - # 保存文件 - with open(filename, "wb") as f: - f.write(data) - # 打印完成信息 - print(f"已保存{title}.txt") diff --git a/src/setup/fanqie_update.py b/src/setup/fanqie_update.py deleted file mode 100644 index 79c20bf..0000000 --- a/src/setup/fanqie_update.py +++ /dev/null @@ -1,176 +0,0 @@ -""" -作者:星隅(xing-yv) - -版权所有(C)2023 星隅(xing-yv) - -本软件根据GNU通用公共许可证第三版(GPLv3)发布; -你可以在以下位置找到该许可证的副本: -https://www.gnu.org/licenses/gpl-3.0.html - -根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 -请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 - -本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 - -免责声明: -该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和版权持有人无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 - -请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 - -无论您对程序进行了任何操作,请始终保留此信息。 -""" - -# 导入必要的模块 -import requests -from bs4 import BeautifulSoup -from urllib.parse import urljoin -import datetime -import re -import os - - -# 定义番茄更新函数 -def fanqie_update(user_agent, data_folder): - # 指定小说文件夹 - novel_folder = "小说" - - novel_files = [file for file in os.listdir(novel_folder) if file.endswith(".txt")] - - if not novel_files: - print("没有可更新的文件") - return - - no_corresponding_files = True # 用于标记是否存在对应的txt和upd文件 - - for txt_file in novel_files: - txt_file_path = os.path.join(novel_folder, txt_file) - upd_file_path = os.path.join(data_folder, txt_file.replace(".txt", ".upd")) - novel_name = txt_file.replace(".txt", "") - - if os.path.exists(upd_file_path): - - print(f"正在尝试更新: {novel_name}") - # 读取用于更新的文件元数据 - with open(upd_file_path, 'r') as file: - lines = file.readlines() - - # 保存上次更新时间和上次章节id到变量 - last_update_time = lines[0].strip() - url = lines[1].strip() - last_chapter_id = lines[2].strip() - encoding = lines[3].strip() - print(f"上次更新时间{last_update_time}") - result = download_novel(url, encoding, user_agent, last_chapter_id, txt_file_path) - if result == "DN": - print(f"{novel_name} 已是最新,不需要更新。\n") - else: - print(f"{novel_name} 已更新完成。\n") - # 获取当前系统时间 - current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - # 创建要写入元信息文件的内容 - new_content = f"{current_time}\n{url}\n{result}\n{encoding}" - # 打开文件并完全覆盖内容 - with open(upd_file_path, "w") as file: - file.write(new_content) - - no_corresponding_files = False - else: - print(f"{novel_name} 不是通过此工具下载,无法更新") - - if no_corresponding_files: - print("没有可更新的文件") - - -# 定义更新番茄小说的函数 -def download_novel(url, encoding, user_agent, start_chapter_id, txt_file_path): - - headers = { - "User-Agent": user_agent - } - - # 获取网页源码 - response = requests.get(url, headers=headers) - html = response.text - - # 解析网页源码 - soup = BeautifulSoup(html, "html.parser") - - # 获取所有章节链接 - chapters = soup.find_all("div", class_="chapter-item") - - last_chapter_id = None - # 找到起始章节的索引 - start_index = 0 - for i, chapter in enumerate(chapters): - chapter_url = urljoin(url, chapter.find("a")["href"]) - chapter_id_tmp = re.search(r"/(\d+)", chapter_url).group(1) - if chapter_id_tmp == start_chapter_id: # 更新函数,所以前进一个章节 - start_index = i + 1 - last_chapter_id = chapter_id_tmp - - # 判断是否已经最新 - if start_index >= len(chapters): - return "DN" # 返回Don't Need. - - # 打开文件 - with open(txt_file_path, 'ab') as f: - # 从起始章节开始遍历每个章节链接 - for chapter in chapters[start_index:]: - # 获取章节标题 - chapter_title = chapter.find("a").get_text() - - # 获取章节网址 - chapter_url = urljoin(url, chapter.find("a")["href"]) - - # 获取章节 id - chapter_id = re.search(r"/(\d+)", chapter_url).group(1) - - # 构造 api 网址 - api_url = f"https://novel.snssdk.com/api/novel/book/reader/full/v1/?device_platform=android&parent_enterfrom=novel_channel_search.tab.&aid=2329&platform_id=1&group_id={chapter_id}&item_id={chapter_id}" - - # 尝试获取章节内容 - chapter_content = None - retry_count = 1 - while retry_count < 4: # 设置最大重试次数 - # 获取 api 响应 - api_response = requests.get(api_url, headers=headers) - - # 解析 api 响应为 json 数据 - api_data = api_response.json() - - if "data" in api_data and "content" in api_data["data"]: - chapter_content = api_data["data"]["content"] - break # 如果成功获取章节内容,跳出重试循环 - else: - if retry_count == 1: - print(f"{chapter_title} 获取失败,正在尝试重试...") - print(f"第 ({retry_count}/3) 次重试获取章节内容") - retry_count += 1 # 否则重试 - - if retry_count == 4: - print(f"无法获取章节内容: {chapter_title},跳过。") - continue # 重试次数过多后,跳过当前章节 - - # 提取文章标签中的文本 - chapter_text = re.search(r"

([\s\S]*?)
", chapter_content).group(1) - - # 将

标签替换为换行符 - chapter_text = re.sub(r"

", "\n", chapter_text) - - # 去除其他 html 标签 - chapter_text = re.sub(r"", "", chapter_text) - - # 在小说内容字符串中添加章节标题和内容 - content = f"\n\n\n{chapter_title}\n{chapter_text}" - - # 根据编码转换小说内容字符串为二进制数据 - data = content.encode(encoding, errors='ignore') - - # 将数据追加到文件中 - f.write(data) - - # 打印进度信息 - print(f"已增加: {chapter_title}") - - # 返回更新完成 - return last_chapter_id diff --git a/src/setup/function.py b/src/setup/function.py deleted file mode 100644 index 459c4d5..0000000 --- a/src/setup/function.py +++ /dev/null @@ -1,347 +0,0 @@ -""" -作者:星隅(xing-yv) - -版权所有(C)2023 星隅(xing-yv) - -本软件根据GNU通用公共许可证第三版(GPLv3)发布; -你可以在以下位置找到该许可证的副本: -https://www.gnu.org/licenses/gpl-3.0.html - -根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 -请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 - -本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 - -免责声明: -该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和版权持有人无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 - -请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 - -无论您对程序进行了任何操作,请始终保留此信息。 -""" - - -import fanqie_normal as fn -import fanqie_debug as fd -import fanqie_batch as fb -import fanqie_chapter as fc -import fanqie_update as fu -import os -import requests -from sys import exit -from packaging import version - -# 定义全局变量 -mode = None -page_url = None -txt_encoding = None -ua = None -type_path_num = None -return_info = None -user_folder = os.path.expanduser("~") -data_path = os.path.join(user_folder, "fanqie_data") -os.makedirs(data_path, exist_ok=True) - - -# 用户须知 -def print_usage(): - print("欢迎使用此程序!") - print("""用户须知: -此程序开源免费,如果您付费获取,那么您已经被骗了。 - -本程序灵感及api来自于ibxff所作用户脚本,脚本链接请到更多中查看; -为保护此程序不被用于不良商业行为,此程序使用GPLv3许可证, -请您基于此程序开发或混合后,使用GPLv3开源,感谢配合。 - -您可以自由地复制、修改和分发本许可证文档,但不能销售它。 -您可以使用此程序提供有偿代下载服务,但在提供服务的同时,必须向服务的接收者提供此程序的获取方式, -以便他们可以自由使用、修改和分发该软件,同时也必须遵守GPLv3协议的所有其他规定。 - -用户QQ群(闲聊):621748837 - -免责声明: -该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。 -使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和版权持有人无关。 -作者不对因使用该程序而导致的任何损失或损害承担任何责任。 -""") - - -# 请用户同意协议并选择模式 -def start(): - global mode # 声明mode为全局变量 - global return_info - - # 定义变量flag控制是否退出程序 - flag = True - while True: - print_usage() - print("请选择以下操作:") - print("1. 同意并进入正常模式") - print("2. 同意并进入自动批量模式(测试)") - print("3. 同意并进入分章保存模式(测试)") - print("4. 同意并进入Debug模式") - print("5. 查看更多") - print("6. 更新已下载的小说") - print("7. 不同意,退出程序") - choice = input("请输入您的选择(1/2/3/4/5/6/7):(回车默认“1”)\n") - - # 通过用户选择,决定模式,给mode赋值 - if not choice: - choice = '1' - - if choice == '1': - mode = 0 - clear_screen() - print("您已进入正常下载模式:") - break - elif choice == '2': - mode = 2 - clear_screen() - print("您已进入自动批量下载模式(测试);") - break - elif choice == '3': - mode = 3 - clear_screen() - print("您已进入分章保存模式(测试):") - break - elif choice == '4': - mode = 1 - clear_screen() - print("您已进入Debug模式,将会给出更多选项和调试信息。\n") - break - elif choice == '5': - clear_screen() - print("""作者:星隅(xing-yv) -版权所有(C)2023 星隅(xing-yv) - -本软件根据GNU通用公共许可证第三版(GPLv3)发布; -你可以在以下位置找到该许可证的副本: -https://www.gnu.org/licenses/gpl-3.0.html - -根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 -请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 - -本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 - -免责声明: -该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和版权持有人无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 - -请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 - -ibxff所作用户脚本:https://greasyfork.org/zh-CN/scripts/476688 -开源仓库地址:https://github.com/xing-yv/fanqie-novel-download -gitee地址:https://gitee.com/xingyv1024/fanqie-novel-download -作者B站主页:https://space.bilibili.com/1920711824 -提出反馈:https://github.com/xing-yv/fanqie-novel-download/issues/new -(请在右侧Label处选择issue类型以得到更快回复) -""") - input("按Enter键返回...") - clear_screen() - elif choice == '6': - clear_screen() - print("您已进入更新模式") - # 调用番茄更新函数 - return_info = fu.fanqie_update(ua, data_path) - return - elif choice == '7': - clear_screen() - # 确认退出 - while True: - qd = input("您确定要退出程序吗(yes/no)(默认:no): ") - if not qd: - qd = "no" - if qd.lower() == "yes": - input("按Enter退出程序...") - break - elif qd.lower() == "no": - flag = False - break - else: - print("输入无效,请重新输入。") - if flag is True: - exit(0) - else: - clear_screen() - continue - else: - print("无效的选择,请重新输入。") - get_parameter(retry=False) - - -def get_parameter(retry): - global page_url - global txt_encoding - global ua - global type_path_num - - page_url = None - - # 判断是否是批量下载模式 - if mode == 2: - if not os.path.exists('urls.txt'): - with open('urls.txt', 'x') as _: - pass - if retry is True: - print("您在urls.txt中输入的内容有误,请重新输入") - print("请重新在程序同文件夹(或执行目录)下的urls.txt中,以每行一个的形式写入目录页链接") - elif retry is False: - print("请在程序同文件夹(或执行目录)下的urls.txt中,以每行一个的形式写入目录页链接") - input("完成后请按Enter键继续:") - else: - # 不是则让用户输入小说目录页的链接 - while True: - page_url = input("请输入目录页链接:\n") - - # 预留七猫小说判断 - # if "qimao" in page_url: - # if mode == 0: - # mode = 2 - # elif mode == 1: - # mode = 3 - # elif "fanqie" in page_url: - - # 检查 url 是否是小说目录页面 - if "/page/" not in page_url: - print("请输入正确的小说目录页面链接") - else: - break # 如果是正确的链接,则退出循环 - - # 让用户选择保存文件的编码 - while True: - txt_encoding_num = input("请输入保存文件所使用的编码(默认:1):1 -> utf-8 | 2 -> gb2312\n") - - if not txt_encoding_num: - txt_encoding_num = '1' - - # 检查用户选择文件编码是否正确 - if txt_encoding_num == '1': - txt_encoding = 'utf-8' - break - elif txt_encoding_num == '2': - txt_encoding = 'gb2312' - break - else: - print("输入无效,请重新输入。") - - print(f"你选择的保存编码是:{txt_encoding}") - - # 初始化“ua” - ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0" - - # 定义 User-Agent - if mode == 1: # 判断用户是否处于调试模式 - while True: - ua_choice = input("是否自行输入User-Agent?(yes/no)(默认:no): ") - if not ua_choice: - ua_choice = "no" - # 是则询问是否自定义ua - if ua_choice.lower() == "yes": - ua = input("请输入自定义的User-Agent: \n") - break - elif ua_choice.lower() == "no": - break - else: - print("输入无效,请重新输入。") - continue - - type_path_num = None - - # 询问用户是否自定义保存路径 - while True: - - type_path = input("是否自行选择保存路径(yes/no)(默认:no):") - - if not type_path: - type_path = "no" - - if type_path.lower() == "yes": - type_path_num = 1 - if mode == 2: - print("您选择了自定义保存文件夹,请在文件检查后选择保存文件夹。") - elif mode == 3: - print("您选择了自定义保存文件夹,请在下载开始前选择保存文件夹。") - else: - print("您选择了自定义保存路径,请在获取完成后选择保存路径。") - break - - elif type_path.lower() == "no": - type_path_num = 0 - if mode == 2 or mode == 3: - print("您未选择自定义保存路径,请在获取完成后到程序文件夹下output文件夹寻找文件。") - print("(如果您在命令行中执行程序,请到执行目录下寻找output文件夹)") - else: - print("您未选择自定义保存路径,请在获取完成后到程序相同文件夹下寻找文件。") - print("(如果您在命令行中执行程序,请到执行目录下寻找文件)") - break - - else: - print("输入无效,请重新输入。") - continue - perform_user_mode_action() - - -def perform_user_mode_action(): - global return_info - # 判断用户处于什么模式 - if mode == 0: - # 调用番茄正常模式函数 - return_info = fn.fanqie_n(page_url, txt_encoding, ua, type_path_num, data_path) - elif mode == 1: - # 调用番茄调试模式函数 - return_info = fd.fanqie_d(page_url, txt_encoding, ua, type_path_num, data_path) - elif mode == 2: - # 调用番茄批量模式函数 - return_info = fb.fanqie_b(txt_encoding, ua, type_path_num, data_path) - elif mode == 3: - # 调用番茄分章模式函数 - return_info = fc.fanqie_c(page_url, txt_encoding, ua, type_path_num) - - -# 检查更新 -def check_update(now_version): - owner = "xingyv1024" - repo = "fanqie-novel-download" - api_url = f"https://gitee.com/api/v5/repos/{owner}/{repo}/releases/latest" - - print("正在检查更新...") - print(f"当前版本: v{now_version}") - - # 发送GET请求以获取最新的发行版信息 - response = requests.get(api_url) - - if response.status_code == 200: - release_info = response.json() - if "tag_name" in release_info: - latest_version = release_info["tag_name"] - print(f"最新的发行版是:v{latest_version}") - result = compare_versions(now_version, latest_version) - if result == -1: - print("检测到新版本\n更新可用!请到 https://gitee.com/xingyv1024/fanqie-novel-download/releases 下载最新版") - input("按Enter键继续...\n") - else: - print("您正在使用最新版") - - else: - print("未获取到发行版信息。") - input("按Enter键继续...\n") - else: - print(f"请求失败,状态码:{response.status_code}") - input("按Enter键继续...\n") - - -def compare_versions(version1, version2): - - # 使用packaging模块进行版本比较 - v1 = version.parse(version1) - v2 = version.parse(version2) - - if v1 < v2: - return -1 - elif v1 > v2: - return 1 - else: - return 0 - - -def clear_screen(): - os.system('cls') diff --git a/src/setup/main.py b/src/setup/main.py deleted file mode 100644 index fa6be31..0000000 --- a/src/setup/main.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -作者:星隅(xing-yv) - -版权所有(C)2023 星隅(xing-yv) - -本软件根据GNU通用公共许可证第三版(GPLv3)发布; -你可以在以下位置找到该许可证的副本: -https://www.gnu.org/licenses/gpl-3.0.html - -根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 -请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 - -本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 - -免责声明: -该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和版权持有人无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 - -请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 - -无论您对程序进行了任何操作,请始终保留此信息。 -""" - -import function as f -from sys import exit - -version = "2.1" - -# 检查更新 -f.check_update(version) -f.clear_screen() - -# 程序开始 -f.start() - -if f.return_info is None: - input("按Enter键退出...") - exit(0) -else: - f.get_parameter(retry=True) From 3f0ef8a0bf4e544208d7dfa05416a335c2cd900e Mon Sep 17 00:00:00 2001 From: xing-yv Date: Mon, 16 Oct 2023 20:28:38 +0800 Subject: [PATCH 2/3] update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 61d399f..808aeaa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ # 忽略IDE文件 .idea/ src/__pycache__/ -src/setup/__pycache__/ \ No newline at end of file +src/list_edition/__pycache__/ \ No newline at end of file From 976204149c1e5cc32afa7adf21e8cac7eb5f1c69 Mon Sep 17 00:00:00 2001 From: xing-yv Date: Mon, 16 Oct 2023 20:29:03 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=AE=9E=E7=8E=B0GUI=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E9=98=9F=E5=88=97=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/list_edition/fanqie_list.py | 167 +++++++++++++++++++++++++++++++ src/list_edition/list_edition.py | 159 +++++++++++++++++++++++++++++ src/list_edition/public.py | 60 +++++++++++ 3 files changed, 386 insertions(+) create mode 100644 src/list_edition/fanqie_list.py create mode 100644 src/list_edition/list_edition.py create mode 100644 src/list_edition/public.py diff --git a/src/list_edition/fanqie_list.py b/src/list_edition/fanqie_list.py new file mode 100644 index 0000000..b4e021f --- /dev/null +++ b/src/list_edition/fanqie_list.py @@ -0,0 +1,167 @@ +""" +作者:星隅(xing-yv) + +版权所有(C)2023 星隅(xing-yv) + +本软件根据GNU通用公共许可证第三版(GPLv3)发布; +你可以在以下位置找到该许可证的副本: +https://www.gnu.org/licenses/gpl-3.0.html + +根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 +请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 + +本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 + +免责声明: +该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和项目贡献者无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 + +请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 + +无论您对程序进行了任何操作,请始终保留此信息。 +""" +import os + +# 导入必要的模块 +import requests +from bs4 import BeautifulSoup +from urllib.parse import urljoin +import re +import datetime +from os import path +import time +import public as p + + +# 定义正常模式用来下载番茄小说的函数 +def fanqie_l(url, encoding, output_queue): + + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0" + } + + # 获取网页源码 + response = requests.get(url, headers=headers) + html = response.text + + # 解析网页源码 + soup = BeautifulSoup(html, "html.parser") + + # 获取小说标题 + title = soup.find("h1").get_text() + # , class_ = "info-name" + # 替换非法字符 + title = p.rename(title) + + output_queue.put(f"正在获取: {title}") + + # 获取小说信息 + info = soup.find("div", class_="page-header-info").get_text() + + # 获取小说简介 + intro = soup.find("div", class_="page-abstract-content").get_text() + + # 拼接小说内容字符串 + content = f"""如果需要小说更新,请勿修改文件名 +使用 @星隅(xing-yv) 所作开源工具下载 +开源仓库地址:https://github.com/xing-yv/fanqie-novel-download +Gitee:https://gitee.com/xingyv1024/fanqie-novel-download/ +任何人无权限制您访问本工具,如果有向您提供代下载服务者未事先告知您工具的获取方式,请向作者举报:xing_yv@outlook.com + +{title} +{info} +{intro} +""" + + # 获取所有章节链接 + chapters = soup.find_all("div", class_="chapter-item") + + # 定义文件名 + file_path = path.join('output', f'{title}.txt') + + os.makedirs("output", exist_ok=True) + + try: + # 遍历每个章节链接 + for chapter in chapters: + time.sleep(1) + # 获取章节标题 + chapter_title = chapter.find("a").get_text() + + # 获取章节网址 + chapter_url = urljoin(url, chapter.find("a")["href"]) + + # 获取章节 id + chapter_id = re.search(r"/(\d+)", chapter_url).group(1) + + # 构造 api 网址 + api_url = f"https://novel.snssdk.com/api/novel/book/reader/full/v1/?device_platform=android&parent_enterfrom=novel_channel_search.tab.&aid=2329&platform_id=1&group_id={chapter_id}&item_id={chapter_id}" + + # 尝试获取章节内容 + chapter_content = None + retry_count = 1 + while retry_count < 4: # 设置最大重试次数 + # 获取 api 响应 + api_response = requests.get(api_url, headers=headers) + + # 解析 api 响应为 json 数据 + api_data = api_response.json() + + if "data" in api_data and "content" in api_data["data"]: + chapter_content = api_data["data"]["content"] + break # 如果成功获取章节内容,跳出重试循环 + else: + if retry_count == 1: + output_queue.put(f"{chapter_title} 获取失败,正在尝试重试...") + output_queue.put(f"第 ({retry_count}/3) 次重试获取章节内容") + retry_count += 1 # 否则重试 + + if retry_count == 4: + output_queue.put(f"无法获取章节内容: {chapter_title},跳过。") + continue # 重试次数过多后,跳过当前章节 + + # 提取文章标签中的文本 + chapter_text = re.search(r"

([\s\S]*?)
", chapter_content).group(1) + + # 将

标签替换为换行符 + chapter_text = re.sub(r"

", "\n", chapter_text) + + # 去除其他 html 标签 + chapter_text = re.sub(r"", "", chapter_text) + + chapter_text = p.fix_publisher(chapter_text) + + # 在小说内容字符串中添加章节标题和内容 + content += f"\n\n\n{chapter_title}\n{chapter_text}" + + # 打印进度信息 + output_queue.put(f"已获取 {chapter_title}") + + # 根据编码转换小说内容字符串为二进制数据 + data = content.encode(encoding, errors='ignore') + + # 保存文件 + with open(file_path, "wb") as f: + f.write(data) + + # 打印完成信息 + output_queue.put(f"已保存{title}.txt") + + current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + output_queue.put(f"完成时间:{current_time}") + + except Exception as e: + # 捕获所有异常,及时保存文件 + output_queue.put(f"发生异常: \n{e}") + output_queue.put("正在尝试保存文件...") + current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + output_queue.put(f"{current_time}") + # 根据转换小说内容字符串为二进制数据 + data = content.encode(encoding, errors='ignore') + + # 保存文件 + with open(file_path, "wb") as f: + f.write(data) + + output_queue.put("文件已保存!") + return diff --git a/src/list_edition/list_edition.py b/src/list_edition/list_edition.py new file mode 100644 index 0000000..a7cb8da --- /dev/null +++ b/src/list_edition/list_edition.py @@ -0,0 +1,159 @@ +""" +作者:星隅(xing-yv) + +版权所有(C)2023 星隅(xing-yv) + +本软件根据GNU通用公共许可证第三版(GPLv3)发布; +你可以在以下位置找到该许可证的副本: +https://www.gnu.org/licenses/gpl-3.0.html + +根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 +请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 + +本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 + +免责声明: +该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和项目贡献者无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 + +请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 + +无论您对程序进行了任何操作,请始终保留此信息。 +""" + +import queue +import threading +import tkinter as tk +import tkinter.messagebox +from multiprocessing import Process, Queue +import time +import fanqie_list as fl + + +class Spider: + def __init__(self, output_func, output_queue): + self.url_queue = queue.Queue() + self.output_queue = output_queue + self.is_running = True + self.output_func = output_func + + @staticmethod + def crawl(url, output_queue): + # 创建一个新的进程来运行爬虫函数 + p = Process(target=fl.fanqie_l, args=(url, 'utf-8', output_queue)) + p.start() + time.sleep(2) + + def worker(self): + while self.is_running: + try: + url = self.url_queue.get(timeout=1) + Spider.crawl(url, self.output_queue) + self.url_queue.task_done() + except queue.Empty: + continue + + def start(self): + threading.Thread(target=self.worker, daemon=True).start() + + def add_url(self, url): + if "/page/" not in url: + tkinter.messagebox.showinfo("错误", "URL格式不正确,请重新输入") + return + else: + self.url_queue.put(url) + tkinter.messagebox.showinfo("成功", "URL已添加到下载队列") + + def stop(self): + self.is_running = False + + +def main(): + root = tk.Tk() + root.title("番茄工具队列版") + + # 设置窗口大小 + root.geometry("600x400") + + output_text = tk.Text(root, state='disabled') + output_text.pack() + + # 创建滚动条 + scrollbar = tk.Scrollbar(root, command=output_text.yview) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + + # 设置Text组件的yscrollcommand为滚动条的set方法 + output_text.config(yscrollcommand=scrollbar.set) + + # 手动调整滚动条的位置 + scrollbar.place(x=580, y=0, height=320) + + input_frame = tk.Frame(root) + input_frame.pack() + input_entry = tk.Entry(input_frame, width=50) + input_entry.pack(side=tk.LEFT) + + # 调整输入框的位置 + input_frame.place(x=50, y=350) + + def paste_text(): + input_entry.event_generate('<>') + + def show_context_menu(event): + context_menu.post(event.x_root, event.y_root) + + # 创建上下文菜单 + context_menu = tk.Menu(root, tearoff=0) + context_menu.add_command(label="粘贴", command=paste_text) + + # 绑定上下文菜单到输入框 + input_entry.bind("", show_context_menu) + + input_entry.bind("", show_context_menu) + + output_queue = Queue() + + spider = Spider(lambda text: output_text.config(state='normal') or output_text.insert(tk.END, text + "\n") or output_text.config(state='disabled'), output_queue) + + spider.start() + + def add_url(): + url = input_entry.get() + spider.add_url(url) + input_entry.delete(0, tk.END) + + add_button = tk.Button(input_frame, text="添加URL", command=add_url) + add_button.pack(side=tk.LEFT) + + def list_urls(): + urls = list(spider.url_queue.queue) + tkinter.messagebox.showinfo("队列中的URL", "\n".join(urls)) + + list_button = tk.Button(input_frame, text="列出URL", command=list_urls) + list_button.pack(side=tk.LEFT) + + def stop_spider(): + spider.stop() + root.quit() + + stop_button = tk.Button(input_frame, text="退出", command=stop_spider) + stop_button.pack(side=tk.LEFT) + + def check_output_queue(): + while not output_queue.empty(): + message = output_queue.get() + output_text.config(state='normal') + output_text.insert(tk.END, message + "\n") + output_text.config(state='disabled') + + # 滚动到最后一行 + output_text.see(tk.END) + + root.after(100, check_output_queue) # 每秒检查一次输出队列 + + check_output_queue() + + root.mainloop() + + +if __name__ == "__main__": + main() diff --git a/src/list_edition/public.py b/src/list_edition/public.py new file mode 100644 index 0000000..fd5ddf1 --- /dev/null +++ b/src/list_edition/public.py @@ -0,0 +1,60 @@ +""" +作者:星隅(xing-yv) + +版权所有(C)2023 星隅(xing-yv) + +本软件根据GNU通用公共许可证第三版(GPLv3)发布; +你可以在以下位置找到该许可证的副本: +https://www.gnu.org/licenses/gpl-3.0.html + +根据GPLv3的规定,您有权在遵循许可证的前提下自由使用、修改和分发本软件。 +请注意,根据许可证的要求,任何对本软件的修改和分发都必须包括原始的版权声明和GPLv3的完整文本。 + +本软件提供的是按"原样"提供的,没有任何明示或暗示的保证,包括但不限于适销性和特定用途的适用性。作者不对任何直接或间接损害或其他责任承担任何责任。在适用法律允许的最大范围内,作者明确放弃了所有明示或暗示的担保和条件。 + +免责声明: +该程序仅用于学习和研究Python网络爬虫和网页处理技术,不得用于任何非法活动或侵犯他人权益的行为。使用本程序所产生的一切法律责任和风险,均由用户自行承担,与作者和项目贡献者无关。作者不对因使用该程序而导致的任何损失或损害承担任何责任。 + +请在使用本程序之前确保遵守相关法律法规和网站的使用政策,如有疑问,请咨询法律顾问。 + +无论您对程序进行了任何操作,请始终保留此信息。 +""" + +import re + + +# 替换非法字符 +def rename(name): + # 定义非法字符的正则表达式模式 + illegal_characters_pattern = r'[\/:*?"<>|]' + + # 定义替换的中文符号 + replacement_dict = { + '/': '/', + ':': ':', + '*': '*', + '?': '?', + '"': '“', + '<': '<', + '>': '>', + '|': '|' + } + + # 使用正则表达式替换非法字符 + sanitized_path = re.sub(illegal_characters_pattern, lambda x: replacement_dict[x.group(0)], name) + + return sanitized_path + + +def fix_publisher(text): + # 针对性去除所有 出版物 所携带的标签 + text = re.sub(r'

', '', text) + text = re.sub(r'