一、教程簡介
本文基於 ThinkPHP 6.x/8.x 框架,從零到一實現一套完整的微博公開數據採集方案。核心能力包括:自動獲取微博訪問Cookie(無需手動配置)、爬取熱門時間線微博列表、採集單條微博評論、清理文本格式、標準化日期顯示,同時內置防封禁策略和完整的異常處理機制,可直接集成到你的 ThinkPHP 項目中使用。
二、前置準備
1. 環境要求
- PHP 版本:7.4 及以上(需開啓 curl 擴展,可通過 php -m 查看)
- 框架版本:ThinkPHP 6.x / 8.x(5.1 版本可稍作適配)
- 工具依賴:Composer(用於安裝第三方包)
- 服務器:任意可運行 PHP 的環境(本地/雲服務器均可)
2. 安裝核心依賴
本方案使用 GuzzleHTTP 處理 HTTP 請求(比原生 curl 更易用、更穩定),執行以下 Composer 命令安裝:
composer require guzzlehttp/guzzle
三、完整代碼實現
1. 創建控制器文件
在 ThinkPHP 項目的 app/controller 目錄下新建 WeiboController.php,寫入以下完整代碼(包含所有核心功能):
<?php
namespace app\controller;
use GuzzleHttp\Client;
use think\Controller;
use think\facade\Log;
use think\facade\Request;
use think\response\Json;
use DateTime;
use DateTimeZone;
use DateTimeException;
use Exception;
class WeiboController extends Controller
{
/**
* 微博數據採集入口接口
* 訪問地址:http://你的域名/weibo/test?page=1&comment_count=10
* @return Json
*/
public function testweibo()
{
try {
// 1. 獲取並校驗請求參數
$page = Request::param('page', 1, 'intval');
$commentCount = Request::param('comment_count', 20, 'intval');
// 頁碼合法性校驗
if ($page < 1) {
return json(['code' => 0, 'msg' => '頁碼必須大於0', 'data' => []])->code(400);
}
// 2. 初始化Guzzle客户端(模擬瀏覽器請求,避免被識別為爬蟲)
$client = new Client([
'timeout' => 15, // 請求超時時間(秒)
'verify' => false, // 關閉SSL證書驗證(避免服務器證書問題)
'headers' => [
'referer' => 'https://weibo.com/newlogin?tabtype=weibo&gid=102803&openLoginLayer=0&url=https%3A%2F%2Fweibo.com%2F',
'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0',
'Accept' => 'application/json, text/javascript, */*; q=0.01',
'X-Requested-With' => 'XMLHttpRequest',
]
]);
// 3. 自動獲取SUB Cookie(核心:無需手動從瀏覽器複製)
$sub = $this->getSubCookie($client);
if (empty($sub)) {
return json(['code' => 0, 'msg' => '獲取訪問Cookie失敗', 'data' => []])->code(500);
}
// 4. 構造Cookie數組並獲取微博熱門列表
$cookies = ['SUB' => $sub];
$weiboList = $this->getWeiboList($client, $cookies, $page);
// 無數據時返回友好提示
if (empty($weiboList)) {
return json([
'code' => 1,
'msg' => '獲取微博成功,當前頁無數據',
'data' => ['page' => $page, 'total' => 0, 'list' => []]
]);
}
// 5. 處理微博數據(含評論採集,測試模式僅取前3條)
$testLimit = min(3, count($weiboList)); // 限制測試條數,避免請求過多
$resultList = [];
for ($i = 0; $i < $testLimit; $i++) {
$weibo = $weiboList[$i];
// 格式化發佈時間(轉為標準格式)
$formattedDate = $this->formatWeiboDate($weibo['created_at'] ?? '');
// 清理微博內容(去除HTML標籤、保留表情)
$cleanContent = $this->cleanWeiboText($weibo['text_raw'] ?? $weibo['text'] ?? '');
// 獲取評論數據(添加隨機延遲防封禁)
$comments = $this->getWeiboComments($client, $weibo['id'] ?? '', $commentCount);
sleep(rand(1, 3)); // 1-3秒隨機延遲,降低請求頻率
// 組裝結構化數據
$resultList[] = [
'weibo_id' => $weibo['id'] ?? '',
'user_name' => $weibo['user']['screen_name'] ?? '未知用户',
'source' => $weibo['source'] ?? '未知來源',
'content' => $cleanContent,
'publish_time' => $formattedDate,
'stats' => [
'reposts_count' => $weibo['reposts_count'] ?? 0, // 轉發數
'comments_count' => $weibo['comments_count'] ?? 0, // 評論數
'attitudes_count' => $weibo['attitudes_count'] ?? 0 // 點贊數
],
'comments' => $comments,
'comment_count' => count($comments)
];
}
// 6. 返回最終採集結果
return json([
'code' => 1,
'msg' => '微博數據採集成功',
'data' => [
'page' => $page,
'total_weibo' => count($weiboList), // 本次獲取的微博總數
'test_weibo_count' => count($resultList), // 實際處理的微博數
'weibo_list' => $resultList
]
]);
} catch (Exception $e) {
// 異常日誌記錄(便於排查問題)
Log::error("微博採集失敗:{$e->getMessage()} 行號:{$e->getLine()} 文件名:{$e->getFile()}");
return json([
'code' => 0,
'msg' => '採集失敗:'.$e->getMessage(),
'data' => []
])->code(500);
}
}
/**
* 自動獲取SUB Cookie(微博核心認證字段)
* @param Client $client Guzzle客户端實例
* @return string SUB Cookie值(空字符串表示失敗)
*/
private function getSubCookie($client)
{
// 微博訪客Cookie生成接口
$url = "https://passport.weibo.com/visitor/genvisitor2";
$postData = [
"cb" => "visitor_gray_callback",
"tid" => "01AUXHE0uWNcmbV0Qlq3L-R4dZHGS_3E7eKqUtdA9HiUgQ",
"from" => "weibo",
"webdriver" => "false"
];
// 發送POST請求獲取Cookie
$response = $client->request('POST', $url, [
'form_params' => $postData
]);
$responseBody = $response->getBody()->getContents();
// 正則匹配返回結果中的SUB字段
if (preg_match('/"sub":"([^"]+)"/', $responseBody, $matches)) {
return $matches[1];
}
return '';
}
/**
* 獲取微博熱門時間線列表
* @param Client $client Guzzle客户端實例
* @param array $cookies Cookie數組(含SUB)
* @param int $page 分頁參數(max_id)
* @return array 微博列表數據
*/
private function getWeiboList($client, $cookies, $page)
{
$url = 'https://weibo.com/ajax/feed/hottimeline';
// 核心請求參數(微博熱門接口固定參數)
$params = [
'refresh' => '2',
'group_id' => '102803', // 熱門分組ID(推薦流)
'containerid' => '102803',
'extparam' => 'discover|new_feed',
'max_id' => $page, // 分頁參數(頁碼)
'count' => '10', // 每頁獲取10條
];
// 構建Cookie字符串
$cookieStr = '';
foreach ($cookies as $key => $value) {
$cookieStr .= "$key=$value; ";
}
// 發送GET請求獲取微博列表
$response = $client->request('GET', $url, [
'query' => $params,
'headers' => [
'Cookie' => rtrim($cookieStr, '; ') // 去除末尾多餘的分號和空格
]
]);
$responseBody = $response->getBody()->getContents();
$jsonData = json_decode($responseBody, true);
// 返回微博列表(無數據時返回空數組)
return $jsonData['statuses'] ?? [];
}
/**
* 獲取單條微博的評論數據
* @param Client $client Guzzle客户端實例
* @param string $weiboId 微博ID
* @param int $count 要獲取的評論條數(最大20條)
* @return array 格式化後的評論列表
*/
private function getWeiboComments($client, $weiboId, $count = 20)
{
// 微博ID為空時直接返回空
if (empty($weiboId)) {
return [];
}
$url = 'https://weibo.com/ajax/statuses/buildComments';
$params = [
'flow' => 0,
'is_reload' => '1',
'id' => $weiboId, // 目標微博ID
'is_show_bulletin' => '2',
'is_mix' => '0',
'count' => min($count, 20), // 限制最大20條(接口限制)
'uid' => '1700720163',
'fetch_level' => '0',
'locale' => 'zh-CN'
];
// 發送GET請求獲取評論
$response = $client->request('GET', $url, [
'query' => $params
]);
$responseBody = $response->getBody()->getContents();
$jsonData = json_decode($responseBody, true);
// 格式化評論數據
$comments = [];
foreach ($jsonData['data'] ?? [] as $item) {
$comments[] = [
'comment_id' => $item['id'] ?? '',
'user_name' => $item['user']['screen_name'] ?? '未知用户',
'content' => $this->cleanWeiboText($item['text'] ?? ''),
'publish_time' => $this->formatWeiboDate($item['created_at'] ?? ''),
'like_count' => $item['like_counts'] ?? 0
];
}
return $comments;
}
/**
* 清理微博文本(去除HTML標籤、保留表情符號)
* @param string $text 原始微博文本
* @return string 清理後的純文本
*/
private function cleanWeiboText($text)
{
if (empty($text)) {
return '無內容';
}
// 保留img標籤的alt屬性(表情符號,如[微笑])
$text = preg_replace('/<img\s+[^>]*alt="(\[[^\]]+\])"[^>]*>/', '$1', $text);
// 去除所有剩餘HTML標籤(a、span、div等)
$text = preg_replace('/<[^>]+>/', '', $text);
// 去除用户卡片的特殊標記
$text = preg_replace('/ usercard="[^"]*"/', '', $text);
// 保留鏈接文本,去除href屬性
$text = preg_replace('/<a\s+[^>]*href=[^>]*>([^<]+)<\/a>/', '$1', $text);
// 去除換行符和多餘空格
$text = str_replace("\n", ' ', $text);
$text = preg_replace('/\s+/', ' ', trim($text));
return $text;
}
/**
* 格式化微博日期(轉為Y-m-d H:i:s標準格式)
* @param string $dateStr 原始日期字符串(如 Wed Sep 18 10:22:33 +0800 2024)
* @return string 標準化時間
*/
private function formatWeiboDate($dateStr)
{
if (empty($dateStr)) {
return '未知時間';
}
try {
// 解析微博默認日期格式
$date = DateTime::createFromFormat('D M d H:i:s O Y', $dateStr, new DateTimeZone('Asia/Shanghai'));
if ($date) {
return $date->format('Y-m-d H:i:s');
}
// 兼容其他日期格式
$date = new DateTime($dateStr, new DateTimeZone('Asia/Shanghai'));
return $date->format('Y-m-d H:i:s');
} catch (DateTimeException $e) {
return '格式錯誤';
}
}
}
2. 配置路由
在 ThinkPHP 項目的 route/app.php 文件中添加以下路由配置,用於訪問微博採集接口:
use think\facade\Route;
// 微博數據採集接口(GET請求)
Route::get('/weibo/test', 'WeiboController@testweibo');
四、核心功能詳解
1. 自動獲取 SUB Cookie
功能説明
微博接口需要 SUB Cookie 進行身份認證,本方案通過調用微博官方的訪客 Cookie 生成接口(genvisitor2),自動獲取 SUB 值,無需手動從瀏覽器複製 Cookie,解決了 Cookie 過期、配置繁瑣的問題。
關鍵邏輯
- 向 https://passport.weibo.com/visitor/genvisitor2 發送 POST 請求,攜帶固定參數
- 通過正則表達式 /"sub":"(+)"/ 匹配返回結果中的 SUB 值
- 若獲取失敗,直接返回錯誤提示
2. 微博熱門列表爬取
接口説明
使用微博熱門時間線接口 https://weibo.com/ajax/feed/hottimeline,返回推薦流的熱門微博數據。
核心參數
| 參數名 | 取值 | 説明 |
|---|---|---|
| group_id | 102803 | 熱門分組ID(固定值) |
| containerid | 102803 | 容器ID(與group_id一致) |
| max_id | 頁碼(如1) | 分頁參數,控制獲取第幾頁 |
| count | 10 | 每頁獲取的微博條數 |
3. 評論採集
接口説明
通過 https://weibo.com/ajax/statuses/buildComments 接口獲取單條微博的評論數據,接口限制最多返回 20 條/次。
防封禁策略
- 每條評論請求後添加 1-3 秒隨機延遲(sleep(rand(1,3)))
- 限制測試模式下僅採集前 3 條微博的評論
- 模擬瀏覽器請求頭,避免被識別為爬蟲
4. 文本與日期格式化
文本清理
- 保留表情符號(如 [微笑]):通過正則匹配 img 標籤的 alt 屬性
- 去除所有 HTML 標籤:避免前端展示時出現亂碼
- 清理多餘空格和換行:統一文本格式
日期格式化
- 解析微博默認日期格式(如 Wed Sep 18 10:22:33 +0800 2024)
- 轉為 Y-m-d H:i:s 標準格式,便於存儲和展示
- 異常處理:解析失敗時返回「格式錯誤」
五、接口調用與測試
1. 調用方式
GET 請求示例
# 基礎調用(默認頁碼1,每條微博獲取20條評論)
http://你的域名/weibo/test
# 自定義參數(頁碼2,每條微博獲取10條評論)
http://你的域名/weibo/test?page=2&comment_count=10
2. 參數説明
| 參數名 | 類型 | 默認值 | 取值範圍 | 説明 |
|---|---|---|---|---|
| page | int | 1 | ≥1 | 微博列表的頁碼 |
| comment_count | int | 20 | 1~20 | 每條微博要獲取的評論條數 |
3. 返回結果示例
{
"code": 1,
"msg": "微博數據採集成功",
"data": {
"page": 1,
"total_weibo": 10,
"test_weibo_count": 3,
"weibo_list": [
{
"weibo_id": "1234567890123456",
"user_name": "微博官方",
"source": "微博客户端",
"content": "這是一條測試微博[微笑]",
"publish_time": "2024-09-18 10:22:33",
"stats": {
"reposts_count": 1200,
"comments_count": 500,
"attitudes_count": 3000
},
"comments": [
{
"comment_id": "9876543210987654",
"user_name": "普通用户",
"content": "這條微博很有意義",
"publish_time": "2024-09-18 10:30:00",
"like_count": 15
}
],
"comment_count": 1
}
]
}
}
4. 錯誤碼説明
| code | 説明 | 解決方案 |
|---|---|---|
| 0 | 請求失敗 | 查看msg字段的錯誤提示,檢查日誌 |
| 1 | 請求成功 | 正常處理返回數據 |
| 400 | 參數錯誤 | 確保page參數≥1,comment_count參數1~20 |
| 500 | 服務器內部錯誤 | 檢查Cookie獲取是否成功,或接口是否正常訪問 |
六、注意事項
1. 防封禁注意事項
- 請求頻率:避免短時間內大量請求,建議單IP每分鐘請求不超過20次
- 請求頭配置:必須模擬瀏覽器的 user-agent、referer,否則接口會返回403
- SSL驗證:關閉 verify => false 避免證書問題導致請求失敗
- IP封禁:若出現訪問失敗,可更換IP或等待1-2小時後重試
2. 兼容性適配
ThinkPHP 5.1 適配
- 將 use think\facade\Request; 改為 use Request;
- 將 return json()->code(400); 改為 return json()->header('', '', 400);
- 將 Log::error() 改為 \think\Log::error()
3. 合法性説明
- 本方案僅用於爬取微博公開數據,嚴禁用於商業爬蟲、惡意採集
- 遵守《網絡安全法》和微博平台用户協議,控制爬取規模
- 不得將採集的數據用於違法違規場景,否則後果自負
七、擴展優化方向
1. 功能擴展
- 分頁爬取:解析接口返回的 max_id,實現多頁微博自動採集
- 評論分頁:通過評論接口的 max_id 參數實現評論分頁獲取
- 數據存儲:將採集的微博/評論存入 MySQL/Redis(使用 ThinkPHP 模型)
- 關鍵詞過濾:添加關鍵詞篩選,僅採集包含指定關鍵詞的微博
- 多賬號輪換:配置多個 SUB Cookie 輪換使用,降低單賬號封禁風險
2. 性能優化
- Cookie 緩存:將獲取的 SUB Cookie 緩存到 Redis,有效期內無需重複請求
- 異步請求:使用 Guzzle 異步請求批量獲取評論,提升採集效率
- 連接池:配置 Guzzle 連接池,減少 TCP 連接建立開銷
- 數據壓縮:返回數據時開啓 Gzip 壓縮,減少傳輸體積
3. 穩定性優化
- 重試機制:請求失敗時添加重試邏輯(最多3次),提升成功率
- 動態 User-Agent:隨機切換 User-Agent 列表,降低被識別為爬蟲的概率
- 監控告警:添加接口可用性監控,異常時觸發郵件/短信告警
- 熔斷機制:連續失敗次數達到閾值時暫停採集,避免無效請求
八、常見問題排查
1. Cookie 獲取失敗
- 檢查 Guzzle 客户端是否配置了正確的請求頭
- 確認服務器可以訪問 passport.weibo.com(可通過 curl 測試)
- 檢查正則表達式是否匹配最新的返回格式
2. 微博列表返回空
- 確認 SUB Cookie 有效(可手動替換為瀏覽器的 SUB 測試)
- 檢查分頁參數 max_id 是否正確
- 確認請求頭的 referer、user-agent 配置正確