各位好,欢迎来到今天的“代码即魔法”专场。今天我们不聊虚无缥缈的架构图,也不整那些听着像在念经的 996 话术,我们直接上干货——如何用 PHP 这个“老古董”配上 n8n 这个“自动化猛兽”,把你的 50万+ 文章库变成自动喂给 TikTok 和 YouTube 的饲料。
听我说,很多人看到 50万篇文章,第一反应是头皮发麻,第二反应是想找根绳子把自己挂在服务器机架旁。但今天,我们要把这个庞然大物变成一只听话的绵羊,通过自动化工作流,让它主动走到 TikTok 和 YouTube 的舞台中央跳舞。
准备好了吗?我们要开始“肢解”这个工程了。
第一部分:PHP 不是老古董,它是数据库里的“老司机”
首先,咱们得正视一下 PHP。别老是用那种“PHP 已死”的陈词滥调来嘲讽它。PHP 在处理 HTTP 请求、连接数据库、序列化数据方面,依然是效率界的王者。特别是当我们面对海量数据时,PHP 的单进程模型在处理 I/O 密集型任务(比如读取 50万 行数据)时,配合适当的技巧,简直是神级操作。
我们的核心逻辑很简单:数据库拿数据 -> PHP 打包成 JSON -> 丢给 n8n -> n8n 分发给平台。
1. 别做“全表扫描”的傻瓜
很多新手写 PHP,上来就是 SELECT * FROM articles。朋友,你这是在炸服务器,不是在写代码。面对 50万条数据,你要做的是“分批取餐”。
我们要利用 LIMIT 和 OFFSET,或者更优雅的基于时间戳/ID 的查询。我推荐使用基于 ID 的游标方式,既快又不会锁表。
<?php
// database_config.php 已经配置好了 PDO 连接
// 这段代码是我们自动化管道的“取票口”
require_once 'database_config.php';
// 一次取 50 篇,别贪多,贪多嚼不烂,而且容易触发 API 限流
$batchSize = 50;
$processedIds = []; // 记录已经成功处理过的 ID,防止重复
try {
// SQL 查询:只找那些还没发过视频的,或者需要重新发布的文章
// 注意:为了演示,我假设有个 'status' 字段,这里用 OR 逻辑
$stmt = $pdo->prepare("
SELECT id, title, content, slug, created_at, cover_image
FROM articles
WHERE status IN ('draft', 'scheduled')
ORDER BY id ASC
LIMIT :limit
");
$stmt->bindValue(':limit', $batchSize, PDO::PARAM_INT);
$stmt->execute();
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!$articles) {
echo "今天已经跑完了,或者没有待发布的文章,PHP 睡觉去了。n";
exit;
}
// 关键一步:把 PHP 的数组转成 JSON。n8n 就爱吃这个。
// 为了让 n8n 识别,我们加个元数据标记
$payload = [
'meta' => [
'source' => 'php_legacy_backend',
'timestamp' => time(),
'total_articles' => count($articles)
],
'data' => $articles
];
// 设置响应头,告诉 n8n “我给你发 JSON 了”
header('Content-Type: application/json');
// 输出 JSON
echo json_encode($payload, JSON_UNESCAPED_UNICODE);
} catch (PDOException $e) {
// 别在日志里裸奔,至少记录一下错误
error_log("Database Error: " . $e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Database connection failed']);
}
?>
这段代码的意义在于:它像是一个哨兵,每天定好闹钟(或者让 n8n 定时触发),醒来只抓 50 个还没被“喂”过的文章,打包成 JSON 喂给 n8n。多么优雅。
第二部分:n8n,你的“数字泰勒·斯威夫特”
n8n 是什么?它是连接一切的胶水,是自动化界的瑞士军刀。对于我们这个场景,n8n 就相当于那个拿着大喇叭的指挥家。
1. 第一步:Webhook 节点 —— 接吻的开始
我们需要 PHP 把 JSON 丢给 n8n,怎么丢?用 HTTP。n8n 提供一个免费的 Webhook URL。
在 n8n 里,拖入一个 Webhook 节点。
- Method: POST
- Path:
/trigger-video-batch(随便起个名字,记住它) - Response Mode: On Response (这样 PHP 脚本执行完就能得到 n8n 的回复,虽然我们主要用 HTTP 请求主动拉取)
2. 第二步:HTTP Request 节点 —— PHP 的“求爱信”
现在,我们要在 n8n 里写一个 HTTP Request 节点,让 n8n 去问 PHP 要数据。
- Method: GET
- URL:
http://your-php-server.com/get_batch.php - Authentication: None (如果是本地测试) 或者 Basic Auth (如果是远程服务)
点击“Execute Node”,你应该能看到 PHP 返回的 JSON 数据,里面躺着你的 50 篇文章。
第三部分:内容清洗与格式化 —— 让文章变得“上镜”
TikTok 和 YouTube 不会读小说,它们需要短小精悍的钩子。我们不能直接把 50万字的博客扔进去。我们需要在 n8n 里做个简单的“格式化大师”。
1. Split in Batches 节点
虽然我们一次抓了 50 篇,但为了保证每个视频都能成功上传,通常我们会把这 50 篇再切分成 5 批,每批 10 篇,或者每篇视频处理一次。为了演示简单,我们保持 50 篇不动,直接处理。
2. Function 节点 —— 你的 JS 小手
这是 n8n 最强大的地方之一。我们用一个 Function 节点来处理数据。
目标: 从文章标题和内容中截取前 100 个字作为视频描述。
// n8n Function Node Code
const items = this.getInputData();
return items.map(item => {
// item.json 包含了 PHP 返回的 JSON 数据结构
// 我们直接访问 item.json.data[0] 中的内容
const article = item.json.data[0];
// 简单的字符串处理,模拟生成“钩子”
// 实际生产中,这里可以接 OpenAI API 让 AI 写脚本
const title = article.title;
const content = article.content;
// 截取前 100 字
const preview = content.length > 100 ? content.substring(0, 100) + '...' : content;
// 构造适合 TikTok/YouTube 的新对象
return {
json: {
video_id: article.id,
title: title,
description: `🔥 这里有个关于 ${title} 的精彩内容!nn${preview}nn#Automation #Tech #PHP #Life`,
thumbnail_url: article.cover_image || "https://via.placeholder.com/1280x720.jpg?text=No+Cover",
tags: ["Automation", "Tech", "PHP", "n8n"],
// 确保数据类型正确,API 通常不喜欢 PHP 的空字符串变成 null
status: "ready"
}
};
});
这段代码运行后,你的数据就变成了 n8n 看得懂的格式:标题、描述、缩略图。是不是很爽?
第四部分:YouTube —— 庞大系统的噩梦与解决方案
YouTube 是个傲娇的家伙。它有个规矩:每个 Google 账号,最多只能同时上传 15 个视频,而且每天只能上传 50 个视频。 这对于 50万篇文章来说,简直是杯水车薪。
1. YouTube Data API v3 的接入
在 n8n 里,你要安装 YouTube 扩展(Node)。你需要一个 Google Cloud Project,开启 API,拿到 Client ID 和 Client Secret。
2. Loop over Items 节点 —— 逐个击破
因为 YouTube 限制太严,我们不能一次性扔 50 篇进去。在 Function 节点后面,我们要拖入一个 Loop over Items 节点。
这样,n8n 就会拿到你的 50 篇文章,然后循环执行以下操作 50 次。
3. YouTube Upload 节点配置
在 Loop 里面,放一个 YouTube Upload 节点。
- Content Type: Video (或者 Audio,取决于你的文章怎么处理)
- Privacy: Public (根据需求)
- Video Title:
{{ $json.title }} - Video Description:
{{ $json.description }} - Thumbnail URL:
{{ $json.thumbnail_url }}
注意: 实际上,YouTube API 对视频文件的格式有要求(MP4, WebM 等)。如果你的文章只是文字,你可能需要:
- 使用开源工具(如
ffmpeg)把文章文字合成一个简单的视频(比如字幕滚动视频),或者 - 调用 AI 视频生成 API(Runway, Pika 等)把图片转成视频,然后把视频 URL 传给 YouTube。
为了简化,我们假设你有一个脚本,能根据文章内容生成一个视频文件 video.mp4,并且 n8n 能访问这个文件。
这时候,HTTP Request 节点就不止是读数据了,它要写数据。
4. HTTP Request 节点 —— 批量上传
在 Loop 内部,使用 HTTP Request 节点。
- Method: POST
- Authentication: OAuth2 (选择 “Upload Media” 模式)
- URL:
https://www.googleapis.com/upload/youtube/v3/videos?uploadType=multipart&part=snippet,status - Body:
{ "snippet": { "title": "这里是标题", "description": "这里是描述", "tags": ["tag1", "tag2"], "categoryId": "22" // Technology }, "status": { "privacyStatus": "public" } } - File: 选择你的本地视频文件路径。
当这个节点执行成功,YouTube 会返回 videoId。这时候,我们需要把这个 videoId 发回给 PHP 数据库,标记这篇文章“已经上传到 YouTube”。
第五部分:TikTok —— 那个疯狂的舞池
TikTok 呢?TikTok 更有意思。TikTok 官方的 API 对于批量上传是非常保守的。标准的 Creator API 主要用于发布笔记(图文),或者编辑视频。
如果你的目标是批量上传视频到 TikTok,通常有两种路径:
路径 A:使用 TikTok 广告 API (适用于企业账号)
这个比较硬核,需要企业认证,而且需要处理复杂的 OAuth 流程。n8n 支持得很不错,通过 HTTP Request 节点调用 TikTok API。
路径 B:模拟“创作者中心”操作 (适用于个人/小号)
这比较“野路子”,但也最常见。
n8n 可以利用 Puppeteer 节点或者 Crawlee 节点,模拟人类在 TikTok 创作者中心上传视频的操作。虽然这不稳定,但如果 TikTok 允许批量上传(实际上 API 对批量上传限制极严),这或许是唯一出路。
这里我们演示最“正规”的思路:通过 API 尝试上传(注:实际操作中需查阅最新官方文档,因为 TikTok API 经常变):
- Auth: 使用 OAuth2 节点获取 TikTok Token。
- Media Upload: 使用 HTTP Request 节点上传视频文件。TikTok 的 multipart upload 逻辑和 YouTube 类似,但 Endpoint 不同。
- Publish: 获取 media_id 后,再调用 Publish 接口。
// n8n Function Node 伪代码:准备 TikTok Payload
const data = {
media_description: "这是自动化生成的视频描述",
media_category: "Entertainment", // TikTok 类别
// ... 其他参数
};
// n8n HTTP Request Node:
// POST https://open.tiktokapis.com/v2/media/upload/
// Headers: Authorization: "Bearer {{tiktok_token}}", Content-Type: "multipart/form-data"
// Body: 包含视频文件
专家提示: TikTok 对内容审核极其严格。如果你把所有文章都发去,大概率会被限流或者封号。建议使用 n8n 的 Wait 节点,在上传一个视频后,等待 30 分钟再上传下一个,模拟人工操作的节奏。
第六部分:闭环 —— 把状态传回 PHP
现在,我们的文章已经变成了视频,上传到了 YouTube 和 TikTok。
别忘了我们的 PHP 数据库里还有 50万 条数据。我们需要把它们的状态更新一下。
1. Update Article 节点
在 n8n 的工作流最后,我们加一个 HTTP Request 节点,把结果发回 PHP。
- URL:
http://your-php-server.com/update_status.php - Method: POST
- Body:
{ "video_id": 12345, // YouTube 返回的 ID "tiktok_post_id": "abc123", "article_id": 50001, "platform": "youtube" }
2. PHP 的“反馈处理”脚本
PHP 端的 update_status.php 要写得健壮一些。
<?php
$data = json_decode(file_get_contents('php://input'), true);
if ($data) {
$stmt = $pdo->prepare("UPDATE articles SET status = 'published_youtube', youtube_video_id = :vid, published_at = NOW() WHERE id = :id");
$stmt->execute([
':vid' => $data['video_id'],
':id' => $data['article_id']
]);
echo "Status updated for article " . $data['article_id'];
}
?>
第七部分:打造“不倒翁”系统 —— 错误处理与重试
你可能会问:“万一 n8n 上传到一半断了怎么办?我的 50万 文章岂不是白跑了?”
别慌,我们有 Error Trigger 节点和 Retry 机制。
1. Error Trigger
在 Loop 后面,把 On Error 的输出连到一个 If 节点。
2. 重试策略
在 HTTP Request 节点里,设置 Retry On Fail: Yes,重试 3 次。
3. 失败队列
如果实在不行,n8n 的数据会流向 On Error 分支。我们可以把这个失败的数据存入一个专门的 failed_uploads 数据库表,或者写入 Redis。第二天早上,我们手动点一下“重试失败任务”按钮,n8n 会从那个表里把剩下的文章重新拉出来跑一遍。
第八部分:进阶优化 —— 50万篇的哲学思考
做了这么多代码,我们来聊聊这背后的工程哲学。
- 管道化思维: 不要试图在 PHP 里直接调用 YouTube API。PHP 负责数据库,n8n 负责分发。解耦,是稳定性的基石。
- 幂等性: 无论是 PHP 查询未处理数据,还是 n8n 上传视频,都要保证“重跑不犯错”。比如 ID 唯一性约束,或者状态标记机制。
- 渐进式发布: 50万篇如果一天发完,流量会崩掉。利用 n8n 的 Schedule Trigger,设定每天只发 100 篇。这 100 篇我们可以通过 Filter 节点挑选出“高权重”或“最新”的文章。这叫“精选集”,比“垃圾堆”效果更好。
总结
你看,从 50万 篇死气沉沉的文章,到一个个鲜活跳动的视频,我们只用了:
- PHP 做个“分拣员”(筛选数据,打包 JSON)。
- n8n 做个“导演”(编排流程,处理数据)。
- YouTube/TikTok API 做个“舞台”(展示内容)。
这中间没有复杂的微服务架构,没有 Kubernetes 的过度设计,只有最纯粹的数据流转。这就是自动化最迷人的地方——让机器去干那些枯燥的搬运工作,让你有时间去喝咖啡,或者去思考下一个段子从哪来。
现在,去给你的 PHP 脚本加上 LIMIT,去 n8n 里把 Webhook 接通。当你看到你的服务器日志里开始疯狂打印 Executing Node...,当你看到 YouTube 频道里开始自动长出视频,你会发现,这比写任何复杂的算法都要爽。
好了,今天的讲座到此结束。如果代码跑不通,别怪我,怪 n8n 的文档太晦涩,或者怪 TikTok 的 API 没写好。Debug 的时候记得多喝点水,那是程序员的续命水。
祝你好运,让你的 50万 篇文章在 TikTok 上疯传吧!