commit d8a79073da2ff38d6544e6c76bfaa34525d6f47b Author: RRRosmontis Date: Tue Aug 18 07:55:05 2026 +0000 上传文件至「/」 diff --git a/YbbLolicon!.py b/YbbLolicon!.py new file mode 100644 index 0000000..f2dcfd3 --- /dev/null +++ b/YbbLolicon!.py @@ -0,0 +1,477 @@ +while True: + version = "V.2025.2.21(c)" + import easygui as g + import os + import json + import ping3 + import requests + import re + import time + import urllib3 + from datetime import datetime as t + from tqdm import tqdm + from PIL import Image + import webbrowser + +#字符画↘ + YbbLolicon = """————————————————————————————————————————————————————————————————————————————————————————————————————————— + ||| ||| | +||| || || || |||| ||| || || + ||| | || || || || || + || | |||||| |||||| || ||| || | |||| ||| |||||| || + || | || || || || || || || || || || || || || || || || + || || || || || || || || || || || || || || || || + || ||| || ||| || ||| ||| || || || || || | || || || || + |||| ||||| ||||| ||||||| ||||| |||| |||| ||||| ||||| |||| |||| || + +————————————————————————————————————————————————————————————————————————————————————————————————————————— +""" + print(f"{YbbLolicon}\n\n") + + +#禁用警告↘ + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + + +#时间定义↘ + def NowTime(): + now = t.now() + current_time = now.strftime("%H:%M:%S") + return current_time + + +#图片获取定义: + def PictureGet(NumberDisplay,GetTimesDisplay,file_path,pid,API): + if API == "1": + url = f"https://pixiv.re/{pid}.png" + elif API == "2": + url0 = f"https://open.pximg.org/pid.php?pid={pid}" + response = requests.get(url0) + json_data = (response.json()) + url = json_data["origin"] + elif API == "3": + num = GetTimes + if num % 2 == 0: + url = f"https://pixiv.re/{pid}.png" + else: + url0 = f"https://open.pximg.org/pid.php?pid={pid}" + response = requests.get(url0) + json_data = (response.json()) + url = json_data["origin"] + CurrentTime = NowTime() + print(f"[{CurrentTime}]开始获取图片,{GetTimesDisplay}/{NumberDisplay}(url:{url})") + tip = '下载 {}' + CurrentTime = NowTime() + desc = f"[{CurrentTime}]{tip}".format(os.path.basename(file_path)) + progress_bar = tqdm(initial=0, unit='B', unit_divisor=1024, unit_scale=True, desc=desc) + try: + response = requests.get(url,stream=True,verify=False) + if response.status_code == 200: + if 'Transfer-Encoding' in response.headers: + with open(file_path,'wb') as f: + for chunk in response.iter_content(1024): + f.write(chunk) + progress_bar.update(len(chunk)) + f.close() + progress_bar.close() + CurrentTime = NowTime() + print(f"[{CurrentTime}]完成获取,{GetTimesDisplay}/{NumberDisplay}") + elif 'Content-Length' in response.headers: + if len(response.content) == int(response.headers['Content-Length']): + with open(file_path,'wb') as f: + for chunk in response.iter_content(1024): + f.write(chunk) + progress_bar.update(len(chunk)) + f.close() + progress_bar.close() + CurrentTime = NowTime() + print(f"[{CurrentTime}]完成获取,{GetTimesDisplay}/{NumberDisplay}") + except requests.exceptions.SSLError: + progress_bar.close() + CurrentTime = NowTime() + print(f"[{CurrentTime}]图片获取失败,pid:{pid},{GetTimesDisplay}/{NumberDisplay} (如果连续看到此报错,本次获取可能已被api限速,请一段时间后重试,或使用代理.)") + except requests.exceptions.ChunkedEncodingError: + progress_bar.close() + CurrentTime = NowTime() + print(f"[{CurrentTime}]图片获取失败,pid:{pid},{GetTimesDisplay}/{NumberDisplay} (可能是网络中断引起的连接超时,也可能是API限制..)") + + + +#等待10秒定义↘ + def sleep10s(): + with tqdm(range(10), desc='等待时间') as tbar: + for i in tbar: + tbar.set_postfix(loss=i/10, x=i) + time.sleep(1) + tbar.close() + + +#词典存储定义↘ + def dict_save(FilePathDict,FilterTagList,AIban,API): + dictdata = {'FilePathDict': FilePathDict,'FilterTagList':FilterTagList,"AIban":AIban,"API":API} + with open(f'{data_path}\dictdata.json','w') as f: + json.dump(dictdata, f) + + +#图片有效性检测定义↘ + def PictureTest(file_path,InvalidGetList,pid): + try: + img = Image.open(file_path) + img.verify() + except (IOError, SyntaxError): + InvalidGetList.append(pid) + CurrentTime = NowTime() + print(f"[{CurrentTime}]检测到下载图片可能为无效图片,将在主获取结束后尝试重新获取.pid:{pid}") + + +#屏蔽词条修改定义↘ + def Filter(FilterTagList,AIban,FilePathDict): + FilterInput = g.multenterbox(msg=f"请输入屏蔽词条\n支持多词条,请用单个空格分隔", title='YbbLolicon!', fields=("屏蔽词条列表","AI作品排除(true启用/false禁用)"),values=(FilterTagList,AIban)) + try: + FilterTagList = FilterInput[0] + AIbanTest = FilterInput[1] + if AIbanTest == "false" or AIbanTest == "true": + AIban = AIbanTest + dict_save(FilePathDict,FilterTagList,AIban,API) + g.msgbox(msg=F"屏蔽词条修改成功,词典已保存.",title="YbbLolicon!",ok_button="确定") + else: + g.msgbox(msg=F"AI排除项格式输入有误,请重新填写.",title="YbbLolicon!",ok_button="确定") + except TypeError: + pass + + +#本地词典存在检测↘ + data_path = os.path.expanduser("~\Documents\YbbLolicon") + try: + with open(f'{data_path}\dictdata.json', "r") as file: + jsonresult = "本地记录词典获取成功." + except FileNotFoundError: + try: + SavePathCreat = os.path.expanduser("~\Documents") + os.chdir(SavePathCreat) + NewFolder = "YbbLolicon" + os.makedirs(NewFolder) + FilterTagList = "" + FilePathPre = g.diropenbox(title='请选择图片保存路径') + FilePathDict = f"{FilePathPre}" + AIban = "false" + API = "1" + dictdata = {'FilePathDict':FilePathDict,'FilterTagList':FilterTagList,"AIban":AIban,"API":API} + with open(f'{data_path}\dictdata.json', 'w') as f: + json.dump(dictdata, f) + jsonresult = '检测到首次使用或json已丢失,已重新创建json,请按照提示修改默认存储路径与过滤词条.' + except FileExistsError: + FilterTagList = "无屏蔽词条." + FilePathPre = g.diropenbox(title='请选择图片保存路径') + FilePathDict = f"{FilePathPre}" + AIban = "false" + API = "1" + Filter(FilterTagList,AIban,FilePathDict,API) + dictdata = {'FilePathDict':FilePathDict,'FilterTagList':FilterTagList,"AIban":AIban,"API":API} + with open(f'{data_path}\dictdata.json', 'w') as f: + json.dump(dictdata, f) + jsonresult = '检测到首次使用或json已丢失,已重新创建json,请按照提示修改默认存储路径与过滤词条.' + + + +#词典保存文件获取↘ + with open(f'{data_path}\dictdata.json', 'r') as f: + loaded_dict = json.load(f) + FilePathDict = loaded_dict["FilePathDict"] + FilterTagList = str(loaded_dict["FilterTagList"]) + if FilterTagList == None: + FilterTagList = [None] + AIban = str(loaded_dict["AIban"]) + if AIban == None: + AIban = "false" + API = loaded_dict["API"] + if API == None: + API = "1" + +#主界面↘ + if FilterTagList == "": + FilterTagListDisplay = "无" + else: + FilterTagListDisplay = FilterTagList + if API == "1": + APIDisplay = "pixiv.re" + elif API == "2": + APIDisplay = "pximg.org" + elif API == "3": + APIDisplay = "pixiv.re与pximg.org混合." + time.sleep(1) + print(F"{version} by RRRosmontis.\n\n{jsonresult}\n当前图片存储路径:{FilePathDict}\n所有屏蔽词条:{FilterTagListDisplay}\nAI作品排除:{AIban}\nAPI选择:{APIDisplay}") + input("\n按Enter进入主界面.") + choices = ['获取图片(指定tag)','获取图片(指定pid)','设置'] + selection = g.choicebox(f'—————— YbbLolicon! 涩图获取 ——————\n \n选择要进行的项目','YbbLolicon!',choices = choices) +#变量设定↘ + GetTimes = 0 + RunAgain = "Y" + InvalidGetList = [] + + +#图片获取变量请求↘ + if selection == "获取图片(指定tag)": + print("\n") + if FilePathDict == "None": + g.msgbox(msg=f"无图片存储路径.请修改默认值.",title="YbbLolicon!",ok_button="确定") + print("\033c", end="") + os._exit(0) + try: + if FilePathDict == "None": + g.msgbox(msg=F"警告:存储路径未填写,请退出程序并修改,否则无法正常保存.",title="YbbLolicon!",ok_button="确定") +#ping↘ + print("正在测试api延迟,预计时间:小于5秒.") + target_host_1 = 'lolicon.app' + target_host_2 = 'pixiv.re' + target_host_3 = 'pximg.org' + delay_1 = ping3.ping(target_host_1,timeout=2) + delay_2 = ping3.ping(target_host_2,timeout=3,size=1024) + delay_3 = ping3.ping(target_host_3,timeout=2,size=1024) + if delay_1 is None: + print(f"ping {target_host_1} 延迟较高,请检查网络连接或查询相关服务器状态.") + else: + print(f"ping {target_host_1} 延迟为{delay_1}秒.") + if delay_2 is None: + print(f"ping {target_host_2} 延迟较高,请尝试挂加速器后重启程序或查询相关服务器状态.") + else: + print(f"ping {target_host_2} 延迟为{delay_2}秒.") + if delay_3 is None: + print(f"ping {target_host_3} 延迟较高,请尝试挂加速器后重启程序或查询相关服务器状态.") + else: + print(f"ping {target_host_3} 延迟为{delay_3}秒.") + print("注意:多次获取可能会导致api访问速度变慢或暂时禁止访问,请控制获取数量.\n为防止api速率限制,每次获取完成后将会有10秒冷却时间.\n") +#获取请求↘ + userinput = g.multenterbox(msg=f"· 以下为tag说明:\n 获取想要图片的主要tag(如人物名)\n 支持多tag,不同tag间应用单个空格隔开\n \n· 以下为获取数量说明:\n 获取区间:1—20\n 多次获取可能返回相同结果,请尝试更换其他tag(如角色外文名)", title=f'YbbLolicon!', fields=("tag","数量")) + try: + GETINFOORGINAL = userinput[0] + NUMBER = int(userinput[1]) + except ValueError : + g.msgbox(msg=f"数字格式输入有误.请使用阿拉伯数字.",title="YbbLolicon!",ok_button="确定") + RunAgain = "Y" + except TypeError: + RunAgain = "Y" + try: + if NUMBER > 20: + print(f"获取数量过大,无法请求\n将按最大数量(20)请求.({NUMBER}>20)") + NUMBER = int("20") + GETINFO = GETINFOORGINAL.replace(" ","&tag=") + GETINFO = f"tag={GETINFO}" + tag = GETINFO.replace(" ","") + R18choice = ['全年龄','R18','混合'] + R18selection = g.choicebox('请选择年龄分级\n选择完毕后图片将开始获取,请耐心等待','YbbLolicon!',choices = R18choice) + if R18selection == "全年龄": + R18 = "0" + elif R18selection == "R18": + R18 = "1" + elif R18selection == "混合": + R18 = "2" + #请求api ↘ + url = f"https://api.lolicon.app/setu/v2?size=original&r18={R18}&{tag}&num={NUMBER}&excludeAI={AIban}" + #处理返回的json↘ + response = requests.get(url) + json_data = response.json() + result = json_data + #检测返回中是否有链接以确定是否有结果↘ + pattern = str(r'https') + json_data_str = str(json_data) + match = re.search(pattern,json_data_str) + if match: + print("从API获取到结果,已开始获取.") + else: + g.msgbox(msg=f"未从api获取到相应结果.\ntag:{GETINFOORGINAL}\n注意:如果在此处看到了连续无间隔或间隔过长的期望词条,可能是由于没有正确使用空格进行分隔.",title="YbbLolicon!",ok_button="确定") + #从返回的data中获取值↘ + print("正在处理结果并进行词条检测.") + GetList = [] #待获取pid列表 + ResultList = json_data["data"] + ResultCount = len(ResultList) + if ResultCount < NUMBER: + print("\n注意:结果较少,获取数目可能无法达到预期值.\n") + for index, back in enumerate(json_data["data"]): + pid = back["pid"] + tags = str(back["tags"]) + #词条检测↘ + FilterTagListSplit = FilterTagList.split() #空白分割为列表 + def FiltertagTest(FilterTag): + match = re.search(FilterTag,tags) + if not match: + TestResult = "y" + else: + CurrentTime = NowTime() + print(f"[{CurrentTime}]检测到过滤词条,pid:{pid}") + TestResult = "n" + return TestResult + for index, FilterTag in enumerate(FilterTagListSplit): + TestResult = FiltertagTest((FilterTag)) + if TestResult == "n": + break + if TestResult == "n": + continue + elif TestResult == "y": + GetList.append(pid) + CurrentTime = NowTime() + print(f"[{CurrentTime}]词条检测完成.本次符合条件的pid为:{GetList}") + #获取图片↘ + for pid in GetList: + GetTimes = GetTimes + 1 + NumberDisplay = NUMBER + GetTimesDisplay = GetTimes + file_path = f"{FilePathDict}\{pid}.png" + PictureGet(NumberDisplay,GetTimesDisplay,file_path,pid,API) + #图片有效性检测↘ + time.sleep(1) + PictureTest(file_path,InvalidGetList,pid) + if GetTimes < NUMBER: + sleep10s() + #大结果检测与词典存储↘ + if GetTimes == NUMBER: + dict_save(FilePathDict,FilterTagList,AIban,API) + InvalidGetListStr = str(InvalidGetList) + if InvalidGetListStr == "[]": + g.msgbox(msg=f"获取完成.\n有效获取次数:{GetTimes}.",title="YbbLolicon!",ok_button="确定") + if GetTimes < NUMBER: + if GetTimes != 0: + dict_save(FilePathDict,FilterTagList,AIban,API) + InvalidGetListStr = str(InvalidGetList) + if InvalidGetListStr == "[]": + g.msgbox(msg=f"没有足够的结果继续获取.\n有效获取次数:{GetTimes}\ntag:{GETINFOORGINAL}.",title="YbbLolicon!",ok_button="确定") + else: + print(f"\n注意:没有足够的结果继续获取.\n") + InvalidGetListStr = str(InvalidGetList) + if InvalidGetListStr != "[]": + CurrentTime = NowTime() + print(f"[{CurrentTime}]主获取完成.检测到有无效获取.若不关闭程序,将在30秒后重新尝试获取无效图片.") + with tqdm(range(30), desc='等待时间') as tbar: + for i in tbar: + tbar.set_postfix(loss=i/30, x=i) + time.sleep(1) + print("正在尝试重新获取.") + InvalidGetTimes = 0 + InvalidNumber = len(InvalidGetList) + for index, pid in enumerate(InvalidGetList): + url = f"https://pixiv.re/{pid}.jpg" + InvalidGetTimes = InvalidGetTimes +1 + NumberDisplay = InvalidNumber + GetTimesDisplay = InvalidGetTimes + file_path = f"{FilePathDict}\{pid}.png" + PictureGet(NumberDisplay,GetTimesDisplay,file_path,pid,API) + if InvalidGetTimes < InvalidNumber: + sleep10s() + if InvalidGetTimes == InvalidNumber: + if GetTimes < NUMBER: + g.msgbox(msg=f"获取完成.(没有足够的结果继续获取)\n有效获取次数:{GetTimes}\n无效图片数:{InvalidNumber},重获取次数:{InvalidGetTimes}.",title="YbbLolicon!",ok_button="确定") + else: + g.msgbox(msg=f"获取完成.\n有效获取次数:{GetTimes}\n无效图片数:{InvalidNumber},重获取次数:{InvalidGetTimes}.",title="YbbLolicon!",ok_button="确定") + except NameError: + print("\033c", end="") + RunAgain = "Y" +#网络报错处理↘ + except requests.exceptions.SSLError: + if GetTimes > 0: + GetTimesErrorsInform = GetTimes - 1 + elif GetTimes == 0: + GetTimesErrorsInform = 0 + g.msgbox(msg=f"服务器访问次数过多,请一段时间后尝试重新获取.\n已获取次数:{GetTimesErrorsInform}\n本次tag:{GETINFOORGINAL}\nurl:{url}",title="YbbLolicon!",ok_button="确定") + except requests.exceptions.ConnectionError: + g.msgbox(msg=f"获取失败:远程主机强迫关闭了一个现有的连接.",title="YbbLolicon!",ok_button="确定") + + +#其他功能性choices处理↘ + elif selection == "获取图片(指定pid)": + print("\n") + try: + print("正在测试api延迟,预计时间:小于5秒.") + target_host = 'pixiv.re' + delay = ping3.ping(target_host,timeout=3,size=1024) + if delay is None: + print(f"ping {target_host} 延迟较高,请尝试挂加速器后重启程序或查询相关服务器状态.") + else: + print(f"ping {target_host} 延迟为{delay}秒.") + pid = g.enterbox(msg='输入指定pid.', title='YbbLolicon!', default='', strip=True, image=None, root=None) + pidtest = str(pid) + if pidtest == "None": + pass + else: + try: + pid = int(pid) + NumberDisplay = "1" + GetTimesDisplay = "1" + file_path = f"{FilePathDict}\{pid}.png" + PictureGet(NumberDisplay,GetTimesDisplay,file_path,pid,API) + PictureTest(file_path,InvalidGetList,pid) + InvalidGetListStr = str(InvalidGetList) + if InvalidGetListStr == "[]": + g.msgbox(msg=f"获取完成.",title="YbbLolicon!",ok_button="确定") + elif InvalidGetListStr != "[]": + CurrentTime = NowTime() + print(f"[{CurrentTime}]主获取完成.检测到有无效获取.若不关闭程序,将在30秒后重新尝试获取无效图片.") + with tqdm(range(30), desc='等待时间') as tbar: + for i in tbar: + tbar.set_postfix(loss=i/30, x=i) + time.sleep(1) + print("正在尝试重新获取.") + PictureGet(NumberDisplay,GetTimesDisplay,file_path,pid,API) + try: + img = Image.open(file_path) + img.verify() + g.msgbox(msg=f"获取完成.",title="YbbLolicon!",ok_button="确定") + except (IOError, SyntaxError) as e: + InvalidGetList.append(pid) + CurrentTime = NowTime() + g.msgbox(msg=f"检测到下载项仍无效,请确保pid准确,或一段时间后重试.pid:{pid}",title="YbbLolicon!",ok_button="确定") + + + except ValueError: + g.msgbox(msg=f"数字格式输入有误.请使用阿拉伯数字.",title="YbbLolicon!",ok_button="确定") + except requests.exceptions.SSLError: + g.msgbox(msg=f"服务器访问次数过多,请一段时间后尝试重新获取.\nurl:{url}",title="YbbLolicon!",ok_button="确定") + except requests.exceptions.ConnectionError: + g.msgbox(msg=f"获取失败:远程主机强迫关闭了一个现有的连接.",title="YbbLolicon!",ok_button="确定") + + elif selection == "设置": + choices = ['保存路径修改','屏蔽词条修改','API设定',"API详情"] + selection = g.choicebox(f'—————— YbbLolicon! 涩图获取 ——————\n(设置页) \n \n选择要进行的项目','YbbLolicon!',choices = choices) + if selection == "保存路径修改": + print("\n") + FilePathDict = g.diropenbox(title='请选择图片保存路径') + FilePathDictTest = str(FilePathDict) + if FilePathDictTest == "None": + pass + else: + dict_save(FilePathDict,FilterTagList,AIban,API) + g.msgbox(msg=f"保存路径修改成功,词典已保存.",title="YbbLolicon!",ok_button="确定") + + elif selection == "屏蔽词条修改": + print("\n") + Filter(FilterTagList,AIban,FilePathDict) + + elif selection == "API设定": + APIChoice = g.buttonbox(msg=f'请选择需要使用的API:\n1:pixiv.re\n2:pximg.org\n3:以上两个API交替使用.\n \n当前api:{API}\n如果不进行选择而关闭窗口,API将自动设定为1.', title='YbbLolicon!', choices=('1', '2', '3'), image=None, images=None, default_choice=None, cancel_choice=None, callback=None, run=True) + if APIChoice == "None": + API == "1" + elif APIChoice == None: + API == "1" + else: + API = APIChoice + dict_save(FilePathDict,FilterTagList,AIban,API) + g.msgbox(msg=f"API设定成功,词典已保存.",title="YbbLolicon!",ok_button="确定") + + elif selection == "API详情": + choices = ['lolicon.app','pixiv.re','pximg.org'] + selection = g.choicebox(f'请选择需要了解的API\nlolicon.app:获取需求tag的pid用.\npixiv.re:取图用.\npximg.org:取图用.\n可手动访问以确定API是否仍在提供服务.','YbbLolicon!',choices = choices) + if selection == "lolicon.app": + webbrowser.open("lolicon.app") + elif selection == "pixiv.re": + webbrowser.open("pixiv.re") + elif selection == "pximg.org": + webbrowser.open("pximg.org") + + else: + RunAgain = "N" + + + if RunAgain == "Y": + pass + print("\033c", end="") + elif RunAgain == "N": + break \ No newline at end of file diff --git a/YbbLolicon!.spec b/YbbLolicon!.spec new file mode 100644 index 0000000..df322d9 --- /dev/null +++ b/YbbLolicon!.spec @@ -0,0 +1,39 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['YbbLolicon!.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + name='YbbLolicon!', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=['ico.ico'], +) diff --git a/ico.ico b/ico.ico new file mode 100644 index 0000000..3e0eb12 Binary files /dev/null and b/ico.ico differ diff --git a/ico.png b/ico.png new file mode 100644 index 0000000..283ce23 Binary files /dev/null and b/ico.png differ