.avif(data:image/avif;base64,)图片转jpg下载

博主发现网站图片另存为本地是AVIF格式无法查看,想用Python实现转换。起初按GPT建议安装avif报错,后找到pillow_avif包,借助AI写出可直接放链接转JPG的代码,运行效果良好。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

逛某网站发现网站图片另存为本地是avis,自然下载下来也无法查看,不能偷图这咋行,赶紧网上找方法。

这种小功能还是想用python来执行,方便(虽然我不会python QAQ)
一开始听了gpt的,搞什么pip install avif ,装的时候就报错了,奇奇怪怪看着就麻烦。

继续找,看到有人文章地址说pillow_avif 这个包啊,确实可以
还是找AI写了个可以直接放链接转jpg的代码(包自己下,pillow_avif包下不了可以看下上面的文章地址)

import base64
from PIL import Image
import pillow_avif
from io import BytesIO

def avif_to_jpg(avif_data, output_file):
    # 将 AVIF 数据解码
    img_data = avif_data.split(',')[-1]
    byte_data = base64.b64decode(img_data)
    
    # 使用 BytesIO 对象包装字节数据
    byte_stream = BytesIO(byte_data)
    
    # 使用 pillow_avif 插件打开 AVIF 图片
    image = Image.open(byte_stream)
    
    # 将 AVIF 图片保存为 JPEG 格式
    image.save(output_file, 'JPEG')

if __name__ == "__main__":
	#完整base64编码图片数据
    avif_data = "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWl...."
    output_file = "output.jpg"
    avif_to_jpg(avif_data, output_file)
    print(f"AVIF 图片已成功转换为 JPEG 格式并保存为 {output_file}")

运行结果如下,完全ok
在这里插入图片描述

import vuetify, { transformAssetUrls } from "vite-plugin-vuetify"; import versionUpdatePlugin from "./plugins/versionUpdatePlugin"; import viteCompression from "vite-plugin-compression"; import cssnano from "cssnano"; // PWA Config const shortTitle = "Xescort.net"; const description = "The best incall & outcall independent escort, 100% real escort and real-name verification, the best dating experience, regardless of gender, age, region or other more requirements can be met."; const themeColor = "#4f46e5"; // https://v3.nuxtjs.org/api/configuration/nuxt.config export default defineNuxtConfig({ modules: [ "@nuxtjs/tailwindcss", "@vite-pwa/nuxt", "@pinia/nuxt", "@pinia-plugin-persistedstate/nuxt", "@nuxt/image", "nuxt-vitalizer", async (options, nuxt) => { nuxt.hooks.hook("vite:extendConfig", (config) => { config.plugins ||= []; config.plugins.push(vuetify()); }); }, ], postcss: { plugins: { "@fullhuman/postcss-purgecss": { content: ["./pages/**/*.vue", "./components/**/*.vue", "./layouts/**/*.vue", "./app.vue"], safelist: [ /^v-/, // 保留所有 Vuetify 类名 /^mdi-/, /--text$/, /--bg$/, /^theme--/, ], }, }, }, image: { formats: ["webp", "avif"], screens: { xs: 320, sm: 640, md: 768 }, presets: { cover: { modifiers: { fit: "cover", format: "webp" } }, }, // domains: ["https://assets.xescort.net"], }, build: { analyze: { analyzerMode: "static", }, transpile: ["vuetify", "@fancyapps/ui", "@nuxt/image"], }, ssr: true, // import styles experimental: { externalVue: false, }, css: [ "vue-snap/dist/vue-snap.css", "@/assets/main.scss", "@/assets/icons/iconfont.css", "@/assets/icons/bootstrap-icons.css", ], devtools: { enabled: true }, routeRules: { "/**": { appMiddleware: ["cache"], }, }, // enable takeover mode typescript: { shim: false }, nitro: { compressPublicAssets: true, prerender: { // crawlLinks: true, // 自动爬取页面链接 routes: [], // 入口页面 ignore: ["/_ipx/**"] /* dynamic routes */, }, routeRules: { "/**": { // cache: false, // headers: { // "Cache-Control": "no-cache, no-store, must-revalidate", // }, }, "/api/**": { cache: { swr: true, maxAge: 60, }, }, // 60秒后重新验证 "/": { prerender: false, // 预渲染首页 isr: 3600, // swr: 60, headers: { "Cache-Control": "public, max-age=3600, s-maxage=3600", // 缓存1小时 }, }, "/categories/[type]/[args]": { prerender: false, // 预渲染分类页面 isr: 3600, swr: 600, headers: { "Cache-Control": "public, max-age=3600, s-maxage=3600", // 缓存1小时 }, }, "/search": { prerender: false, // 预渲染搜索页面 isr: 3600, swr: 600, headers: { "Cache-Control": "public, max-age=3600, s-maxage=3600", // 缓存1小时 }, }, "/details/[tag]/[identity]": { prerender: false, isr: 3600, swr: 600, headers: { "Cache-Control": "public, max-age=3600, s-maxage=3600", // 缓存1小时 }, }, "/escorts/[country]": { prerender: false, // 预渲染所有 escort 页面 isr: 3600, swr: 600, headers: { "Cache-Control": "public, max-age=3600, s-maxage=3600", // 缓存1小时 }, }, "/escorts/[country]/[state]": { prerender: false, // 预渲染所有 escort 页面 isr: 3600, swr: 600, headers: { "Cache-Control": "public, max-age=3600, s-maxage=3600", // 缓存1小时 }, }, "/escorts/[country]/[state]/[city]": { prerender: false, // 预渲染所有 escort 页面 isr: 3600, swr: 600, headers: { "Cache-Control": "public, max-age=3600, s-maxage=3600", // 缓存1小时 }, }, }, }, // Based on docs found here - https://vuetifyjs.com/en/getting-started/installation/#using-nuxt-3 vite: { css: { preprocessorOptions: { scss: { api: "modern-compiler", }, }, // postcss: { // plugins: [ // cssnano({ // preset: ["default", { discardComments: { removeAll: true } }], // }), // ], // }, }, optimizeDeps: { include: ["vuetify", "@fancyapps/ui"], }, build: { modulePreload: false, sourcemap: false, emptyOutDir: true, // 强制清空输出目录 rollupOptions: { output: { format: "es", manualChunks: (id) => { if (id.includes(".scss")) { return "style"; } if (id.includes("node_modules")) { return "vendor"; } }, }, }, }, plugins: [ viteCompression({ algorithm: "gzip", ext: ".gz", threshold: 2048, // Only compress files larger than 10KB }), versionUpdatePlugin(), vuetify({ autoImport: true, }), ], vue: { template: { transformAssetUrls, }, }, // server: { // // host: "0.0.0.0", // Removed as it is not allowed in the current type // proxy: { // "/api": { // target: "https://www.xkwsb.top", // changeOrigin: true, // // rewrite: (path) => path.replace(/^\/api/, ""), // }, // }, // }, define: { "process.env.DEBUG": false, }, }, // devServer: { // https: { // cert: "./certs/cert.pem", // key: "./certs/dev.pem", // }, // }, runtimeConfig: { isServer: true, baseUrl: process.env.NUXT_BASE_URL, port: process.env.PORT || "8000", public: { wsUrl: process.env.NUXT_WS_URL, chatWs: process.env.NUXT_CHAT_WS, chatApi: process.env.NUXT_CHAT_API, port: process.env.PORT || "8000", baseUrl: process.env.NUXT_BASE_URL, gClient: process.env.GOOGLE_CLIENT_ID, }, }, vitalizer: { delayHydration: { hydrateOnEvents: ["click", "touchstart"], idleCallbackTimeout: 2000, postIdleTimeout: 1000, }, disablePrefetchLinks: "dynamicImports", disablePreloadLinks: true, disableStylesheets: "entry", }, app: { head: { htmlAttrs: { lang: "en", }, meta: [ { name: "theme-color", content: themeColor }, { name: "mobile-web-app-capable", content: "yes" }, { name: "rating", content: "adult" }, { name: "robots", content: "noarchive" }, { name: "referrer", content: "no-referrer" }, ], link: [ { rel: "stylesheet", href: "https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css", }, { rel: "icon", type: "image/x-icon", href: "/icons/favicon.ico" }, { rel: "apple-touch-icon", href: "/icons/apple-touch-icon.png" }, { rel: "alternate", href: "https://www.xescort.net", hreflang: "x-default" }, { rel: "preconnect", href: "https://api.iconify.design" }, { rel: "preconnect", href: "https://assets.xescort.net" }, { rel: "dns-prefetch", href: "https://api.iconify.design" }, ], // // DNS 预解析 Vue 官方 CDN // { rel: "dns-prefetch", href: "https://unpkg.com" }, // // 预解析常用 CDN(按需添加) // { rel: "dns-prefetch", href: "https://cdn.jsdelivr.net" }, // { rel: "dns-prefetch", href: "https://fonts.googleapis.com" }, // // 推荐同时配置 TCP 预连接 // { rel: "preconnect", href: "https://unpkg.com", crossorigin: "anonymous" }, script: [ // { // src: "https://unpkg.com/vue@3/dist/vue.global.prod.js", // defer: true, // 异步加载 // tagPosition: "bodyClose", // }, // { // src: "https://www.googletagmanager.com/gtag/js?id=G-5VCS4RBFF2", // tagPosition: "bodyClose", // tagPriority: "low", // }, // { // type: "text/javascript", // innerHTML: // "window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-5VCS4RBFF2');", // tagPosition: "bodyClose", // tagPriority: "low", // }, ], }, }, pwa: { includeAssets: ["favicon.ico", "*.txt"], manifest: { name: shortTitle, short_name: shortTitle, description: description, theme_color: themeColor, lang: "en", background_color: "#ffffff", icons: [ { src: "/icons/pwa-192x192.png", sizes: "192x192", type: "image/png", purpose: "any", }, { src: "/icons/pwa-512x512.png", sizes: "512x512", type: "image/png", purpose: "any", }, { src: "/icons/pwa-maskable-192x192.png", sizes: "192x192", type: "image/png", purpose: "maskable", }, { src: "/icons/pwa-maskable-512x512.png", sizes: "512x512", type: "image/png", purpose: "maskable", }, ], }, workbox: { globPatterns: ["**/*.{js,css,html,png,webp,gif,jpg,jpeg,svg,ico}"], globIgnores: ["**/icons/iconfont.css", "**/icons/bootstrap-icons.css"], runtimeCaching: [ { urlPattern: ({ request }) => request.destination === "image", handler: "CacheFirst", options: { cacheName: "images", expiration: { maxEntries: 50, maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days }, }, }, { urlPattern: ({ request }) => request.destination === "script" || request.destination === "style", handler: "StaleWhileRevalidate", options: { cacheName: "scripts-styles", expiration: { maxEntries: 20, maxAgeSeconds: 1 * 24 * 60 * 60, // 1 days }, }, }, ], }, }, compatibilityDate: "2025-08-12", }); 帮我分析下这个nuxt3的配置文件 修改哪些地方可以优化项目
最新发布
08-20
检查下下面这段代码,有无语法错误,或者逻辑错误#!/usr/bin/python3 # coding=utf-8 import io import sys import time import requests import json import re import base64 from urllib.parse import urlparse, urljoin, quote import urllib3 import gzip import zlib import brotli import chardet from typing import Optional, Tuple # 禁用SSL警告 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') SUC_RES = { 'resCode': 200, 'resTime': 0, 'keyword': 'SUCCESS', 'message': [] } FAIL_RES = { 'resCode': 500, 'resTime': 0, 'keyword': 'FAILED', 'message': [] } # 封装解码 class HttpResponseProcessor: def __init__(self, url: str, headers: dict = None): """ 初始化响应处理器 :param url: 请求的URL :param headers: 请求头,默认为None """ self.url = url self.headers = headers or {} self.response = None self.raw_content = None self.text_content = None self.encoding = None self.status_code = None def fetch_response(self): """ 发送HTTP请求并获取响应 :return: None """ try: self.response = requests.get( url=self.url, headers=self.headers, allow_redirects=False, # 禁用自动重定向 stream=True # 流模式获取原始响应 ) self.status_code = self.response.status_code self.raw_content = self.response.content except Exception as e: raise Exception(f"请求失败: {str(e)}") def print_response_headers(self): """ 打印响应头信息 :return: None """ if not self.response: raise Exception("尚未获取响应,请先调用 fetch_response()") def decode_content(self) -> str: """ 尝试解码内容为文本 :return: 解码后的文本内容 """ if not self.raw_content: raise Exception("尚未获取原始内容,请先调用 fetch_response()") try: # 检测内容编码 encoding_detected = chardet.detect(self.raw_content)['encoding'] # 尝试解码 if encoding_detected: self.text_content = self.raw_content.decode(encoding_detected) self.encoding = encoding_detected else: # 尝试常见编码 for encoding in ['utf-8', 'gbk', 'gb2312', 'latin1']: try: self.text_content = self.raw_content.decode(encoding) self.encoding = encoding break except: continue else: self.text_content = "无法解码内容" self.encoding = None return self.text_content except Exception as e: # 将内容保存到文件以便分析 with open('response.bin', 'wb') as f: f.write(self.raw_content) raise Exception("内容解码失败") def process_response(self) -> Tuple[int, Optional[str], Optional[str]]: """ 完整处理响应的便捷方法 :return: (status_code, text_content, encoding) """ self.fetch_response() self.print_response_headers() text = self.decode_content() return self.status_code, text, self.encoding def print_err_result(e): FAIL_RES['error'] = e exit(1) def make_request(url, params=None, data=None, method='get', session=None): try: start = time.time() req_func = session.get if session else requests.get if method.lower() == 'post': req_func = session.post if session else requests.post headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Accept': 'application/json,text/plain,text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'X-Requested-With': 'XMLHttpRequest', 'accept-encoding': 'gzip, deflate, br,zstd' } response = req_func( url, params=params, data=data, verify=False, headers=headers ) res_time = (time.time() - start) * 1000 if response.status_code in [200, 302]: SUC_RES['resTime'] = int(res_time) SUC_RES['message'].append(f"请求 {url} 成功") return response else: FAIL_RES['error'] = f"请求失败,状态码: {response.status_code}, 响应内容: {response.text},头信息:{session.headers}" FAIL_RES['message'].append(f"请求 {url} 失败") return None except Exception as e: print_err_result(f"请求过程中发生错误: {str(e)}") return None def cas_login(username, password): # 使用会话保持cookies session = requests.Session() token = None try: # 第一步:获取lt令牌 params1 = { 'service': 'https://www.fifedu.com/iplat/ssoservice', 'get-lt': 'true', 'n': str(int(time.time() * 1000)), 'callback': 'jsonpcallback', '_': str(int(time.time() * 1000)) } url1 = "https://cycore.fifedu.com/cas-server/login" response1 = make_request(url1, params=params1, session=session) if not response1: return False # 1. 检查响应是否以jsonpcallback开头 if not response1.text.startswith('jsonpcallback'): raise ValueError("响应格式不符合预期,不是JSONP格式") # 2. 提取括号内的JSON部分 json_str = response1.text[len('jsonpcallback('):-2] # 去掉首尾的jsonpcallback(和); # 3. 将字符串解析为字典 data = json.loads(json_str) # 4. 提取所需的值 lt = data.get('lt', '') execution = data.get('execution', '') if not lt or not execution: raise ValueError("响应中缺少lt或execution字段") # 第二步:提交登录表单 data2 = { 'service': 'https://cycore.fifedu.com/iplat/ssoservice', 'callback': 'logincallback', 'isajax': 'true', 'isframe': 'true', '_eventId': 'submit', 'serviceURL': 'null', 'lt': lt, 'type': 'pwd', 'execution': execution, 'username': username, 'password': password, '_': str(int(time.time() * 1000)) } url2 = "https://cycore.fifedu.com/cas-server/login" response2 = make_request(url2, data=data2, method='post', session=session) if not response2: return False # 检查登录是否成功 response_text = response2.text.strip() if response_text.startswith("logincallback"): json_str = response_text[len("logincallback("):-2] login_result = json.loads(json_str) token = login_result.get("token", "") ticket = login_result.get("ticket", "") if not token or not ticket: return False else: return False # 第三步:ssosevice跳 params3 = { 'callback': 'jQuery371043019069426885914_1754445556768', 'action': 'login', '_': '1754445556769' #冯改'callback': 'jQuery37107223533462281438_1752200612928', #冯改'action': 'login', #冯改'_': '1752200612929' } session.headers.update({ 'Referer': 'https://www.fifedu.com/iplat/fifLogin/scuai/index.html?service=https://assess.fifedu.com/testcenter/home/teacher_index', #冯改'Referer': 'https://www.fifedu.com/iplat/fifLogin/scuai/index.html?service=https://ai.fifedu.com&loginT=&execution=e10s1', 'Accept': '*/*', 'Accept-encoding': 'gzip, deflate, br, zstd', 'Accept-Language': 'zh-CN,zh;q=0.9', 'cache-control': 'no-cache', 'pragma': 'no-cache', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36' }) url3 = "https://www.fifedu.com/iplat/ssoservice" response3 = session.get( url3, params=params3, allow_redirects=True ) if not response3: return False # 第四步:跳到目标页面 params4 = { 'nextPage': 'https://assess.fifedu.com/testcenter/home/teacher_index', #冯改'nextPage': 'https://ai.fifedu.com', #冯改'loginT': '', #冯改'execution': 'e10s1' } url4 = "https://www.fifedu.com/iplat/ssoservice" existing_cookies = session.cookies.get_dict() cookie_parts = [ f'SESSION={existing_cookies.get("SESSION", "")}', f'_wafuid={existing_cookies.get("_wafuid", "")}', f'prod-token={token}' #这里在线考试里面还有Hm_lpvt_5734acaafaf7a1011672a1fef0933656,Hm_lvt_5734acaafaf7a1011672a1fef0933656,HMACCOUNT三个值 ] cookie_str = '; '.join(filter(None, cookie_parts)) session.headers.update({ 'Referer': 'https://www.fifedu.com/iplat/fifLogin/scuai/index.html?service=https://assess.fifedu.com/testcenter/home/teacher_index', #冯改'Referer': 'https://www.fifedu.com/iplat/fifLogin/scuai/index.html?service=https://ai.fifedu.com&loginT=&execution=e10s1', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'Accept-encoding': 'gzip, deflate, br, zstd', 'Accept-Language': 'zh-CN,zh;q=0.9', 'cache-control': 'no-cache', #冯加 'pragma': 'no-cache', #冯加 #冯改'Connection': 'keep-alive', 'priority': 'u=0, i', 'Upgrade-Insecure-Requests': '1', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36', #冯加 'Cookie': cookie_str }) response4 = make_request(url4, params=params4, method='get', session=session) if not response4: return False processor = HttpResponseProcessor(url4, headers=session.headers) processor.response = response4 processor.raw_content = response4.content processor.status_code = response4.status_code text_content = processor.decode_content() # 第五步:跳到业务接口 url5 = "https://assess.fifedu.com/testcenter/home/getUser" #冯改url5 = "https://ai.fifedu.com/gestApp/api/user/getUserInfo" session.headers.update({ 'Referer': 'https://assess.fifedu.com/testcenter/home/teacher_index', #冯改'Referer': 'https://ai.fifedu.com/home', 'Accept': '*/*', #冯改'Accept': 'application/json, text/plain, */*', 'Accept-encoding': 'gzip, deflate, br, zstd', 'Accept-Language': 'zh-CN,zh;q=0.9', #冯改'Connection': 'keep-alive', 'priority': 'u=0, i', #冯改'Upgrade-Insecure-Requests': '1', 'Cookie': f'prod-token={token}', #冯改'Content-Type': 'application/json' 'cache-control': 'no-cache', #冯加 'pragma': 'no-cache', #冯加 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36' #冯加 }) response5 = make_request(url5, method='get', session=session) if not response5: return False processor = HttpResponseProcessor(url5, headers=session.headers) processor.response = response5 processor.raw_content = response5.content processor.status_code = response5.status_code text_content = processor.decode_content() sess = session.cookies.get_dict() if token and sess: return token, sess else: return False, False except Exception as e: return False def get_credentials(): username = "jffwbc1" password = "R2pjcHgxMjMhQCM=" return cas_login(username, password) if __name__ == '__main__': username = "jffwbc1" password = "R2pjcHgxMjMhQCM=" token, sess = cas_login(username, password)
08-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值