本教學改編自 SICSS Singapore 2023 工作坊代碼。 微博使用 JSON API(非傳統 HTML), 直接返回結構化數據,不需要 XPath 解析。 爬取指定用戶在特定時間段內的所有貼文,提取 10 個字段。
GET https://weibo.com/ajax/statuses/searchProfileuid(用戶ID)page(頁碼)starttime / endtime(Unix 時間戳)Cookie:COOKIE_STR| 字段名 | JSON 取值路徑 | 說明 |
|---|---|---|
| _id | each_weibo['id'] | 微博唯一 ID |
| weibo_url | 'https://weibo.com/' + user_id + '/' + mblogid | 拼接完整 URL |
| user_id | each_weibo['user']['id'] | 嵌套 JSON 取用戶 ID |
| created_at | each_weibo['created_at'] | 發布時間(UTC 字符串) |
| source | each_weibo['source'] | 發布來源(iPhone/Android/網頁版) |
| like_num | each_weibo['attitudes_count'] | 點讚數 |
| repost_num | each_weibo['reposts_count'] | 轉發數 |
| comment_num | each_weibo['comments_count'] | 評論數 |
| content | each_weibo['text_raw'] | 純文字內容(不含 HTML 標籤) |
| geo_info | each_weibo['geo'] | 地理位置(可選,多數為 null) |
weibo.com/u/2793117267$CONFIG.uid(在用戶主頁執行)weibo.com/ajax/profile/info?uid=... 的 Network 請求# 安裝所需套件
# 注意:json 是 Python 內建模組,不需要 pip 安裝!
!pip install requests # HTTP 請求庫
# datetime 也是 Python 內建模組,不需要 pip 安裝
import os
print("工作目錄:", os.getcwd())import requests # 發送 HTTP 請求
import datetime # 將日期字符串轉換為 Unix 時間戳
import json # 解析微博 API 回傳的 JSON 數據
import csv # 儲存為 CSV 格式
import os # 文件路徑操作
print("✅ 所有套件導入完成")# ═══════════════════════════════════════════════════════
# 如何獲取 Cookie(必讀):
# 1. 用 Chrome 登入 https://weibo.com
# 2. 按 F12 → Network 分頁 → 刷新頁面
# 3. 點擊任意 weibo.com 請求 → Headers → 找 "Cookie:"
# 4. 複製整行 Cookie 值,貼到下方 COOKIE_STR
# ═══════════════════════════════════════════════════════
COOKIE_STR = "SCF=Av5pDLQkZ9ZlxHgaag1MAaUUyaxG2-5DK7nlXjJvpZsj9NuZ2pMQTQ0a3Aze3DMsyoi4OEnr9bsjKt4j6rVowcA.; SINAGLOBAL=9907163292993.223.1751026142282; UOR=,,login.sina.com.cn; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WFYz2N3N-pzMGw55dfaf__s5JpX5KMhUgL.Fo-NSK2NSoBceh52dJLoIpfFHCH81C-41C-R1FH8SCHWxC-R1CH8SEHF1C-ReBtt; ULV=1772725555092:20:2:2:6279291782624.625.1772725554898:1772441874527; XSRF-TOKEN=-kxxzRn39W1i04b1nmsVT2rO; ALF=1776269269; SUB=_2A25EvFqFDeRhGeNJ7lMW9irKyzyIHXVnsNJNrDV8PUJbkNANLRfYkW1NS9Us91NdbNrIxiubyfsbwmSoyYTVsEc-; WBPSESS=9BNQRUw_qfhmyP_PgW7d1L64bIfGo2ey0LhxkWfSBoYg1CR5KvOorvcWNoS8mfMmzxbSI_NxRyzNS5AN5azAo5x7rkPTdpTyffiHD4KMMCjPxTmxzOEfOZLkLuvdkOjCkIjg1UnUqdECUSdGzlSVAA=="
# 自動從 Cookie 中提取 XSRF-TOKEN(微博防範 CSRF 攻擊用)
def get_xsrf_token(cookie_str):
for part in cookie_str.split(';'):
part = part.strip()
if part.startswith('XSRF-TOKEN='):
return part.split('=', 1)[1].strip()
return ''
XSRF_TOKEN = get_xsrf_token(COOKIE_STR)
# ── 爬取參數設定(必須在 HEADERS 之前定義,因為 Referer 需要 USER_ID)──
USER_ID = "2793117267" # 目標用戶的微博 UID(在用戶主頁 URL 中可找到)
START_DATE = '2023-04-07' # 開始日期(格式:YYYY-MM-DD)
END_DATE = '2023-06-08' # 結束日期(格式:YYYY-MM-DD)
MAX_PAGES = 3 # 最多爬幾頁(每頁約 10-20 條)
OUTPUT_CSV = 'weibo_posts.csv'
# HTTP Headers:模擬真實 Chrome 瀏覽器
# 關鍵:微博 API 需要 Referer + X-XSRF-TOKEN 才能通過 403 驗證
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Cookie': COOKIE_STR,
'Referer': 'https://weibo.com/u/' + USER_ID, # 必須!告訴服務器請求來自哪裡
'X-XSRF-TOKEN': XSRF_TOKEN, # 必須!防止 CSRF 攻擊驗證
'X-Requested-With': 'XMLHttpRequest', # 標記為 AJAX 請求
}
# ── 將日期轉換為 Unix 時間戳(微博 API 要求)────────────
date_start = datetime.datetime.strptime(START_DATE, '%Y-%m-%d')
ds = int(datetime.datetime.timestamp(date_start)) # 開始時間戳
date_end = datetime.datetime.strptime(END_DATE, '%Y-%m-%d')
de = int(datetime.datetime.timestamp(date_end)) # 結束時間戳
print("✅ 參數設定完成")
print("用戶 ID:", USER_ID)
print("時間範圍:", START_DATE, "→", END_DATE)
print("時間戳:", ds, "→", de)# ═══════════════════════════════════════════════════════
# 微博 API 說明:
# 端點:https://weibo.com/ajax/statuses/searchProfile
# 參數:uid(用戶ID)、page(頁碼)、starttime/endtime(Unix時間戳)
# hasori=1(原創)、hasret=1(轉發)、hastext=1(有文字)
# haspic=1(有圖片)、hasvideo=1(有視頻)、hasmusic=1(有音樂)
# 注意:這是 JSON API,不是 HTML,不需要 XPath 解析
# ═══════════════════════════════════════════════════════
all_weibos = [] # 儲存所有頁的微博
for page_num in range(1, MAX_PAGES + 1):
print("正在爬取第", page_num, "頁...")
# 構建 API URL(用 .format() 填入參數)
url = (
'https://weibo.com/ajax/statuses/searchProfile'
'?uid={}&page={}&starttime={}&endtime={}'
'&hasori=1&hasret=1&hastext=1&haspic=1&hasvideo=1&hasmusic=1'
).format(USER_ID, page_num, ds, de)
# 發送 GET 請求(帶 Cookie 認證)
response = requests.get(url, headers=HEADERS)
# 檢查回應狀態
if response.status_code != 200:
print(" ✗ 請求失敗,狀態碼:", response.status_code)
break
# 將 JSON 字符串解析為 Python dict
data = json.loads(response.text)
# 取出微博列表(在 data['data']['list'] 中)
weibo_list = data.get('data', {}).get('list', [])
print(" → 找到", len(weibo_list), "條微博")
if not weibo_list:
print(" → 沒有更多數據,停止爬取")
break
all_weibos.extend(weibo_list)
# 加入延遲,避免被封鎖
import time
time.sleep(1.5)
print("\n✅ 共收集", len(all_weibos), "條微博")
# 預覽第一條微博的 JSON 結構(了解數據格式)
if all_weibos:
first = all_weibos[0]
print("\n第一條微博預覽:")
print(" ID:", first.get('id'))
print(" 用戶:", first.get('user', {}).get('screen_name'))
print(" 時間:", first.get('created_at'))
print(" 內容:", str(first.get('text_raw', ''))[:50], "...")# ── 提取 10 個字段 ────────────────────────────────────
weibo_records = []
for each_weibo in all_weibos:
item = {}
# 字段 1:微博唯一 ID
item['_id'] = each_weibo.get('id', '')
# 字段 2:微博完整 URL(拼接用戶 ID + mblogid)
user_id_val = each_weibo.get('user', {}).get('id', '')
mblogid = each_weibo.get('mblogid', '')
item['weibo_url'] = 'https://weibo.com/' + str(user_id_val) + '/' + str(mblogid)
# 字段 3:用戶 ID(嵌套在 user 對象中)
item['user_id'] = user_id_val
# 字段 4:用戶暱稱
item['screen_name'] = each_weibo.get('user', {}).get('screen_name', '')
# 字段 5:發布時間(UTC 字符串,如 "Fri Apr 07 10:30:00 +0800 2023")
item['created_at'] = each_weibo.get('created_at', '')
# 字段 6:發布來源(如 "iPhone客户端"、"微博 weibo.com")
item['source'] = each_weibo.get('source', '')
# 字段 7:點讚數(attitudes = 態度 = 點讚)
item['like_num'] = each_weibo.get('attitudes_count', 0)
# 字段 8:轉發數
item['repost_num'] = each_weibo.get('reposts_count', 0)
# 字段 9:評論數
item['comment_num'] = each_weibo.get('comments_count', 0)
# 字段 10:純文字內容(text_raw 不含 HTML 標籤,比 text 更乾淨)
item['content'] = each_weibo.get('text_raw', '')
# 字段 11(可選):地理位置(多數為 null)
item['geo_info'] = str(each_weibo.get('geo', ''))
weibo_records.append(item)
print("✅ 已解析", len(weibo_records), "條微博")
if weibo_records:
print("\n前 3 條預覽:")
for r in weibo_records[:3]:
print(" [", r['_id'], "]", r['screen_name'], "|", r['created_at'][:10], "|", str(r['content'])[:30], "...")# ── 儲存為 CSV ────────────────────────────────────────
if not weibo_records:
print("❌ 錯誤:weibo_records 為空!請先執行 Cell 4 和 Cell 5")
else:
fieldnames = ['_id', 'weibo_url', 'user_id', 'screen_name',
'created_at', 'source', 'like_num', 'repost_num',
'comment_num', 'content', 'geo_info']
# encoding='utf-8-sig' 加入 BOM,確保 Excel 正確顯示中文
with open(OUTPUT_CSV, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction='ignore')
writer.writeheader()
writer.writerows(weibo_records)
print("✅ 已儲存 " + str(len(weibo_records)) + " 條微博到 " + OUTPUT_CSV)
# ── 可靠下載函數(JavaScript 觸發 + 備用連結)────────────
def colab_download(filename):
"""
在 Google Colab 中可靠地下載文件
方法 1:JavaScript 直接觸發瀏覽器下載
方法 2:顯示備用下載連結(若瀏覽器攔截,手動點擊)
"""
import base64
from IPython.display import display, Javascript, HTML
with open(filename, 'rb') as f:
data = base64.b64encode(f.read()).decode('utf-8')
# JavaScript 動態建立 <a> 標籤並自動點擊
js = """
var link = document.createElement('a');
link.href = 'data:text/csv;base64,""" + "' + data + '" + """';
link.download = '""" + "' + filename + '" + """';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
"""
display(Javascript(js))
# 備用手動下載連結
html_link = '<a href="data:text/csv;base64,' + data + '" download="' + filename + '" style="color:#4ade80;font-family:monospace;font-size:13px">⬇️ 點此手動下載 ' + filename + '</a>'
display(HTML(html_link))
print("📥 已觸發下載:" + filename)
colab_download(OUTPUT_CSV)
print("\n✅ 下載完成!")
print(" 📄 " + OUTPUT_CSV + " — 微博貼文(" + str(len(weibo_records)) + " 條)")COOKIE_STR、USER_ID、START_DATE、END_DATEjson.loads() 解析dict['key'] 取值etree.HTML() 解析tree.xpath("...") 取值