如何批量替换 t.co 短链接?一个脚本自动还原 Twitter 真实网址


🔗 效率工具

如何批量替换 t.co 短链接?
一个脚本自动还原 Twitter 真实网址

收藏资源列表时最烦的事:全是 t.co/xxxxxxx,根本不知道链接去哪儿 😩

⏱ 5 分钟搞定环境
🤖 全自动批量处理
💻 Windows / Mac 通用

为什么需要这个脚本?

从推特、微博转载来的资源合集,里面的链接往往长这样:

网址:https://t.co/hQ1t70vDZB
网址:https://t.co/sFhwjvWIPO
网址:http://t.co/XIG6CBbQUX
…(还有 99 个)

这种 t.co 短链接是 Twitter/X 的跳转链,点开才能知道真实地址。如果你想:

  • 把资源列表整理发布到自己网站
  • 分享给不方便翻墙的朋友
  • 存档备份,防止链接失效看不出原网址

一个一个点开再复制,100 条链接要花多少时间?用这个脚本,2 分钟全部搞定。

✅ 实际运行效果

处理前

https://t.co/TAWzrsgliF

处理后

https://zoom.earth/

102 条链接,全部自动替换,输出一个干净的文本文件 🎉

原理是什么?

t.co 链接会拦截普通的程序请求(返回 403 错误),但真实浏览器访问时会自动跳转到真实地址。

这个脚本用的是 Puppeteer——一个可以用代码控制 Chrome 浏览器的工具。它会在后台悄悄打开浏览器,逐个访问短链接,记录跳转后的真实网址,然后批量替换你文本里的所有短链。

💡
不需要懂编程,把脚本下载下来,按步骤操作就行。整个过程就像”让电脑帮你一个一个点链接”。

操作步骤

1

安装 Node.js

Node.js 是运行这个脚本必须的环境,免费安装。

  1. 打开 nodejs.org,点击绿色的「LTS」版本下载
  2. 双击安装包,一路点「下一步」
  3. 打开命令行(Windows 搜索「cmd」),输入 node -v,出现版本号说明成功

2

下载脚本,准备文件

新建一个文件夹(比如叫「链接替换」),把脚本文件放进去,再建一个 input.txt,把你要处理的文本粘贴进去保存。

链接替换/
  ├── resolve-tco.js   ← 脚本文件
  └── input.txt       ← 你的原始文本

3

安装依赖并运行

在文件夹里打开命令行,依次执行:

# 第一次运行需要执行(只需一次)
npm install puppeteer

# 每次处理新文本时执行
node resolve-tco.js input.txt output.txt

查看结果

脚本运行完毕后,文件夹里会多出两个文件:
📄 output.txt — 已替换完成的正文,直接复制使用
📋 output_map.json — 所有短链与真实地址的对照表,方便核查

常见问题

提示「Cannot find module」错误?

说明命令行当前不在脚本所在的文件夹。用 cd 命令切换到正确目录,比如 cd D:\链接替换,再重新运行。

某些链接显示 ❌ 解析失败?

原推文可能已被删除,或账号已注销。脚本会自动保留这些失效短链不做修改,其余正常链接不受影响。

链接很多,会不会很慢?

每条链接间隔约 0.6 秒,100 条大概需要 1~2 分钟。脚本会实时显示进度,放着等它跑完就行。

Mac 上能用吗?

完全可以,Node.js 和 Puppeteer 跨平台,Windows / Mac / Linux 都支持。Mac 上用「终端」app 代替命令提示符即可。

📦

获取脚本文件

脚本完整代码见下方,复制保存为 resolve-tco.js 即可使用

/**
 * resolve-tco.js
 * 用法:node resolve-tco.js input.txt output.txt
 */
/**
* resolve-tco.js
* 用法:node resolve-tco.js input.txt output.txt
* 自动提取文本中所有 t.co 短链接,用 Puppeteer 解析真实 URL,替换后输出
*/

const puppeteer = require('puppeteer');
const fs = require('fs');

// ── 参数处理 ──────────────────────────────────────────────
const [,, inputFile, outputFile] = process.argv;
if (!inputFile || !outputFile) {
console.error('用法:node resolve-tco.js <输入文件> <输出文件>');
process.exit(1);
}

const content = fs.readFileSync(inputFile, 'utf-8');

// 提取所有 t.co 短链接(去重)
const TCO_RE = /https?:\/\/t\.co\/[A-Za-z0-9]+/g;
const shortUrls = […new Set(content.match(TCO_RE) || [])];

if (shortUrls.length === 0) {
console.log('未找到任何 t.co 链接,直接复制文件。');
fs.writeFileSync(outputFile, content, 'utf-8');
process.exit(0);
}

console.log(`共找到 ${shortUrls.length} 个不重复的 t.co 链接,开始解析…\n`);

// ── 核心解析逻辑 ──────────────────────────────────────────
async function resolveAll(urls) {
const browser = await puppeteer.launch({
headless: 'new',
args: ['–no-sandbox', '–disable-dev-shm-usage'],
});
const page = await browser.newPage();

// 伪装成正常浏览器,避免 403
await page.setUserAgent(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
'AppleWebKit/537.36 (KHTML, like Gecko) ' +
'Chrome/124.0.0.0 Safari/537.36'
);

// 屏蔽图片/字体加速访问
await page.setRequestInterception(true);
page.on('request', req => {
if (['image', 'font', 'stylesheet'].includes(req.resourceType())) {
req.abort();
} else {
req.continue();
}
});

const mapping = {};

for (let i = 0; i < urls.length; i++) {
const url = urls[i];
process.stdout.write(`[${i + 1}/${urls.length}] ${url} → `);
try {
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 12000 });
const resolved = page.url();
mapping[url] = resolved;
console.log(resolved);
} catch (e) {
mapping[url] = null; // 解析失败保留原链接
console.log(`❌ 失败 (${e.message.split('\n')[0]})`);
}
// 适当间隔,避免被限速
await new Promise(r => setTimeout(r, 600));
}

await browser.close();
return mapping;
}

// ── 主流程 ────────────────────────────────────────────────
(async () => {
const mapping = await resolveAll(shortUrls);

// 统计
const success = Object.values(mapping).filter(Boolean).length;
const failed = shortUrls.length – success;
console.log(`\n解析完成:成功 ${success} 个,失败 ${failed} 个`);

// 保存映射表(方便复查)
const mapFile = outputFile.replace(/(\.\w+)?$/, '_map.json');
fs.writeFileSync(mapFile, JSON.stringify(mapping, null, 2), 'utf-8');
console.log(`映射表已保存:${mapFile}`);

// 替换文本
let result = content;
for (const [short, real] of Object.entries(mapping)) {
if (real) result = result.replaceAll(short, real);
}

fs.writeFileSync(outputFile, result, 'utf-8');
console.log(`替换完成,输出文件:${outputFile}`);
})();

小结

整个流程只需要做一次环境搭建,之后每次处理新的链接列表只需要:

📝
粘贴文本
保存为 input.txt

运行脚本
等待 1~2 分钟

拿走结果
output.txt 直接用

如果这篇教程帮到了你,欢迎收藏分享~有问题可以在评论区留言。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部