前面的文章《Python爬虫 - 01.实现贴吧一键签到》实现了贴吧一键签到,但是贴吧一键签到的功能有很多限制,每天只能一点后才能用,而且一次只能签到50个吧,所以我们换一种思路避开这些限制,实现随时随地签到
一、分析
我们要先获取到关注的贴吧列表信息,然后逐一进行签到
- 获取贴吧关注列表
- 获取贴吧签到的接口信息
- 遍历贴吧列表,发送请求进行签到
二、获取贴吧关注列表
获取贴吧关注列表有几种方法可以实现
- 通过baidu贴吧提供的功能"我的贴吧",从HTML中提取到贴吧列表信息
- 从贴吧首页中去提取关注列表信息
第一种比较麻烦,如果页数比较多发送的请求也相应增多,还需要解析HTML,效率低下,而且信息量没有那么全面
所以我推荐用第二种
打开贴吧首页,按F12
打开浏览器开发者工具,选择网络标签。在贴吧首页中,我们可以看到 爱逛的吧
下面就是你的关注列表,但这个关注列表由于只显示部分吧,剩下的被隐藏了,我们把鼠标放到查看更多
上,剩下的就会显示出来,这里要观察开发者工具中网络的变化,发现并没有发送网络请求,说明这些数据不是通过AJAX
请求拿到的,而是在网页加载的时候就是已经加载到了过来了。
下面我们分别从HTML,JS以及本地存储中查找这个关注列表的信息
首先从HTML中查找,选择查看器,首先定位到这个查看更多弹出的组件,这个组件有个标题是常逛的吧
,按Ctrl+F
搜一下这个标题,有两个结果,第二个就是这个组件,里面的内容包含了隐藏的贴吧列表信息,这样我们可以通过解析HTML拿到关注贴吧的列表。
除了这种解析HTML的方法,是否还有其他方法呢?答案是有,查找JS信息
首先我们拿到一个吧的id,搜data-fid
即可,例如我关注的帝吧(李毅吧),id为59099
,我们在查看器中搜索59099
,会出来3个结果,排除掉HTML标签,剩下两个是包含在script
中的内容,把他们复制出来分析一下
第一个script
中的内容格式化后如下
_.Module.use('spage/widget/forumDirectory',{
"forums": [
{
"user_id": 38947xxx,
"forum_id": 59099,
"forum_name": "\u674e\u6bc5",
"is_like": 0,
"is_black": 0,
"like_num": 0,
"is_top": 0,
"in_time": 1632026524,
"level_id": 10,
"level_name": "\u62a0\u811a\u5927\u6c49",
"cur_score": 2474,
"score_left": 526,
"is_sign": 0
},
//...
//...
],
"directory": {...}
})
第二个script
中的内容如下:
_.Module.use("spage/widget/AsideV2",
[
[
{
"user_id": 38947xxx,
"forum_id": 59099,
"forum_name": "\u674e\u6bc5",
"is_like": 0,
"is_black": 0,
"like_num": 0,
"is_top": 0,
"in_time": 1632026524,
"level_id": 10,
"level_name": "\u62a0\u811a\u5927\u6c49",
"cur_score": 2496,
"score_left": 504,
"is_sign": 1
},
//...
//...
]
])
从上面两段内容,我们可以看出,这脚本中的数据就是贴吧关注列表的信息,包含了贴吧id,名字,经验,是是否签到等信息,只要把这其中的列表提取出来即可,上面两个任意一个都可以,因为他们的数据 是相同的。
三、获取贴吧签到的接口信息
点进一个贴吧,按F12打开开发者工具,然后点击签到就拿到了这个签到接口
接口地址:
https://tieba.baidu.com/sign/add
这是一个POST
请求,带有三个请求参数
{
'ie': 'utf-8',
'kw': 'python',
'tbs': '222b0543ebe394f916324xxxxx'
}
这三个参数分别是编码,贴吧的名字和一个tbs参数,tbs参数上一篇已经说过如何提取,这里就不重复了。
没看过的上一篇文章的朋友可以先去看一下,传送门:《Python爬虫 - 01.实现贴吧一键签到》
tbs参数是可以重复使用的,只要我们拿到一个即可,不用每个请求都要重新拿一次新的值
四、编码实现
下面开始编码实现
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
实现百度贴吧签到
API:
一键签到 POST https://tieba.baidu.com/tbmall/onekeySignin1
逐个签到 POST https://tieba.baidu.com/sign/add
注意事项:
1、百度贴吧0点到1点是不能够使用一键签到的,所以要逐个贴吧进行签到
'''
__author__ = "Conca"
import requests
import re
import time
import json
import random
# 请求头信息,定义为全局方便重用
headers = {
"User-Agent": "Firefox/92.0", # 替换为你的UA
"Referer": "https://tieba.baidu.com/",
"Cookie": "your cookie xxxxxxxxxxxxxx", # 替换为你的cookie
}
# 从HTML中获取tbs参数和贴吧关注列表
def getTiebaInfo():
try:
url = "https://tieba.baidu.com/"
# 发送请求,下载贴吧HTML页面
response = requests.get(url, headers=headers)
response.raise_for_status()
response.encoding = response.apparent_encoding
html = response.text
# 从HTML中提取tbs参数
tbs = getTbs(html)
# 从HTML中提取贴吧关注列表
forums = getAllForums(html)
return {"forums": forums, "tbs": tbs}
except Exception as e:
print("获取贴吧数据失败,原因:", e)
return None
# 用正则表达示从HTML中提取参数 tbs 的值
def getTbs(html):
# 正则表达式学得不太好,用得有点呆板,凑合用
match = re.search(r'PageData.tbs = "(.*)";PageData.is_iPad', html)
if match:
tbs = match.group(0).split('"')[1]
return tbs
return None
# 获取关注的贴吧列表
def getAllForums(html):
# _.Module.use('spage/widget/forumDirectory', {"forums": [...],"directory": {}})
match = re.search(r'{"forums":\[.*\],"directory"', html)
if match:
data = match.group(0)
forums = json.loads(data[data.find('['):data.rfind("]")+1])
return forums
return None
# 逐个吧签到
def tiebaSigninOneByOne(tiebaInfo):
# 签到接口
signin_url = "https://tieba.baidu.com/sign/add"
tbs = tiebaInfo.get("tbs")
# 统计结果
success_count = 0
fail_count = 0
# 签到
for forum in tiebaInfo.get("forums"):
# 跳过已经签到的贴吧,减少请求次数,防止验证码
is_sign = forum.get("is_sign")
if is_sign == 1:
continue
# 构建请求数据
forum_name = forum.get("forum_name")
sigin_data = {
"ie": "utf-8",
"kw": forum_name,
"tbs": tbs,
}
try:
# 发送请求签到
response = requests.post(
url=signin_url, data=sigin_data, headers=headers)
response.raise_for_status()
response.encoding = response.apparent_encoding
content = response.json()
# 判断签到结果,打印消息
if content.get("no") == 0:
success_count += 1
print("{}吧签到成功".format(forum_name))
else:
fail_count += 1
print("{}吧签到失败,失败原因:{}".format(
forum_name, content.get("error")))
except Exception as e:
fail_count += 1
print("Error: {}吧签到发生错误,{}".format(forum_name, e))
# 随机睡眠1-5秒,防止弹验证码,自动化不追求速度,一切求稳
second = random.randint(1, 5)
time.sleep(second)
print("本次签到成功%d个,失败%d个" % (success_count, fail_count))
# 主方法
def main():
print("-----------百度贴吧开始签到-------------")
tiebaInfo = getTiebaInfo()
if tiebaInfo:
tiebaSigninOneByOne(tiebaInfo)
else:
print("签到失败")
print("-----------百度贴吧签到结束-------------")
if __name__ == "__main__":
main()
到此就实现了贴吧签到的功能