document.cookie 设置有效期:max-age vs expires 与删除的坑

document.cookie 是一个字符串 API,"设置有效期"其实就是在 cookie 字符串里追加 expires 或 max-age 属性。 规则很简单:不写有效期 = 会话 cookie(浏览器关掉就没)、写了 = 持久化 cookie。 方式一:expires(老写法) 传一个 UTC 时间字符串: const d = new Date(); d.setTime(d.getTime() + 7 * 24 * 60 * 60 * 1000); // 7 天document.cookie = `token=abc; expires=${d.toUTCString()}; path=/`;注意必须是 toUTCString(),用 toString() 或 toISOString() 会挂。 方式二:max-age(现代推荐) 直接指定"多少秒后过期": document.cookie = `token=abc; max-age=${7 * 24 * 60 * 60}; path=/`;单位是秒。max-age 更直观,也不用管时区。老到 IE 那一档才要考虑 expires。 删除 cookie 本质就是"立刻过期": document.cookie = "token=; max-age=0; path=/"; // 或 document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";三个删不掉的坑 1. path 不一致 创建时用 path=/,删除也必须带上 path=/。不带就等于删的是当前路径下的 cookie,很可能和实际存放的 cookie 不是同一个。 2. domain 不一致 .example.com 和 sub.example.com 是两条不同的 cookie。删除时必须匹配写入时的 domain。 3. HttpOnly cookie 前端改不了 服务端设置了 HttpOnly 的 cookie,document.cookie 完全读不到、也删不掉。只能后端接口去清。 封装模板 前端项目里放一份省心: export function setCookie(name, value, days = 7, options = {}) { const d = new Date(); d.setTime(d.getTime() + days * 864e5); // 864e5 = 24*60*60*1000 const parts = [ `${name}=${encodeURIComponent(value)}`, `expires=${d.toUTCString()}`, `path=${options.path || "/"}`, ]; if (options.domain) parts.push(`domain=${options.domain}`); if (options.secure) parts.push("secure"); if (options.sameSite) parts.push(`samesite=${options.sameSite}`); document.cookie = parts.join("; "); }export function getCookie(name) { return document.cookie .split("; ") .find(row => row.startsWith(name + "=")) ?.split("=")[1]; }export function deleteCookie(name, options = {}) { setCookie(name, "", -1, options); // 复用同样的 path/domain }上生产别忘的属性 正经站点几乎必配的三个属性:Secure:只走 HTTPS SameSite=Lax(登录态用 Strict 更安全) HttpOnly(防 XSS 盗 token,只能后端 Set-Cookie 时写)前端能设的就 Secure 和 SameSite,HttpOnly 必须走服务端。 一句话总结 现代写法用 max-age、删除记得带 path=/、跨域跨子域记得对齐 domain。真安全靠 HttpOnly + Secure + SameSite,那部分归后端管。

document.cookie 有效期设置:expires 与 max-age 的区别和常见坑

document.cookie 通过在赋值字符串中附加 expires 或 max-age 属性来控制有效期。不写任何有效期,则默认为会话 Cookie,浏览器关闭后自动清除。 expires:指定过期时间点 const d = new Date(); d.setTime(d.getTime() + 7 * 24 * 60 * 60 * 1000); // 7天后document.cookie = "token=abc123; expires=" + d.toUTCString() + "; path=/";expires 的值必须是 UTC 格式时间字符串(toUTCString() 输出符合要求)。兼容性好,适合需要支持老浏览器的场景。 max-age:指定存活秒数 // 设置 7 天后过期 document.cookie = "token=abc123; max-age=" + (7 * 24 * 60 * 60) + "; path=/";// 设置 30 分钟后过期 document.cookie = "session=xyz; max-age=1800; path=/";max-age 单位是秒,语义更直观,是现代浏览器推荐用法。max-age 优先级高于 expires,两者同时存在时以 max-age 为准。 删除 Cookie 本质是将有效期设为过去: // 方法一:max-age=0 立即过期 document.cookie = "token=; max-age=0; path=/";// 方法二:expires 设为历史时间 document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";常见坑 path 不一致无法删除 设置时用了 path=/,删除时也必须带上相同的 path: // 错误:path 不匹配,删除失败 document.cookie = "token=; max-age=0";// 正确 document.cookie = "token=; max-age=0; path=/";domain 不一致无法删除 .example.com 和 sub.example.com 是不同 domain,设置与删除必须一致。 HttpOnly Cookie 前端无权操作 带 HttpOnly 标志的 Cookie 在 document.cookie 中不可见,只能由服务端通过 Set-Cookie 响应头设置和清除。 实用封装 function setCookie(name, value, days) { const d = new Date(); d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000); document.cookie = `${name}=${encodeURIComponent(value)}; expires=${d.toUTCString()}; path=/`; }function getCookie(name) { return document.cookie .split('; ') .find(row => row.startsWith(name + '=')) ?.split('=')[1]; }function deleteCookie(name) { document.cookie = `${name}=; max-age=0; path=/`; }使用示例: setCookie('token', 'abc123', 7); // 7天有效期 getCookie('token'); // 'abc123' deleteCookie('token');如果需要跨子域共享 Cookie,添加 domain=.example.com;HTTPS 环境下建议同时设置 Secure; SameSite=Strict。

Python 自动安装 Chrome 插件:策略强制 vs 开发者模式加载

写脚本自动帮机器装个 Chrome 插件——听着简单。Chrome 团队故意不让你静默装本地 crx(安全策略,不是技术问题)。想绕开有几条现实可行的路。 路线一:企业策略强制安装(Web Store 上架的插件) Chrome 支持通过 Windows 注册表 / macOS plist / Linux JSON 策略,指定"必须自动装某扩展"。这是 IT 部门批量部署的标准做法。 前提是扩展在 Chrome Web Store 上架——策略只接受 update URL 拉取,不认本地 crx。 Windows 注册表方式 import winregEXT_ID = "aapocclcgogkmnckokdopfmhonfmgoek" # 举例:Google Docs Offline UPDATE_URL = "https://clients2.google.com/service/update2/crx"path = r"SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist"key = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, path) winreg.SetValueEx(key, "1", 0, winreg.REG_SZ, f"{EXT_ID};{UPDATE_URL}") winreg.CloseKey(key)写完重启 Chrome,扩展会自动安装、无法卸载。 注意:需要管理员权限 ExtensionInstallForcelist 的编号("1"、"2" ...)要不重复 只支持 Web Store 上架的扩展;企业内部扩展需要自建 update.xml配套删除: import winreg key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_WRITE) winreg.DeleteValue(key, "1")路线二:Selenium 启动时加载本地插件 不做正式安装,只是启动 Chrome 时把插件目录加载进来——最适合自动化脚本、爬虫、批量测试。 加载解压后的目录(源码形式): from selenium import webdriver from selenium.webdriver.chrome.options import Optionsopts = Options() opts.add_argument(r"--load-extension=D:\extensions\my-plugin")driver = webdriver.Chrome(options=opts) driver.get("https://example.com")加载已打包的 crx: opts = Options() opts.add_extension(r"D:\extensions\my-plugin.crx")局限:不会真的"安装",关闭浏览器就没了 需要 Selenium 控制的 Chrome,不影响用户日常浏览器 Chrome 每次启动会有"以开发者模式使用未打包扩展"警告条路线三:Playwright 加载扩展 Playwright 加载扩展要用 persistent context,且必须用 Chromium(Chrome 分发版不能通过所有测试): from playwright.sync_api import sync_playwrightwith sync_playwright() as p: ctx = p.chromium.launch_persistent_context( user_data_dir="D:/tmp/pw-profile", headless=False, args=[ "--disable-extensions-except=D:\\extensions\\my-plugin", "--load-extension=D:\\extensions\\my-plugin", ], ) page = ctx.new_page() page.goto("https://example.com")不推荐的路线 UI 自动化拖 crx 到 chrome://extensions/:Chrome 会弹确认框、快捷键会变、chrome 版本升级后经常挂。别在这个方向上死磕。 改本地 Extensions 目录里的 Preferences JSON:Chrome 会检测校验和,第二次启动直接把你写进去的扩展禁用掉。 场景对应目的 用什么公司/网吧批量部署 Web Store 扩展 策略强制安装爬虫 / 自动化脚本用自己写的扩展 Selenium 加载需要长期挂着扩展,用户不能卸载 策略强制只是测试自己开发中的插件 Chrome 开发者模式手动一句话总结 要"真安装"就上企业策略(Web Store 扩展),要"临时加载"就 Selenium/Playwright --load-extension。Chrome 就是不让你静默装本地 crx,别硬撞。

油猴脚本虚拟文件输入 + FileReader 封装

在油猴脚本里读取本地文件,思路是:动态创建隐藏的 <input type="file">,触发点击,再用 FileReader 读取内容。 封装函数 /** * 选择文件并读取内容 * @param {Object} options * @param {string} [options.accept] - 文件类型限制,如 ".json,.txt" * @param {string} [options.readAs="text"] - text | dataURL | arrayBuffer * @param {string} [options.encoding="utf-8"] - 文本编码(仅 text 模式有效) * @returns {Promise<{file: File, content: any}>} */ function selectFileAndRead(options = {}) { const { accept = "", readAs = "text", encoding = "utf-8" } = options; return new Promise((resolve, reject) => { let input = document.getElementById("__file_input__"); if (!input) { input = document.createElement("input"); input.type = "file"; input.id = "__file_input__"; input.style.display = "none"; document.body.appendChild(input); } input.accept = accept; input.onchange = () => { const file = input.files[0]; if (!file) { reject(new Error("未选择文件")); return; } const reader = new FileReader(); reader.onload = (e) => resolve({ file, content: e.target.result }); reader.onerror = () => reject(new Error("读取失败")); switch (readAs) { case "dataURL": reader.readAsDataURL(file); break; case "arrayBuffer": reader.readAsArrayBuffer(file); break; default: reader.readAsText(file, encoding); } }; // 必须重置 value,否则选同一个文件不会触发 change 事件 input.value = ""; input.click(); }); }使用示例 读取文本文件 selectFileAndRead({ accept: ".txt,.json" }).then(({ file, content }) => { console.log(file.name, content); });读取 JSON 并解析 selectFileAndRead({ accept: ".json" }) .then(({ content }) => JSON.parse(content)) .then((data) => console.log("JSON 数据:", data)) .catch((err) => console.error(err));读取图片为 base64 selectFileAndRead({ accept: "image/*", readAs: "dataURL" }).then( ({ content }) => { const img = document.createElement("img"); img.src = content; document.body.appendChild(img); } );async/await 版 document.addEventListener("keydown", async (e) => { if (e.ctrlKey && e.key === "u") { try { const { file, content } = await selectFileAndRead({ accept: ".json" }); const config = JSON.parse(content); applyConfig(config); } catch (err) { console.error(err); } } });关键坑 浏览器安全限制:input.click() 必须在用户事件(点击、键盘)的同步调用链里触发,不能在 setTimeout 或 Promise 回调里调用,否则会被拦截。 同一文件无法重复选择:input.value = "" 在 click() 前必须执行,否则选同一个文件第二次不会触发 change 事件。 中文乱码:Windows 下生成的文件有时是 GBK 编码: selectFileAndRead({ encoding: "gbk" });DOM 污染:函数复用同一个 __file_input__ 元素,不会重复创建,避免 DOM 堆积导致的性能问题。 在油猴脚本中的完整示例 // ==UserScript== // @name 配置文件导入 // @namespace http://tampermonkey.net/ // @version 1.0 // @match https://example.com/* // @grant none // ==/UserScript==(function () { "use strict"; // 插入导入按钮 const btn = document.createElement("button"); btn.textContent = "导入配置"; btn.style.cssText = "position:fixed;top:10px;right:10px;z-index:9999"; document.body.appendChild(btn); btn.addEventListener("click", async () => { const { content } = await selectFileAndRead({ accept: ".json" }); const config = JSON.parse(content); console.log("已加载配置:", config); }); // ... selectFileAndRead 函数定义 ... })();

让 AI 改写文本不像 AI 写的:提示词分级 + 三个关键技巧

想让 AI 改写一段文本、又不显得像"AI 味"很重——是很多人写作、内容运营、简历润色时的常见需求。核心是改写策略大于提示词模板。列几个能直接用的分级模板,和真正拉开差距的三个技巧。 分级提示词模板 基础版(简单改写) 请对以下内容进行改写,在不改变原意的前提下: 1. 调整句式结构,避免重复表达 2. 使用更自然的中文表达 3. 避免明显的 AI 写作风格 4. 保持逻辑清晰原文: {{文本}}轻度改写用。 进阶版(降 AIGC 痕迹) 请对以下内容进行深度改写,使其更像人类自然写作: 1. 打乱原有句式结构,避免连续相似句型 2. 适当增加口语化或过渡表达 3. 减少"首先、其次、最后"等明显结构词 4. 避免套话、空话和模板化表达 5. 可以适当重组段落顺序,但保持原意不变原文: {{文本}}反 AI 检测版(效果最好) 请将以下文本改写为更接近真实人类写作风格的版本: - 避免过于工整、对称的句式 - 适当加入不完全对称的表达 - 控制句长变化,让长短句交替出现 - 减少抽象表达,增加具体描述 - 避免连续使用同一结构开头不要解释,只输出改写后的内容。原文: {{文本}}三个真正关键的技巧 只用提示词效果最多 60 分。剩下 40 分是策略: 1. 多轮改写 一次改写效果有限。让 AI 改完后,再来一轮: 上面这段还有些 AI 味,帮我再改一遍,重点: - 削减"这一点""这类""从而""进而"这类连接词 - 一个短句 + 一个长句相邻,别让所有句子都差不多长 - 举一个具体例子,别只讲道理多来 2-3 轮,AIGC 检测分数会明显下降。 2. 长短句交错 AI 写的句子长度很均匀——都是 20-30 字的长句。人写的天然有变化: ❌ AI 味: 本方法通过对现有算法的优化,能够显著提高数据处理效率, 从而降低系统运行成本,进而提升用户体验,具有重要意义。✅ 人写感: 方法很简单——把老算法改了改。快了,成本降了。用户体验也 上来了。明确要求 AI:"交替使用 8-15 字的短句和 25-40 字的长句"。 3. 加人为瑕疵 真实人写作会有一点点"不完美":偶尔口语化:说白了 其实吧 我觉得 不完全对称:不是每次都并列三点 具体描述:温度很高 → 40 度出头 偶尔的语气词:嗯 倒是 不过要求 AI:"适当加入口语表达和具体细节,避免每段结构都相同"。 AI 写作的典型特征清单 写完后自检——下面这些出现越多,AI 味越重:"首先...其次...最后..." "随着 XX 的发展 / 随着 XX 的进步" "具有重要意义 / 具有重要价值" "综上所述 / 综合来看" "在 XX 领域 / 在 XX 方面" "值得注意的是 / 需要指出的是" 每段都是"背景 - 论点 - 论据"完美三段 所有句子长度相近 用词过于书面(旨在 致力于 诚然 毋庸置疑)一个好的降 AI 味提示词,会明确禁掉这几类。 组合模板(推荐直接用) 请把下面这段改成更像人写的风格,具体要求:1. 完全禁用这些词:首先、其次、最后、综上所述、值得注意的是、 随着...的发展、具有重要意义2. 句子长度变化:连续三句里至少有一个 10 字以内的短句3. 具体化:如果原文说"很多"就换成大概数字,如果说"效果好"就 补一个例子4. 段落结构别都一样,第二段可以直接引入例子,第三段可以以问句 开头5. 保持原意,但可以调整段落顺序不要说明改了什么,直接给最终版本。原文: {{文本}}检测工具 想验证效果,用GPTZero、Originality.ai、Copyleaks 这类工具跑一下。中文的可以试试知网 AIGC 检测、万方。不同工具算法不同,一个通过不代表都通过 分数不是 0 就绝对安全——大多数工具认为 20% 以内是可接受的 检测本身也在进化,去年管用的技巧今年可能被识别注意事项学术论文:AI 降重不能解决抄袭问题。核心思路和论据仍是别人的,改写只是躲检测,学术规范上仍然算问题 商业内容:用 AI 生成的内容做营销要留证据链(提示词 + 原始输出),出问题能溯源 简历 / 求职:过度包装容易被面试官看出来,"人味"多了反而更真实一句话总结 降 AI 味 = 禁用套话 + 长短句交错 + 具体化 + 多轮改写。提示词模板只是起点,真正的效果在策略上。用检测工具验证,别指望改一次就 0%。