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