# 缘由

简单和闲得蛋疼,同时随大流,因为网上有很多领京豆的文章和所谓源码,要么就是所谓 XX 面板和 GitHub Action 之类的,要么就是成本很高的之类的。这些可惜我几乎没运行成功过,所以我决定搜一下简单易懂的代码来实现薅京东豆子羊毛的功能。

# 功能

  • 30 天自动签到领京豆
  • 每天邮件提醒情况

# 实现

# 自动领京豆

实际就是单纯用 python 的 request 模块来实现的,没啥含金量。但要注意的是, cookies 里的 pt_key 需要每一个月手动更换一次。这个我也很无奈,暂时想不到很好的解决方法。同时我把 response.text 的文本进行字符串匹配,我发现可以通过文本中的签到成功、签到失败、今天已签到、用户未登录来简单判断 cookies 是否过期。

领京豆函数
def _check_in(pt_key):
    url = "https://api.m.jd.com/client.action?functionId=signBeanAct&body=%7B%22fp%22%3A%22-1%22%2C%22shshshfp%22%3A%22-1%22%2C%22shshshfpa%22%3A%22-1%22%2C%22referUrl%22%3A%22-1%22%2C%22userAgent%22%3A%22-1%22%2C%22jda%22%3A%22-1%22%2C%22rnVersion%22%3A%223.9%22%7D&appid=ld&client=apple&clientVersion=10.0.4&networkType=wifi&osVersion=14.8.1&uuid=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6&openudid=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6&jsonp=jsonp_1645885800574_58482";
    headers  = {"Connection":'keep-alive',
                "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
                "Cache-Control": 'no-cache',
                "User-Agent": "okhttp/3.12.1;jdmall;android;version/10.3.4;build/92451;",
                "accept": "*/*",
                "connection": "Keep-Alive",
                "Accept-Encoding": "gzip,deflate",
                # you only replace pt_key after 30day
                "Cookie": "__jd_ref_cls=JingDou_SceneHome_NewGuidExpo;\
                        mba_muid=1645885780097318205272.81.1645885790055;\
                        mba_sid=81.5;\
                        __jda=122270672.1645885780097318205272.1645885780.1645885780.1645885780.1;\
                        __jdb=122270672.1.1645885780097318205272|1.1645885780; __jdc=122270672;\
                        __jdv=122270672%7Ckong%7Ct_1000170135%7Ctuiguang%7Cnotset%7C1644027879157;\
                        pre_seq=0;\
                        pre_session=3acd1f6361f86fc0a1bc23971b2e7bbe6197afb6|143;\
                        unpl=JF8EAKZnNSttWRkDURtVThUWHAgEWw1dH0dXOjMMAFVcTQQAEwZORxR7XlVdXhRKFx9sZhRUX1NIVw4YBCsiEEpcV1ZVC0kVAV9XNVddaEpkBRwAExEZQ1lWW1kMTBcEaWcAUVpeS1c1KwUbGyB7bVFeXAlOFQJobwxkXGhJVQQZBR0UFU1bZBUzCQYXBG1vBl1VXElRAR8FGxUWS1hRWVsISCcBb2cHUm1b%7CV2_ZzNtbRYAFxd9DUNcKRxYB2ILGloRUUYcIVpAAHsbWQZjVBEJclRCFnUUR11nGlgUZgIZXkFcQRRFCEJkexhdB24LFFtEUHMQfQ5GXH0pXAQJbRZeLAcCVEULRmR6KV5VNVYSCkVVRBUiAUEDKRgMBTRREV9KUUNGdlxAByhNWwVvBUIKEVBzJXwJdlR6GF0GZAoUWUdRQCUpUBkCJE0ZWTVcIlxyVnMURUooDytAGlU1Vl9fEgUWFSIPRFN7TlUCMFETDUIEERZ3AEBUKBoIAzRQRlpCX0VFIltBZHopXA%253d%253d; \
                        pt_key=xxx;\
                        pt_pin=jd_IyPtvUHFxSit;\
                        pwdt_id=jd_505bacd333f6b;\
                        sid=1b2c8b7ce820c4188f048e689bf58c8w;\
                        visitkey=36446698972455355"
                }
    
    headers["Cookie"] = replace_cookie_spaces(headers["Cookie"])
    cookie = cookie_to_dict(headers["Cookie"])
    cookie["pt_key"] = pt_key
    headers["Cookie"] = dict_to_cookie(cookie)
    response = requests.post(url=url, headers=headers)
    print(response.text)
    matchs = re.search(r'签到成功|签到失败|今天已签到|用户未登录', response.text, re.DOTALL)
    # print(matchs)
    return matchs.group(0)

目前已经把 pt_key 抽离出来了,而且新增了用户未登录的判断条件来判断 cookies 是否过期

# pt_key 抽离原理

Cookies 是字符串,形式如下

key1=value1;key2=value2;...etckey1=value1;key2=value2;...etc

直接从字符串内部修改 pt_key 是不太方便的,但是字符串可以使用字典推导式转化成字典,字典格式如下

{key1:value1,key2:value2...etc}\left \{ 'key1': 'value1', 'key2': 'value2'...etc\right \}

然后就可以修改 pt_key ,再次转化为 Cookies 字符串。但是对于我的程序有几个注意点。

  1. 最开始的 Cookies 字符串需要去除多余的空格.
  2. 再次由字典转化为字符串时需要做额外的处理.
    比如字典转化的字符串仍然是字典的格式

{key1:value1,key2:value2...etc}\left \{ 'key1': 'value1', 'key2': 'value2'...etc\right \}

而我们想要的是 Cookies 字符串的格式

key1=value1;key2=value2;...etckey1=value1;key2=value2;...etc

所以要做的处理如下,对于 {} 、空格、 '' 这些要去除,而 : 要替换成 = , , 要替换成 ; 。这些可以用 re.sub 函数解决。

# 邮件提醒

其实也是抄的,原理直接引用原文其中的话

Python 内置对 SMTP 的支持,可以发送纯文本邮件、HTML 邮件以及带附件的邮件。Python 对 SMTP 支持有 smtplib 和 email 两个模块,email 负责构造邮件,smtplib 负责发送邮件。

邮件提醒函数
def _format_addr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))
def _send_message(form_addr, password, to_addr, smpt_server, matchs):
    # 设置邮件信息
    if matchs == '签到成功' or matchs == '今天已签到':
        msg = MIMEText('JD Douzi automatically signed in successfully.', 'plain', 'utf-8')
    else:
        msg = MIMEText('Your pt_key has expired.', 'plain', 'utf-8')
    # 设置收发邮箱
    msg['From'] = _format_addr('Server <%s>' %form_addr)
    msg['To'] = _format_addr('administrator <%s>' %to_addr)
    msg['Subject'] = Header('Jingdong Douzi automatic sign-in situation', charset='utf-8').encode()
    # 发送邮件
    server = smtplib.SMTP(smpt_server, port=25)
    server.set_debuglevel(1)
    server.login(form_addr, password)
    server.sendmail(form_addr, [to_addr], msg.as_string())
    server.quit()

其中_send_message 函数中:
orm_addr : 发件人的邮箱。
password : 开启 SMTP 服务时的授权码。
to_addr : 收件人的邮箱。
smpt_server : 邮箱的服务器地址。
matchs : cookies 的检测状态。

# 缺陷与仓库

  • 隐私性没处理好
  • cookies 不能自动更新

github 仓库戳这里

# 参考

Python 爬虫之 Email 提醒 - 简书
Python - 网页转义字符及正则全文匹配 - 阿里云开发者社区
Python 正则表达式 或者 (|) 圆括号_houyanhua1 的博客 - CSDN 博客_(|
Python main () 函数 - 菜鸟教程
Python 运算符 | 菜鸟教程
【Python 教程】用字典推导式将 cookie 字符串转化为字典的方法
一日一技:re.sub 第二个参数使用函数