目标
- 获取微博热榜
- 获取微博博文
- 获取博文评论
实践
使用 Python 爬虫进行词频统计,分析网友评论倾向。
- 获取某一条博文的所有评论
- 分词、统计词频
- 制作词云
流程与分析
获取 XSRF-TOKEN
需要先获取 XSRF-TOKEN
,请求如下接口即可:(这一步不定需要,但是前端每一部请求都携带有 XSRF-TOKEN 所以还是要获取一下)
GET https://weibo.com/newlogin
参数:
params = { 'tabtype': 'weibo', 'gid': '102803', # 貌似是固定的 'openLoginLayer': '0', 'url': 'https://weibo.com/', }
热榜请求
URL:GET https://weibo.com/ajax/side/hotSearch
不需要任何验证,直接可以请求。
获取推荐博文
URL:GET https://weibo.com/ajax/feed/hottimeline
参数:
params = { 'since_id': '0', 'refresh': '0', 'group_id': '102803', 'containerid': '102803', 'extparam': 'discover|new_feed', 'max_id': '0', 'count': '10', }
如果没有进行游客验证会被重定向到:GET https://passport.weibo.com/visitor/visitor 进行游客验证。
参数:
params = { 'entry': 'miniblog', 'a': 'enter', 'url': '' # 自定义来时的链接 }
经过测试发现不需要先访问这个地址,可以直接去游客验证请求
游客验证请求(获取门票):
POST https://passport.weibo.com/visitor/genvisitor2
参数:
params = { 'cb': 'visitor_gray_callback', 'tid': '', # 可以不填 'from': 'weibo', }
服务器会响应类似如下的内容:
window.visitor_gray_callback && visitor_gray_callback({"retcode":20000000,"msg":"succ","data": {"next":"cross_domain","sub":"_2AkMR3yxzf8NxqwFRmfwTyWLmZIhzzAjEieKng92oJRMxHRl-yj9kqhQItRB6Ol8CnIdwUDcyy_MV5xEsWRW6mxQNIM5P", "subp":"0033WrSXqPxfM72-Ws9jqgMF55529P9D9WhKudzL7vNo6rVgM_I8.Nfw","alt":"","savestate":0, "tid":"01Aa9SQlUD5uH1PaX5dB4hUTYf1vHk_1S3H6bnpEw2bN1p","new_tid":false,"confidence":90}});
我们需要用到其中的几个参数:sub
、subp
。可以使用正则表达式匹配。
然后用如上的参数重新去请求:
GET https://login.sina.com.cn/visitor/visitor
params = { 'a': 'crossdomain', # 踩的坑:`a` 参数是固定 `crossdomain` 而不是服务器传来的 `next` 参数值。 's': sub, 'sp': subp, 'from': 'weibo', '_rand': random.random(), 'entry': 'miniblog', 'url': 'https://weibo.com/ajax/feed/hottimeline?since_id=0&refresh=0&group_id=102803&containerid=102803&extparam=discover%7Cnew_feed&max_id=0&count=10', }
触发 302 跳转则代表验证通过。
获取长文本
GET https://weibo.com/ajax/statuses/longtext
参数:
params = { 'id': '' # 博文 mblogid }
扫码登录
- 前置验证
请求 GET https://passport.weibo.com/sso/signin 获取X-CSRF-TOKEN
参数:
params = { 'entry': 'miniblog', 'source': 'miniblog', 'disp': 'popup', 'url': 'https://weibo.com/newlogin?tabtype=weibo&gid=102803&openLoginLayer=0&url=https%3A%2F%2Fweibo.com%2F', # 重定向 }
- 获取二维码门票
请求 GET https://passport.weibo.com/sso/v2/qrcode/image
参数:
params = { 'entry': 'miniblog', 'size': '180', }
请求标头带上 X-Csrf-Token
headers = { 'Referer': 'https://passport.weibo.com/sso/signin?entry=miniblog&source=miniblog&disp=popup&url=https%3A%2F%2Fweibo.com' }
需要先获取 X-Csrf-Token
。
响应:
{ "retcode": 20000000, "msg": "succ", "data": { "qrid": "2MmZmg8E_AAM_bZzVmsg0CUhy_3DqSvEHBnFyY29kZQ..", "image": "https:\/\/v2.qr.weibo.cn\/inf\/gen?api_key=a0241ed0d922e7286303ea5818292a76&data=https%3A%2F%2Fpassport.weibo.cn%2Fsignin%2Fqrcode%2Fscan%3Fqr%3D2MmZmg8E_AAM_bZzVmsg0CUhy_3DqSvEHBnFyY29kZQ..%26sinainternalbrowser%3Dtopnav%26showmenu%3D0&datetime=1719910719&deadline=0&level=M&logo=https%3A%2F%2Fimg.t.sinajs.cn%2Ft6%2Fstyle%2Fimages%2Findex%2Fweibo-logo.png&output_type=img&redirect=0&sign=75306149231bc90427b64f08148a6123&size=180&start_time=0&title=sso&type=url" } }
- 获取二维码图片(生成二维码图片)
这个其实就是上一个接口的 image 数据,格式为 png 。
请求 GET https://v2.qr.weibo.cn/inf/gen
参数:
params = { 'api_key': 'a0241ed0d922e7286303ea5818292a76', 'data': 'https://passport.weibo.cn/signin/qrcode/scan?qr=2MmZmg8E_AAM_bZzVmsg0CUhy_3DqSvEHBnFyY29kZQ..&sinainternalbrowser=topnav&showmenu=0', 'datetime': '1719910719', 'deadline': '0', 'level': 'M', 'logo': 'https://img.t.sinajs.cn/t6/style/images/index/weibo-logo.png', 'output_type': 'img', 'redirect': '0', 'sign': '75306149231bc90427b64f08148a6123', 'size': '180', 'start_time': '0', 'title': 'sso', 'type': 'url', }
- 巡回查码
请求 GET https://passport.weibo.com/sso/v2/qrcode/check
参数:
params = { 'entry': 'miniblog', 'source': 'miniblog', 'url': 'https://weibo.com/newlogin?tabtype=weibo&gid=102803&openLoginLayer=0&url=https%3A%2F%2Fweibo.com%2F', # 重定向地址 'qrid': '2MmZmg8E_AAM_bZzVmsg0CUhy_3DqSvEHBnFyY29kZQ..', # 二维码id 'disp': 'popup', }
未扫描情况响应:
{ "retcode": 50114001, "msg": "未使用", "data": null }
已扫描情况响应:
{ "retcode": 20000000, "msg": "succ", "data": { "url": "跳转URL" } }
请求标头带上 X-Csrf-Token
headers = { 'Referer': 'https://passport.weibo.com/sso/signin?entry=miniblog&source=miniblog&disp=popup&url=https%3A%2F%2Fweibo.com' }
- 验证登录的方法
请求 GET https://weibo.com/ajax/log/action
查看返回标头是否含有 X-Log-Uid
如果有则是用户ID。
获取评论
请求 GET https://weibo.com/ajax/statuses/buildComments 获取评论。
参数:
params = { 'flow': '0', # 按热度,1为按时间 'is_reload': '1', 'id': '博文数字ID', 'is_show_bulletin': '2', 'is_mix': '0', 'count': '几个', 'uid': '发文博主ID', 'fetch_level': '0', 'locale': 'zh-CN', 'max_id': '' # 用于下一趟加载评论,会这个值第一次请求这个接口时会提供,为0则已经获取全部 }
模块使用例子
from weibo import Weibo wb = Weibo()
游客登录
wb.login_as_visitor() print(wb.get_hottimeline())
扫码登录
import time qrcode, data = wb.get_login_qrcode() login = wb.check_qrcode(qrcode) count = 0 while not login: time.sleep(1) login = wb.check_qrcode(qrcode) print(f"\r等待扫码登录......({count}s)", end="") count += 1 print("\r", end="") print("登录成功。")
获取动态
# 获取用户动态 data = wb.get_mymblog(uid=7467555881, page=0) since_id = data['since_id'] for blog in data['list']: print("-" * 150) print("发布者:", blog['user']['screen_name']) print("发布时间:", blog['created_at']) print("发布内容:\n" + blog['text_raw']) print("博文ID:", blog['idstr'])
获取博文
blog = wb.get_status("Nw310uTcQ") print("发布者:", blog['user']['screen_name']) print("博文发布时间:", blog['created_at']) print("发布内容:\n" + blog['text_raw']) print("博文数字ID:", blog['idstr'])
获取评论
info = wb.get_comments(id=blog['idstr'], user_id=blog['user']['id'], count=10) i = 0 while info['max_id'] != 0: for comment in info['data']: i += 1 print(f"\r计数:{i}", comment['user']['screen_name'] + ":" + comment['text_raw'], end="") print(comment['text_raw'], file=comment_file, flush=True) time.sleep(0.5) info = wb.get_comments(id=blog['idstr'], user_id=blog['user']['id'], max_id=info['max_id'], count=10)
获取热榜
for realtime in wb.get_hot_search()['realtime']: print(realtime['word'])