WP 内容矩阵采集系统:利用 PHP 协程驱动 Gemini API 实现海量 SEO 长文的自动化重构

各位老铁,把手机调成静音,把零食放一边,今天咱们不聊那些虚头巴脑的框架,也不讲什么设计模式八股文。咱们来聊聊怎么用 PHP 这门“上古神兽”去驾驭 AI 的风潮,搞一个能自动洗稿、自动生成、自动发文章的“内容矩阵核弹”。

听好了,这可不是那种每天让你手动复制粘贴的脚本,这是一个基于PHP 协程 + Google Gemini API + WordPress 构建的自动化工业化流水线。

准备好了吗?咱们开始“炸”服务器。


第一部分:为什么你要搞这个?(打破你的阿Q精神)

在座的各位,可能都经历过这种绝望:老板说,我们需要 1000 篇关于“减肥餐”或者“区块链”的长文。你一咬牙一跺脚,开始写。

第一天,你是神,键盘敲得飞起,觉得自己是鲁迅转世。
第三天,你觉得自己是便秘患者,键盘敲得火星四溅。
第七天,你看着屏幕上的“由于篇幅过长,此处省略一万字”,直接把电脑砸了。

手动写文章是反人类的,就像让大猩猩弹钢琴。但是,SEO 需要海量内容。怎么办?去买数据?去抄袭?那是要封号的,而且低级。现在的玩法是:采集 -> 重写 -> 再采集 -> 再重写

这就需要一台“永动机”。这台机器的核心,就是协程

第二部分:为什么是 PHP?为什么是协程?

你可能会问,Go 语言不是协程之王吗?为什么不用 Go?

Go 语言适合写后端服务,但在 PHP 的世界里,你已经有 WordPress 了。你想把这些文章发回你的 WP 网站,继续用 PHP 是最顺滑的。而且,PHP 8 的性能已经脱胎换骨,配合 Swoole 或 OpenSwoole,它能跑出惊人的 I/O 密集型吞吐量。

什么是协程?
简单说,传统的 PHP 是“打电话”模式:你拨通客服,挂了等回电。这期间你啥也干不了,得干等。
协程是“面谈”模式: 你和客服面谈,他说“稍等我去查个档”,然后你转头去帮别人倒杯水,聊两句。等他查完了,自然叫你。

这就是非阻塞 I/O。对于我们要疯狂调用 Gemini API 和爬虫抓取页面来说,这种能力简直是救命稻草。

第三部分:系统架构概览

我们的系统长这样:

  1. 数据源采集层(爬虫): 利用 PHP 协程并发抓取 100 个不同的网站,提取文章标题和正文。
  2. 语义重组层(LLM): 把抓来的文章喂给 Gemini,让它用 100 种不同的语气、句式重新写一遍。
  3. 落地方案(WP-API): 把生成的文章通过 WordPress 的 REST API 批量推送到你的站点。
  4. 任务调度器(大脑): 管理进度、限流、重试、去重。

第四部分:核心技术 —— PHP 协程实战

咱们先不谈脑子(LLM),先让 PHP 的手脚动起来。

Swoole 的协程 HTTP 客户端是神器。你可以想象一下,如果你用传统的 curl_multi 或者多进程,管理起来头都大了。但在 Swoole 里,写同步代码就能得到异步结果。

来看看这段代码,感受一下什么是“赛博朋克写法”:

<?php

require_once __DIR__ . '/vendor/autoload.php';

use SwooleCoroutine;
use SwooleCoroutineHttpClient;

/**
 * 并发采集 10 个网页内容
 */
function fetchMultipleUrls(array $urls)
{
    // 使用 Co::create 或直接在协程函数中写
    // 这里的关键点:我们并不等待每一个 curl 完成,而是让它们同时跑
    $tasks = [];
    foreach ($urls as $index => $url) {
        $tasks[] = Coroutine::create(function () use ($url, $index) {
            $client = new Client(parse_url($url, PHP_URL_HOST), 443, true);

            // 协程的魔法:这里的 yield 实际上把 CPU 释放回调度器
            // 但代码写起来像同步一样
            $client->get(parse_url($url, PHP_URL_PATH));

            if ($client->statusCode === 200) {
                // 这里拿到了网页内容,你可以直接解析 DOM
                return [
                    'url' => $url,
                    'content' => $client->body,
                    'title' => '抓取成功 ' . $index
                ];
            }
            return null;
        });
    }

    // 这里返回的是一个 Generator,协程其实已经在后台跑了
    foreach ($tasks as $task) {
        $result = $task->yield();
        if ($result) {
            echo "拿到了 " . $result['title'] . " 的内容长度: " . strlen($result['content']) . "n";
        }
    }
}

// 模拟数据
$urls = [
    'https://example.com/article/1',
    'https://example.com/article/2',
    'https://example.com/article/3',
    // ... 假设有 100 个
];

Coroutine::run(function () use ($urls) {
    fetchMultipleUrls($urls);
});

echo "所有任务执行完毕n";

看到了吗?没有复杂的回调地狱,没有 Promise 对象的纠结。就这么几行代码,10 个请求同时发出。如果你的网络快,可能 0.5 秒就抓完了 10 个网页。这就是协程的魅力。

第五部分:大脑 —— Gemini API 的调用艺术

现在我们有了数据,得让它变成新东西。Google 的 Gemini API 是目前最聪明的模型之一,比 GPT-4 便宜,比 GPT-3.5 聪明。

我们的目标是“语义重构”。不是简单的替换同义词(那是老黄历了,会被 Google 算法打脸),而是要改变段落结构,改变叙述角度。

1. 构建提示词

怎么让 AI 不写烂文?你得像训狗一样训它。

/**
 * 构建 Gemini 的请求体
 */
function buildGeminiPrompt($sourceText)
{
    return [
        'contents' => [
            [
                'parts' => [
                    [
                        'text' => <<<PROMPT
                        你是一位资深的内容架构师和 SEO 专家。你的任务是将以下提供的网页内容,改写为一篇高质量、原创性强的长文。

                        **核心指令:**
                        1. **彻底重组结构:** 不要保留原文的段落顺序。将关键点打乱,按照新的逻辑重新组织。
                        2. **同义转换与扩写:** 使用完全不同的词汇和句式。例如,不要把 "good" 写成 "nice",要用 "excellent" 或 "beneficial"。
                        3. **增加深度:** 在改写的同时,适当补充相关的背景信息或数据(如果源文本中有),使文章更丰满。
                        4. **排版优化:** 文章中必须包含:一个引人入胜的标题、清晰的小标题(H2)、以及适当的强调标记。

                        **源文本:**
                        {$sourceText}

                        请直接输出改写后的 HTML 格式文章。
                        PROMPT
                    ]
                ]
            ]
        ]
    ];
}

2. 协程调用 API

这里要注意,Gemini API 是有速率限制的。如果你在一个循环里疯狂调用,几秒钟就会遇到 429 Too Many Requests 的错误。这时候,协程的信号量 功能就派上用场了。

use SwooleCoroutineSemaphore;

function rewriteContentWithGemini(string $text)
{
    // 限制并发数为 5,避免被 Google 封 IP
    $semaphore = new Semaphore(5); 

    return Coroutine::create(function () use ($text, $semaphore) {
        $semaphore->lock();

        $client = new SwooleHttpClient('generativelanguage.googleapis.com', 443, true);

        // 替换 YOUR_API_KEY
        $apiUrl = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=YOUR_API_KEY";

        $data = buildGeminiPrompt($text);

        $client->post($apiUrl, json_encode($data), function ($client) {
            if ($client->statusCode === 200) {
                $response = json_decode($client->body, true);
                if (isset($response['candidates'][0]['content']['parts'][0]['text'])) {
                    return $response['candidates'][0]['content']['parts'][0]['text'];
                }
            }
            throw new Exception("API 调用失败: " . $client->body);
        });

        // 关键:等待协程结束
        Coroutine::join($client->task);

        $semaphore->unlock();
    })->yield();
}

第六部分:矩阵化策略 —— 怎么避免内容雷同

你可能会问,如果我有 1000 个源 URL,全部喂给同一个 Prompt,出来的文章是不是都一个味儿?

这正是我们需要解决的“同质化”问题。我们需要给每个任务分配不同的角色

角色分配矩阵:

  1. 权威专家: 强调数据、深度分析、专业术语。
  2. 新手小白: 强调通俗、易懂、使用反问句、讲段子。
  3. 养生博主: 强调健康、注意事项、情感共鸣。
  4. 硬核极客: 强调代码、底层原理、速度。

在代码里,我们定义一个配置文件:

$personas = [
    'expert' => "你是一个行业专家,用数据说话,语气严肃。",
    'casual' => "你是一个热爱分享的朋友,语气轻松,多使用感叹号。",
    'storyteller' => "你是一个讲故事的人,把枯燥的技术转化为跌宕起伏的故事。",
    'salesman' => "你是一个推销员,强调产品的好处,诱导用户点击。"
];

function getPromptWithPersona($text, $personaKey)
{
    $persona = $personas[$personaKey];
    // ... 在构建 Prompt 时,将 $persona 插入到指令中
    return <<<TEXT
    {$persona}
    重写以下内容:
    {$text}
    TEXT;
}

这样,当你循环抓取文章时,随机打乱 $personas 数组,或者按照特定顺序分配,生成的 1000 篇文章虽然讲的是同一个源素材,但读起来就像 1000 个人在说话。

第七部分:落地 WordPress —— 这里的坑比数据还深

有了文章内容,怎么存进 WP?直接 SQL 插入?太丑陋,而且容易报错。

用 WordPress 的 REST API。这是最优雅的方式。

1. 认证

你需要一个 Application Password。在 WP 后台 -> 用户 -> 应用密码,创建一个。

function createPostInWordPress($title, $content, $categoryIds = [])
{
    $client = new SwooleHttpClient('你的站点域名', 443, true);

    $payload = [
        'title'    => $title,
        'content'  => $content, // 假设 Gemini 返回的是纯文本或 HTML
        'status'   => 'publish', // 或者 'pending' 稍后审核
        'categories' => $categoryIds
    ];

    $client->post('/wp-json/wp/v2/posts', json_encode($payload), function ($client) {
        if ($client->statusCode === 201) {
            $data = json_decode($client->body, true);
            echo "发布成功!Post ID: " . $data['id'] . "n";
            return $data['link'];
        } else {
            echo "发布失败: " . $client->body . "n";
        }
    });

    Coroutine::join($client->task);
}

2. 图片处理

文章里通常需要配图。你可以让 Gemini 返回图片的 URL,或者自己写个爬虫去 Unsplash/Pexels 搜关键词。

这里有个技巧:CDN 加速。不要把图片存到 WP 的 uploads 目录,直接把图片 URL 放进去,WP 会自动从远程拉取并缓存。

第八部分:全链路整合 —— 炸裂的主循环

现在,把上面的所有零件拼起来。我们要做一个 Master Worker 模式。

虽然 Swoole 的单进程协程已经很快了,但如果你想跑 10 万篇文章,你需要多进程。

use SwooleProcess;
use SwooleProcessPool;

$pool = new Pool(4); // 开启 4 个 Worker 进程

$pool->on('workerStart', function ($pool, $workerId) {
    echo "Worker #{$workerId} 已启动n";

    // 每个 Worker 进程负责一部分 URL
    $myUrls = array_slice($allUrls, $workerId, 10);

    foreach ($myUrls as $url) {
        try {
            // 1. 抓取
            $content = fetchContent($url);

            // 2. LLM 重写 (带限流)
            $rewritten = rewriteContentWithGemini($content);

            // 3. 存入 WP
            createPostInWordPress($rewritten['title'], $rewritten['content']);

            // 4. 记录日志
            logToDB($url, 'success');

        } catch (Exception $e) {
            logToDB($url, 'failed', $e->getMessage());
        }
    }
});

$pool->start();

第九部分:避坑指南与运维

代码写得再漂亮,跑起来也是一地鸡毛。这里有三个必须要知道的“坑”。

1. 内存泄漏

PHP 的垃圾回收(GC)虽然强大,但如果你在协程里创建了大量对象且不释放,Swoole 进程可能会内存溢出(OOM)。
对策: 及时 unset 变量。不要把整个 HTML 文档存到数组里,抓取到什么处理什么。

2. DNS 解析慢

在 PHP 中使用协程 HTTP 客户端,默认不会缓存 DNS。如果你有 1000 个请求,意味着要解析 1000 次 DNS。
对策:

// 开启 Swoole 的全局 DNS 缓存
Co::set(['dns_cache_expire' => 600]);
// 或者手动缓存

3. AI 的幻觉

Gemini 有时候会一本正经地胡说八道,编造不存在的数据。
对策: 在 Prompt 里加一条铁律:“如果源文本中没有某个数据,请不要编造,请使用通用的描述性语言代替。” 并且,写完文章后,自己做一个简单的正则检查,确保关键实体没有跑偏。

第十部分:终极形态 —— 自动化闭环

当你把这个系统跑通后,你拥有的不仅仅是一堆文章,而是一个内容农场

你可以进一步优化:

  • A/B 测试 Prompt: 把生成的文章发到推特或测试域名,看哪个流量高,然后自动调整 Prompt。
  • 去重算法: 在存入 WP 之前,先调用一个简单的相似度算法(比如 Edit Distance 或 Jaccard 相似度),如果和库里文章太像,就扔掉。

结束语:代码是冷冰冰的,但数据是热的

写到这里,咱们今天的讲座也该收尾了。

这套系统,利用了 PHP 协程的高并发特性解决了“时间”问题,利用了 Gemini 的大脑解决了“质量”问题,利用了 WordPress 解决了“落地”问题。

它不是在偷懒,它是在榨取互联网的价值。它把原本需要人类花费一个月的重复劳动,压缩到了几分钟。人类不应该被束缚在键盘上,人类应该去思考如何优化算法,如何调整 Prompt,如何设计更好的架构。

所以,别再纠结写不写得出来那 1000 篇文章了。去装个 Swoole,去申请个 API Key,让你的 PHP 服务器动起来。

这就是技术,这就是生产力。散会!

(注:以上代码仅为演示逻辑,实际生产环境需增加重试机制、异常捕获、日志记录及代理 IP 池,切勿用于非法用途。)

发表回复

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