WordPress 自动提交百度:我配了半年,其实一直失败

我在博客上挂了「发文章自动推送百度」这个功能,一直以为它在工作。

直到那天我心血来潮,直接去调了一次百度接口——报错。

回头查服务器上的配置,才发现问题从第一天起就存在。

先说这个功能值不值得做

百度发现新页面有三条路:等蜘蛛自己爬、提交 sitemap、主动推送。

前两条都是「等」—— sitemap 要百度定期来拉,蜘蛛爬更看它心情。主动推送是唯一能「发完立刻告诉百度」的。

对老站意义不大,蜘蛛本来就来得勤。但新站完全不一样:你没权重,蜘蛛来得稀,推送相当于主动把「我这儿有新内容」递到百度面前。

值得做。而且配好之后是全自动的,不用管。

一个对照实验,把根因钉死

百度「普通收录」的推送接口长这样:

http://data.zz.baidu.com/urls?site=你的站点&token=你的token

我先猜了三种可能,逐个排除:

token 过期? 去站长平台重新复制了一遍,还是报错。

配额用尽? 但配额用尽报的是 over quota,跟我这个不是一回事。

站点没验证? 重新确认了验证状态,是正常的。

一个都不对。

后来我做了个最笨的对照实验,只改一个参数:

# 带协议
curl -H 'Content-Type:text/plain' \
  --data 'https://www.example.com/post.html' \
  'http://data.zz.baidu.com/urls?site=https://www.example.com&token=xxx'
# {"error":400,"message":"site init fail"}

# 不带协议
curl -H 'Content-Type:text/plain' \
  --data 'https://www.example.com/post.html' \
  'http://data.zz.baidu.com/urls?site=www.example.com&token=xxx'
# {"remain":9,"success":1}

根因就一个:site 参数不能带协议。

写 https://www.example.com 报 site init fail,写 www.example.com 才正常。我把线上配置翻出来一看,存的就是带协议的那一串。

site 参数带协议与不带协议的结果对照
同一个接口,只改一个参数:带协议报 site init fail,不带协议正常推送

为什么半年没人发现

这才是更值得说的部分。

问题不只在参数写错,还在于旧代码根本不看结果:

wp_remote_post( $api, array(
    'timeout'  => 3,
    'blocking' => false,   // ← 元凶在这
    'body'     => $url,
) );

blocking => false 的意思是:请求发出去就不管了,不等待响应。

好处是发布文章时不会因为百度接口慢而卡住页面;坏处是——推送失败你永远不会知道。

参数错误 + 静默失败,两件事叠在一起,就成了「功能看起来配好了,实际上半年没成功过一次,而没有任何人察觉」。

正确的写法

修的时候顺手加了三道保险。这是现在在跑的代码:

/** site 参数必须剥掉协议 —— 带协议会 site init fail */
function baidu_host() {
    $s = trim( (string) BAIDU_SITE );
    $s = preg_replace( '#^https?://#i', '', $s );
    return rtrim( $s, '/' );
}

function baidu_post( $url ) {
    $api = 'http://data.zz.baidu.com/urls?site=' . rawurlencode( baidu_host() )
         . '&token=' . rawurlencode( BAIDU_TOKEN );

    $r = wp_remote_post( $api, array(
        'timeout'  => 8,
        'blocking' => true,                                   // ① 拿响应
        'headers'  => array( 'Content-Type' => 'text/plain' ),
        'body'     => $url,
    ) );

    if ( is_wp_error( $r ) ) {
        return array( false, '网络错误:' . $r->get_error_message(), null );
    }
    $code = (int) wp_remote_retrieve_response_code( $r );
    $body = json_decode( wp_remote_retrieve_body( $r ), true );

    if ( 200 === $code && isset( $body['success'] ) ) {
        return array( true, '成功', (int) $body['remain'] );
    }
    return array( false, $body['message'] ?? ( 'HTTP ' . $code ), null );
}

三道保险分别是:

# 保险 解决什么
① blocking => true 拿得到响应,不再「发了不知道结果」
② 结果写进 postmeta 后台文章列表直接显示推送状态
③ 失败自动重试 配额用尽这类临时错误,1/2/3 小时后各重试一次

第 ③ 条用 wp_schedule_single_event 排个延迟任务,不用你手动补推。

百度推送链路与三道保险
拿响应、记结果、自动重试 —— 让推送失败不再是静默的

顺便说下配额

普通收录的额度是百度按站点质量动态给的,新站一般每天 10 条左右。我配好那天推了两次,剩余从 10 掉到 8。

所以代码里还加了去重:同一个 URL 6 小时内不重复推。不然你在编辑器里改五次错别字,一天的额度就没了。

怎么确认它真的在工作

配好之后不要只信代码,去验证。三种办法,从简单到彻底:

① 看后台。加了状态列之后,文章列表里能直接看到:未推送 / ✓ 成功(剩余配额)/ ✗ 失败(原因)。

② 直接调一次接口:

// 在服务器上跑,把 123 换成你的文章 ID
php -r 'define("WP_USE_THEMES", false);
  require "/path/to/wp-load.php";
  var_dump( baidu_post( get_permalink(123) ) );'

③ 查 hook 有没有注册上:

var_dump( has_action('publish_post', 'baidu_push') );  // 返回 10 就对了

第三种最容易被忽略——代码写对了,不代表 hook 挂上了。

最后

回头看,最坑的地方其实不是那个参数。

是 blocking => false。

它当时是个"合理"的选择:发布文章不该被第三方接口拖慢。但它把"失败"这个信息一起吞掉了。

现在我给自己定了个规矩:凡是调外部接口,都要拿响应、记结果、能看见。 哪怕只记一行日志。

慢几秒可以忍,不知道它死没死,忍不了。


本站为个人技术记录,内容均为原创实操总结,转载请注明出处。

发表评论