Python模拟登录百度贴吧(requests)


写在前面

这两天本打算做两件事,其一是仅利用BDUSS实现模拟登录百度,再进一步请求到贴吧首页,其二是关于朴素贝叶斯分类器的,拉普拉斯平滑是很重要的内容,需要对文章进行补充。但第一件事就花了两天,而且得到的结果与预期并不相符,暂且记录一下过程。拉普拉斯平滑近期会补充。

思路分析

前几天写了贴吧自动灌水脚本,是用完整的cookie请求的,而我想用最少的信息做成这件事,把cookie精简再精简,是否可以只利用某一字段就实现呢?目前未探究这个问题,而选择了另外一个方向,我尝试使用Cookie中的BDUSS字段请求贴吧首页,并不能返回登录状态的信息,退而求其次,请求百度首页,这一次在返回的信息中找到了用户名,也就是说,登录成功了。


接下来打开开发者工具,把网页从百度首页转向贴吧首页,可以看到有三次302重定向,重定向的过程中生成了一个贴吧需要的“令牌”,将其写进了贴吧cookie中,这个“令牌”是STOKEN字段,用包含BDUSS和STOKEN两个字段的cookie重新请求贴吧首页,登录成功,返回的信息中有我关注的贴吧。


那这个STOKEN字段是怎么生成的呢?使用浏览器登录百度(非贴吧首页)的时候,百度会把请求发送到passport.baidu.com,即百度的账号中心,它会生成PTOKEN和STOKEN,而获取STOKEN的关键就是PTOKEN,我猜PTOKEN是md5加密的32位小写密文,明文的规则没有在百度的js文件中找到,这种东西应该被不起眼的变量名伪装保护了。好了,啰里啰嗦一大段,并没有图片可以放上来....

主要代码

用到的包和全局变量

#用到的包
import requests
import time
from bs4 import BeautifulSoup as bs
import logging

#日志相关
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
#请求头
headers_sign = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36",
    }
#从这个接口中获取关注的吧
Attention_URL = "http://tieba.baidu.com/f/like/mylike"
#利用此接口进行签到
sign_url = 'https://tieba.baidu.com/sign/add'

登录模块

def login():
    #贴吧首页
    url = "https://tieba.baidu.com/index.html"
    #在这里写上你的BDUSS
    BDUSS = [
        "",
        ]
    #在这里写上PTOKEN
    PTOKEN = [
        ""
        ]
    #构建Cookie
    login_cookie = {
        "BDUSS": BDUSS[0],
        "PTOKEN": PTOKEN[0],
        }
    #向贴吧首页发起GET请求,并禁止重定向
    req = requests.get(url = url,headers = headers_sign,cookies = login_cookie,allow_redirects = False)
    #从反馈的信息中对Cookie进行补齐
    for key in req.cookies.keys():
        login_cookie[key] = req.cookies[key]
    #虽然上面禁止了重定向,但是status_code仍然是302,如果是200那说明BDUSS过期了
    if req.status_code == 302:
        #重定向的目标地址
        pass_url = req.headers['Location']
        #print(pass_url)  #可以取消注释看看重定向的地址
        #对目标地址发起请求,这里仍然会重定向
        req_pass = requests.get(url = pass_url,headers = headers_sign,cookies = login_cookie,allow_redirects = False)
        #获取重定向的地址
        stoken_url = req_pass.headers['Location']
        #print(stoken_url)
        #访问目标地址,获取STOKEN
        req_stoken = requests.get(url = stoken_url,headers = headers_sign,cookies = login_cookie,allow_redirects = False)
        try:
            #将STOKEN写入login_cookie
            login_cookie["STOKEN"] = req_stoken.cookies['STOKEN']
            #print(login_cookie['STOKEN'])
            #带上login_cookie去签到
            run(login_cookie)
        except:
            pass
    else:
        print(req.status_code)

其他函数

#签到模块
def client_sign(kw,Cookie):
    #post请求需要的参数,kw指吧名
    data = {"ie":"utf-8","kw":kw}
    #带上参数对接口发起post请求,并将response处理成json格式
    res = requests.post(url=sign_url, data=data, cookies = Cookie,timeout=5).json()
    #检查签到状态
    if res['no'] == 0:
        logger.info(kw + ',' + '签到成功!')
    else:
        logger.info(kw + ',' + res['error'])

#获取关注的吧
def get_favorite(Cookie):
    #Attention_URL算是一个接口,在i贴吧查看关注的吧时可以看到
    req = requests.get(url = Attention_URL,headers=headers_sign,cookies = Cookie)
    #调用bs4解析网页,需要预先安装lxml模块
    html = bs(req.text,'lxml')
    #从网页中检索关注的吧
    tieba_lists = html.find('div',class_ = "forum_table").find_all("tr")[1:]
    #将关注的吧存入列表并返回
    myfavorite = []
    for tieba in tieba_lists:
        myfavorite.append(tieba.find('a',class_ = None).get('title'))
    return myfavorite

#控制函数
def run(Cookie):
    #获取关注的吧
    favorites = get_favorite(Cookie)
    #逐一签到,并停顿0.1秒
    for favorite in favorites:
        client_sign(favorite,Cookie)
        time.sleep(0.1)

入口函数

if __name__ == '__main__':
    login()

信息获取

关于BDUSS和PTOKEN的获取,首先打开Chrome,清理掉baidu.com下的所有cookie,重新登录百度。转到谷歌设置-隐私设置和安全性-cookie及其他网站数据-查看所有cookie和网站数据,分别检索passport.baidu.com和baidu.com,可以找到PTOKEN,STOKEN以及BDUSS。也可以从开发者工具里面获取,但是获取PTOKEN需要请求一次贴吧首页,从重定向的地址里面找。


经过多次尝试,我觉得STOKEN是由PTOKEN和BDUSS共同决定的,后面两者不变它就不变。BDUSS是根据用户信息得到的,这个算是用户标识符,虽然它也是可变的,但只要不退出登录,很久都不会过期。PTOKEN也是可变的,跟BDUSS一样,退出登录的时候就会过期,所谓退出登录是指在百度首页点击退出登录按钮,或者使用以下方法:

#参数提供BDUSS就可以了
def logout(bduss = None):
    if bduss:
        cookie = {'BDUSS':bduss}
        url = 'https://passport.baidu.com/?logout&u='
        requests.post(url = url,headers = headers_sign,cookies = cookie)
        print('该BDUSS已作废')

写在后面

如果能找到PTOKEN的生成规则我会再来更新的!

第一次更新

对单独贴吧签到的话,只需要BDUSS就够了,获取关注的吧还是需要STOKEN

import requests

headers_sign = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36",
    }
sign_url = 'https://tieba.baidu.com/sign/add'

def client_sign(kw,Cookie):
    data = {"ie":"utf-8","kw":kw}
    res = requests.post(url=sign_url, data=data, cookies = Cookie,timeout=5).json()
    if res['no'] == 0:
        print(kw + ',' + '签到成功!')
    else:
        print(kw + ',' + res['error'])

if __name__ == '__main__':
    kw = ''#吧名
    Cookie = {'BDUSS':''} #写入你的BDUSS
    client_sign(kw,Cookie)