Trên blog của tôi có một loạt bài viết mang tên “Những suy nghĩ nhỏ,” thực chất là bản sao lưu cá nhân của tài khoản Weibo. Tuy nhiên, trước đây tôi phải duy trì thủ công, điều này khá phức tạp và tốn thời gian. Trong hai ngày qua, tôi đã tạo ra một kịch bản Python để tự động trích xuất nội dung Weibo và chuyển đổi nó thành định dạng phù hợp với loạt bài “Những suy nghĩ nhỏ.” Bài viết “Những suy nghĩ nhỏ” (tháng 12 năm 2020) mà tôi đã đăng hôm qua đã được tạo lại hoàn toàn bằng cách sử dụng kịch bản này.
Để thực hiện việc này, tôi cần:
- Đăng nhập vào Weibo để lấy cookie và user-agent.
- Sử dụng API lật trang Weibo.
- Sử dụng API mở rộng cho các bài viết dài trên Weibo.
- Thư viện Python bên thứ ba: requests.
Để đảm bảo lưu trữ đầy đủ, kịch bản đã thực hiện:
- Lấy toàn bộ nội dung bài viết bất cứ khi nào có thể.
- Lấy cả các bài viết được chia sẻ lại.
- Tải xuống tất cả hình ảnh liên quan và lưu chúng vào máy cục bộ.
- Thay thế các đường dẫn rút gọn bị chặn bởi Weibo bằng các đường dẫn gốc.
Tôi không phải là chuyên gia lập trình, vì vậy mã nguồn của tôi còn rất thô sơ. Kịch bản chỉ hoạt động trên máy tính của tôi (macOS + Python 3.8). Dưới đây là mã nguồn đầy đủ:
import requests
from datetime import datetime
import random
import time
# Chỉ định thời gian bắt đầu để chỉ lấy nội dung sau tháng YYYYMM
NAM_THANG = datetime.strptime('202012','%Y%m')
# Thay thế bằng cookie và user-agent của bạn
DIEU_KHOAN_DANG_NHAP = {
'cookie': '',
'user-agent': ''
}
# Lấy nội dung của một bài Weibo đơn lẻ, không bao gồm nội dung chia sẻ lại.
def lay_mot_bai_weibo(doi_tuong_json):
ma_bai = doi_tuong_json['mblogid']
duong_dan_bai = '...' + ma_bai
thoi_gian_tao = datetime.strptime(doi_tuong_json['created_at'].replace('+0800',''),'%a %b %d %H:%M:%S %Y')
chuoi_thoi_gian = time.strftime('%Y-%m-%d %H:%M')
ten_nguoi_dung = doi_tuong_json['user']['screen_name']
if doi_tuong_json['isLongText'] == True:
du_lieu_dai = requests.get('...')
van_ban_nguyen_van=du_lieu_dai['longTextContent'].replace('\u200b','').strip()
if 'url_struct' in du_lieu_dai.keys():
van_ban_nguyen_van=thay_the_url(van_ban_nguyen_van, du_lieu_dai['url_struct'])
else:
van_ban_nguyen_van = doi_tuong_json['text_raw'].replace('\u200b','').strip()
if 'url_struct' in doi_tuong_json.keys():
van_ban_nguyen_van = thay_the_url(van_ban_nguyen_van, doi_tuong_json['url_struct'])
danh_sach_hinh_anh = doi_tuong_json['pic_ids']
van_ban_hinh_anh = ''
for id_hinh in danh_sach_hinh_anh:
duong_dan_hinh = doi_tuong_json['pic_infos'][id_hinh]['original']['url']
ten_hinh = tai_xuong_hinh(duong_dan_hinh)
van_ban_hinh_anh = van_ban_hinh_anh + '![[' + ten_hinh + ']]' + '\n'
noi_dung_bai = '@{ten_nguoi_dung}:{van_ban_nguyen_van}\n\n{van_ban_hinh_anh}'.format(
ten_nguoi_dung=ten_nguoi_dung,
van_ban_nguyen_van=van_ban_nguyen_van ,
van_ban_hinh_anh=van_ban_hinh_anh
)
return {
'thoi_gian_bai': thoi_gian_tao,
'duong_dan_bai': duong_dan_bai,
'nguoi_dung': ten_nguoi_dung,
'noi_dung_bai': noi_dung_bai
}
# Lấy nội dung của một bài Weibo, bao gồm nội dung chia sẻ lại nếu có.
def lay_bai_weibo_don(doi_tuong_json):
bai_chinh = lay_mot_bai_weibo(doi_tuong_json)
noi_dung_hoan_chinh = ''
if 'retweeted_status' in doi_tuong_json.keys():
bai_chia_se = lay_mot_bai_weibo(doi_tuong_json['retweeted_status'])
noi_dung_hoan_chinh = '...'
else:
noi_dung_hoan_chinh = '...'
if 'url_struct' in doi_tuong_json.keys():
noi_dung_hoan_chinh=thay_the_url(noi_dung_hoan_chinh, doi_tuong_json['url_struct'])
return {
'thoi_gian_bai': bai_chinh['thoi_gian_bai'],
'noi_dung_hoan_chinh': noi_dung_hoan_chinh
}
# Lấy một trang Weibo, yêu cầu thời gian phải từ NAM_THANG trở đi.
def lay_trang_weibo(so_trang,NAM_THANG):
duong_dan_trang = '...' + str(so_trang)
yeu_cau_bai = requests.get(duong_dan_trang, headers=DIEU_KHOAN_DANG_NHAP)
if yeu_cau_bai.json()['ok'] != 1:
return {
'so_trang': so_trang,
'ok': yeu_cau_bai.json()['ok'],
'dung_thoi_gian': False,
'danh_sach_bai': []
}
else:
danh_sach_bai = yeu_cau_bai.json()['data']['list']
danh_sach_bai_rieng = []
dung_thoi_gian = False
for bai in danh_sach_bai:
bai_don = lay_bai_weibo_don(bai)
thoi_gian = bai_don['thoi_gian_bai']
if thoi_gian >= NAM_THANG:
danh_sach_bai_rieng.append(bai_don)
else:
dung_thoi_gian = True
return {
'so_trang': so_trang,
'ok': 1,
'dung_thoi_gian': dung_thoi_gian,
'danh_sach_bai': danh_sach_bai_rieng
}
# Lấy tất cả bài Weibo trong một tháng.
def lay_weibo_thang(NAM_THANG):
danh_sach_weibo = []
for so_trang in range(1,100):
time.sleep(random.random()*5)
weibo_trang = lay_trang_weibo(so_trang, NAM_THANG)
danh_sach_weibo.append(weibo_trang)
if weibo_trang['dung_thoi_gian'] == True:
break
return danh_sach_weibo
# Tải hình ảnh về thư mục pic, thêm tiền tố "weibo-yyyymm" vào tên file và trả về tên file.
def tai_xuong_hinh(duong_dan):
ten_hinh = 'weibo-' + NAM_THANG.strftime('%Y%m') + '-' + duong_dan.split('?')[0].split('/')[-1]
yeu_cau_hinh = requests.get(duong_dan)
with open('pic/'+ten_hinh, 'wb') as f:
f.write(yeu_cau_hinh.content)
return ten_hinh
# Thay thế các đường dẫn ngắn bằng các đường dẫn gốc.
def thay_the_url(van_ban, cau_truc_url):
for url in cau_truc_url:
md_url='{tieu_de_url}'.format(tieu_de_url=url['url_title'], duong_dan_dai=url['long_url'])
van_ban=van_ban.replace(url['short_url'], md_url)
return van_ban
# Ghi Weibo vào tệp .md và trả về các trang thất bại.
def lam_viec():
weibo_thang = lay_weibo_thang(NAM_THANG)
noi_dung_md = ''
trang_404 = []
for trang in weibo_thang:
print(trang['so_trang'],': ',trang['ok'], '\n')
for bai in trang['danh_sach_bai']:
noi_dung_md = bai['noi_dung_hoan_chinh'] + noi_dung_md
ten_tep = 'Những suy nghĩ nhỏ ({thoi_gian}).md'.format(thoi_gian=NAM_THANG.strftime('%Y%m'))
with open(ten_tep,'w') as f:
f.write(noi_dung_md)
lam_viec()
print('done')
Kịch bản này giúp tôi tiết kiệm rất nhiều thời gian và đảm bảo rằng mọi dữ liệu đều được sao lưu chính xác. Tôi hy vọng nó cũng sẽ hữu ích cho những ai muốn làm điều tương tự!
Sửa đổi lần cuối vào 2025-03-16