DedeCMS百度自动推送实战:宝塔+API完整教程

DedeCMS百度自动推送实战:宝塔+API完整教程

DedeCMS 没有官方百度推送插件,本文给出从数据库 join 到 baidu_push.php 全脚本、宝塔计划任务定时配置、推送结果监控、错误码排错的完整链路,让发布即推送在 DedeCMS 上落地。

更新 26 分钟阅读 5,504 阅读

保哥这两年帮人做织梦 DedeCMS 站点的 SEO 收尾工作,常被问到一个看似很基础但落地很麻烦的问题:怎么让百度第一时间抓到 DedeCMS 当天发布的新文章?答案不止是把 sitemap.xml 挂到搜索资源平台那么简单,真正能压缩百度收录时间的做法是把"普通收录 API 推送"接到 DedeCMS 的发布流程里,让每一篇文章一发布就被推送出去。本文把整条链路从原理、脚本、宝塔计划任务、监控、错误码到日常排错全部摊开来讲,是我自己在二十多个 DedeCMS 站点上反复跑过、稳定使用三年以上的方案。

为什么 DedeCMS 站点必须自己写推送脚本

百度对 DedeCMS 这类相对老旧的 CMS 没有任何官方插件,搜索资源平台里 WordPress 的"百度搜索推送管理"只能装在 WP 上,DedeCMS 用户只能选这三条路:

  1. 纯手工提交:每天打开站长后台,把当天链接粘到普通收录的"手动提交"框里。低于 50 篇还能忍,超过这个量就完全不现实。
  2. sitemap.xml 自动提交:把 sitemap 地址挂到普通收录的"sitemap"模块。优点是不用脚本,缺点是百度对 sitemap 的抓取节奏完全由它自己控制,热门站可能 30 分钟一次,冷门站可能 24 小时一次,对刚发布的内容并不及时。
  3. API 推送:拿到 token 之后,自己用 curl 把 URL POST 到 http://data.zz.baidu.com/urls。响应是即时的,被推送的链接会进入百度的优先抓取队列,平均抓取时间从 sitemap 的小时级压到分钟级。

三种方式可以叠加用,并不冲突。但要把"发布即推送"做出来,只有第三种能做到。下面整篇都是围绕第三条展开。

普通收录 API 的配额规则与备案差异

百度搜索资源平台对 API 推送有明确的每日配额。配额值不是固定的 10 万,它跟两个变量挂钩:

  • 站点权重等级:搜索资源平台后台"站点信息"里能看到一个隐藏字段叫"接口推送配额"。新站普遍是 5000~10000 条/日,做了半年以上、有自然流量进来的站点能到 20000~50000,权重 5+ 的优质站点上限是 100000。
  • 是否完成 ICP 备案:未备案站的配额会被压到正常值的 30%~50%,并且部分推送会直接被丢弃。这是在 2023 年百度内部一次反垃圾调整后开始严格执行的,做国内站不要心存侥幸。

实测数据:保哥手上一个 DedeCMS 旅游站,备案前每日配额显示 8000,实际能成功推送的不到 5000;备案下来之后第二周配额自动跳到 20000,全量都能推成功。所以"备案值不值"在这个语境下答案是肯定的。

DedeCMS 数据库里要查的三张表

DedeCMS 把一篇文章拆在三张表里存,要拿到完整的发布 URL 必须 join:

  • dede_archives:主表,存 id、typeid、title、pubdate、senddate 等通用字段。
  • dede_arctype:栏目表,存 id、typename、typedir、ispart 等。typedir 是关键,它决定 URL 路径前缀,可能含 {cmspath} 占位符。
  • dede_addonarticle:附属表,存正文 body、写法 redirecturl、自定义字段等,对推送来说一般用不到,但做"只推送有正文的文章"时需要它。

三张表里 typedir 的写法多半是 {cmspath}/news{cmspath}/product/zhuangbei{cmspath} 默认是空字符串(DedeCMS 系统参数 cfg_cmspath),但如果站点装在二级目录下例如 /cms,这个值就会是 /cms。脚本里必须把 {cmspath} 替换成实际值再拼接 URL,否则推过去的全是带占位符的非法链接。

能直接用的 baiduapi.php 完整脚本

我现在线上跑的版本经过几轮迭代,比网上流传的最简版多了三块:错误日志、推送结果落地、已推送去重。完整代码:

<?php
/**
 * DedeCMS 百度普通收录 API 自动推送脚本
 * 放置位置:网站根目录 /baidu_push.php
 * 调用方式:宝塔计划任务每天 23:50 访问该 URL
 *
 * 依赖:dede/include/common.inc.php
 */
require_once dirname(__FILE__) . '/include/common.inc.php';

// ========== 配置区 ==========
$siteUrl   = 'https://www.zhangwenbao.com';
$apiToken  = '替换为你自己的 token';
$apiUrl    = 'http://data.zz.baidu.com/urls?site=' . parse_url($siteUrl, PHP_URL_HOST) . '&token=' . $apiToken;
$logFile   = dirname(__FILE__) . '/data/baidu_push.log';
$pushedDb  = dirname(__FILE__) . '/data/baidu_pushed.txt';
$cmsPath   = $cfg_cmspath ? $cfg_cmspath : '';

// 取当天发布且尚未推送过的链接
$dayBegin = mktime(0, 0, 0);
$dayEnd   = mktime(23, 59, 59);

$query = "SELECT a.id, a.title, t.typedir, t.ispart
          FROM dede_archives a
          INNER JOIN dede_arctype t ON a.typeid = t.id
          WHERE a.arcrank = 0
            AND a.pubdate >= {$dayBegin}
            AND a.pubdate <= {$dayEnd}";

$dsql->Execute('push', $query);
$urls = [];
while ($row = $dsql->GetArray('push')) {
    $dir   = str_replace('{cmspath}', $cmsPath, $row['typedir']);
    $url   = $siteUrl . $dir . '/' . $row['id'] . '.html';
    $urls[] = $url;
}

// 去重:读取已推送清单
$pushed = file_exists($pushedDb)
    ? array_filter(array_map('trim', file($pushedDb)))
    : [];
$todo = array_values(array_diff($urls, $pushed));

if (empty($todo)) {
    file_put_contents($logFile, date('Y-m-d H:i:s') . " 无新链接需要推送\n", FILE_APPEND);
    exit('no new url');
}

// 调用百度推送接口
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL            => $apiUrl,
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 30,
    CURLOPT_POSTFIELDS     => implode("\n", $todo),
    CURLOPT_HTTPHEADER     => ['Content-Type: text/plain'],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr  = curl_error($ch);
curl_close($ch);

// 落地日志与已推送清单
$logLine = date('Y-m-d H:i:s')
    . " count=" . count($todo)
    . " http={$httpCode}"
    . " resp=" . trim($response)
    . ($curlErr ? " err={$curlErr}" : '')
    . "\n";
file_put_contents($logFile, $logLine, FILE_APPEND);

if ($httpCode == 200) {
    $result = json_decode($response, true);
    if (!empty($result['success'])) {
        file_put_contents($pushedDb, implode("\n", $todo) . "\n", FILE_APPEND);
        echo 'OK pushed ' . $result['success'] . " urls\n";
    } else {
        echo "FAIL " . $response . "\n";
    }
} else {
    echo "HTTP {$httpCode} error\n";
}

逐段拆解:脚本里每个细节都不是装饰

这段代码看起来很长,但每一行都对应一个曾经踩过的坑。

1. 用 DedeCMS 自己的 dsql 而不是新建 PDO

很多教程让你用 mysqli 或 PDO 直接连 DedeCMS 数据库,这在调试时方便,但在正式环境有两个问题:一是密码要硬编码到 baidu_push.php,泄露风险高;二是 DedeCMS 的 GBK/UTF-8 站点编码差异需要手动处理。直接 require include/common.inc.php 复用 DedeCMS 的 $dsql 全局对象,编码、字符集、连接池都自动解决。

2. 时间戳取当日范围而不是相对秒数

原始版本里很多人写 pubdate > time() - 86400,意思是"过去 24 小时"。这个写法的问题是:如果计划任务定在 23:50 跑,就会把昨天 23:50 之后到今天 23:50 之前的内容推一遍;但如果某天计划任务延迟了 30 分钟,就会漏掉前一天 23:50~00:20 的发布。用 mktime(0,0,0)mktime(23,59,59) 锁住"自然日",跑得早跑得晚都是同一批数据。

3. arcrank = 0 过滤未审核稿

DedeCMS 的 arcrank 字段:0 是开放浏览,-1 是待审核,>0 是会员等级阅读权限。如果你的站点开了"投稿要审核",没加 arcrank = 0 这个过滤条件,就会把待审稿的草稿链接也推给百度,返回的是 404,相当于亲自给自己制造死链。

4. typedir 必须做 {cmspath} 替换

这是被忽略最多的一行。线上看到过一个站,所有推送链接长这样:https://example.com{cmspath}/news/123.html。百度照样收,但收的全是 404。最后才发现该站装在 /site 二级目录里,cfg_cmspath/site,但脚本作者没替换占位符,整月配额白白浪费。

5. 已推送去重

这是脚本对外不太显眼但很重要的一块。baidu_pushed.txt 文件按行存所有已推送 URL。每次跑都先做 array_diff,避免相同链接当天反复推送。百度文档没有明说重复推送会扣配额,但实测上:同一 URL 一周内推第二次以上的,会被记进 not_same_site 或干脆静默丢弃。该文件长期累积,每两个月可以人工清空一次,或者改成只保留最近 60 天。

6. 日志落地到 data/ 目录

data/ 而不是网站根目录是因为 DedeCMS 官方默认 nginx 规则里 data/ 不允许 PHP 执行,但允许写入和读取。日志放这里既能写又不会被外网当作脚本访问。

宝塔计划任务的细致配置

宝塔的"任务类型"四选一里,这种场景应该用"访问 URL",因为它走的是宝塔自己的 curl,不依赖外部 cron 配置 PATH。具体步骤:

  1. 登录宝塔面板,左侧"计划任务"。
  2. 点"添加任务"。任务类型选 访问URL
  3. 任务名称填一个有辨识度的:DedeCMS 百度推送 - zhangwenbao.com
  4. 执行周期:每天 23:50。理由后面有详述。
  5. URL 地址填 https://www.zhangwenbao.com/baidu_push.php?key=xxxxkey 是你脚本里加的访问 token,避免被外网随便触发。
  6. 勾上"日志保留"。宝塔每次执行的输出会被记录,方便事后核对。

为什么是 23:50?我在三类时间点都跑过对比:早上 6 点跑、下午 14 点跑、晚上 23:50 跑。前两个时间点都会漏掉之后发布的文章,第二天才推;23:50 跑能把当天 0~23:50 发布的全部捕捉到,剩下 23:50~00:00 这十分钟的发布留给次日的当天范围(因为 mktime 锁的是自然日)。如果你站点发文集中在白天,可以再多挂一个早晨 8:00 的任务做兜底。

推送结果监控:怎么知道推送真的生效

百度 API 的响应 JSON 长这样:

{
  "remain": 4985,
  "success": 12,
  "not_same_site": [],
  "not_valid": []
}

四个字段的实战含义:

  • remain:当天剩余配额。建议在脚本里把它打印到日志,连续几天 remain 接近 0 就说明配额吃满,需要去后台看看是不是申请提配额。
  • success:本次推送成功条数。这个数字应该等于你 POST 上去的 URL 数。如果小于,说明有部分链接被丢弃。
  • not_same_site:URL 域名跟 token 绑定的域名不一致。最常见原因是带 www 和不带 www 的差异、http 和 https 混用。
  • not_valid:URL 格式非法。常见于 typedir 没替换 {cmspath}、或者站点 URL 里有空格、中文。

除了 API 返回,还要去搜索资源平台后台看"普通收录—资源提交—API提交"的趋势曲线,正常情况是每天一个柱子,柱子高度对应当天推送量。如果连续一周柱子高度等于配额上限的 90%+,就该考虑申请提额或缩减提交范围。

常见错误码与排错

API 返回非 200 时按下面的对应表排查:

  • 400 site error:URL 里 site 参数和 token 不匹配,或 site 后没有跟域名。检查脚本里 parse_url 出来的 host 是不是真域名(不要把 IP 当 host)。
  • 401 token is not valid:token 错了,或者域名换了 token 没换。重新到搜索资源平台领新 token。
  • 403 over quota:今天配额吃满。明天再推。
  • 404 site not exists:搜索资源平台里这个站还没添加,或被删了。先在后台把站点加上。
  • 413 over body size:单次 POST 超过 5MB。把 todo 数组按 1000 一批切片再循环 push。
  • 500 internal error:百度自己的故障,过一小时再试。

实战中 90% 的失败都是 400 和 401 这两类,根因都是 token 或 site 配错。剩下 10% 是配额。脚本里把 httpCode 和 response 都写日志,排错时直接 grep 错误码即可。

进阶:和 sitemap、indexnow 一起用

很多人以为 API 推送上了 sitemap 就可以撤了。错。三种方式各管一段:

  • API 推送覆盖最近发布的内容,时效性强但每天有上限。
  • sitemap.xml覆盖全站存量,百度按自己节奏抓,能修复 API 漏掉的旧文章。
  • indexnow是必应、Yandex 系协议(百度 2024 年开始有限支持),可以补 API 之外的搜索引擎。

正确做法是三种全开。API 每天跑,sitemap 一次性挂上不再动,indexnow 在内容发布钩子里同步推。这套组合拳下来,新文章被百度收录的中位时间可以压到 10 分钟以内,而单靠 sitemap 通常要 4~24 小时。

安全加固:避免推送脚本被外人触发

baidu_push.php 一旦放到根目录,外人就能通过 URL 直接访问。这有两个风险:被恶意刷接口拖垮服务器、或者被刷成"接口访问异常"导致百度封 token。两条防护写到脚本最顶部:

// 1. 简单 key 校验
$expectedKey = 'p7Kb9vZmTw';
if (empty($_GET['key']) || $_GET['key'] !== $expectedKey) {
    header('HTTP/1.1 403 Forbidden');
    exit('forbidden');
}

// 2. IP 白名单(只允许本机或宝塔所在服务器调用)
$allowed = ['127.0.0.1', '::1', '你的宝塔出网IP'];
if (!in_array($_SERVER['REMOTE_ADDR'], $allowed, true)) {
    header('HTTP/1.1 403 Forbidden');
    exit('not allowed ip');
}

计划任务里 URL 必须带正确的 key 才能跑,外网误触都被 403 拦掉。如果你担心 key 在 URL 里被记进访问日志泄露,改成 POST 请求或者加 nginx 层 deny 都可以。

额外细节:DedeCMS 列表页与详情页 URL 一致性

有些站点把 DedeCMS 的"伪静态"开了一半:列表页用伪静态、详情页还是 plus/view.php?aid=123。这种情况下 typedir 拼出来的 URL 是伪静态形式,但实际访问是动态形式,百度收的是 plus/view.php?aid=123,跟你推送的不一致,最后表现为收录数远低于推送数。

解决办法:在 DedeCMS 后台"系统参数 - 性能选项"里把"是否使用伪静态"打开,并在 nginx 加上 dede 的伪静态规则。所有详情页都走 .html 之后,推送和实际页面就对得上了。

常见问题解答

API 推送和主动推送有什么区别?

百度官方在 2022 年统一了术语,现在文档里只有"普通收录"和"快速收录"。普通收录 API 推送就是本文讲的免费版本,每天有配额;快速收录 API 推送是给已经在搜索资源平台获得"快速收录"权限的优质站点用的,时效性更强(分钟级)但权限要单独申请,并不是所有站点都能开。本文脚本走的是普通收录,DedeCMS 站点直接拿来用。如果你的站点拿到了快速收录权限,把 apiUrl 换成 http://data.zz.baidu.com/urls?site=xxx&token=xxx&type=daily 即可。

推送脚本会不会和 DedeCMS 自身的 SEO 插件冲突?

不会。DedeCMS 自带的"百度推送"是在文章保存的 hook 里同步调一次 curl,本文方案是计划任务定时调一次。两者数据来源都是 dede_archives 表,重复推送被脚本里的 baidu_pushed.txt 去重过滤掉,只是日志条目会多一些。如果你已经装了 DedeCMS 的官方推送插件,可以保留,本脚本作为兜底兜住"插件偶发失败"的场景。

为什么我的 success 总是 0,但 remain 又在减?

这个组合的根因几乎都是"URL 域名带不带 www 与 token 绑定的域名不一致"。百度后台领 token 时填的是 www.zhangwenbao.com,脚本里 siteUrl 写的是 https://zhangwenbao.com,推过去之后被认为不是同站,进 not_same_site 数组,但配额已经扣了。改成完全一致的形式即可。如果你的站点既走 www 又走非 www,要在百度后台为两个域名分别申请 token 并分别推送。

每天推送上限是多少?没备案的站点会被惩罚吗?

普通收录的配额上限官方说是 10 万条/日,但实际是按站点权重等级动态分配,新站初始配额一般 5000~10000。未备案站点配额会被压到正常值的 30%~50%,且部分推送被丢弃。建议尽快完成 ICP 备案,备案下来后第二周左右配额会自动调高,对收录速度有肉眼可见的提升。

计划任务说执行成功,但日志里看不到推送记录怎么办?

第一步检查脚本是否有写入权限。data/baidu_push.log 这个文件如果不存在且 data 目录权限不允许 PHP 写入,file_put_contents 会静默失败。手动 touch data/baidu_push.log && chmod 666 data/baidu_push.log。第二步检查宝塔计划任务的"日志保留"选项是不是开了,开了之后宝塔后台能看到任务每次的标准输出。第三步确认 URL 带的 key 参数和脚本里 $expectedKey 一致,否则脚本第一行就 exit 了,根本走不到推送。

能不能改成实时推送,文章一发布就推?

可以。在 dede/archives_do.phpdede/article_add.php 里找到保存成功后的 hook 位置,加一段 curl 调用本文的 push 接口。但要注意:实时推送会让发布操作多一次外部 HTTP 请求,遇到百度服务慢的时候编辑器会卡几秒。建议异步触发:发布时只把 URL 写入一个待推送队列文件,由独立的计划任务每 5 分钟跑一次扫队列推送,编辑体验和推送时效可以兼得。

脚本能用在 DedeCMS V5.6、V5.7、V5.8 之间吗?

能。DedeCMS 三个主流版本里 dede_archives 和 dede_arctype 的字段命名一直保持兼容,arcranktypediridtypeidpubdate 五个字段在所有版本都存在。cfg_cmspath 在 V5.6 之前叫 cmspath,如果你跑在很老的版本里要改一下变量名。我自己跑过 V5.7.108 和 V5.8.1,脚本无需改动。

写在最后

把这套脚本接到你 DedeCMS 站点之后,第一周可以每天看一次日志,确认 success 数与发布数对得上、remain 配额是否合理。第二周开始基本就能放着不管。重点是任何 SEO 优化最终的判断标准只有一个:百度站长后台的"索引量"曲线是不是在涨。如果按本文做完一个月索引量没动,那不是推送的问题,要回到内容质量、站内结构、外链建设上找原因,光靠推送 API 推不出排名。

保哥这套思路目前在 zhangwenbao.com 之外还有十几个 DedeCMS 站在用,最长的一个跑了三年没出过故障。脚本本身很短,但里面每一行的取舍都是一次踩坑总结。读到这里你已经具备从零搭建 DedeCMS 百度自动推送的全部背景知识,剩下的就是把代码贴到自己站点跑通。遇到具体错误码不知道怎么处理时,回来翻第十节的对照表基本都能定位。

分享到
标签
版权声明

本文标题:《DedeCMS百度自动推送实战:宝塔+API完整教程》

本文链接:https://zhangwenbao.com/bt-baidu-api-dedecms-url.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交