• 力扣每日一题每天自动邮件提醒


    A.需求来源与分析
    需求来源于生活,对于只是偶尔有兴趣做做题的我,力扣的每日一题对我一直有以下的不便:

    太简单不想做,需要花太多时间的不想做,每天打开力扣其实只是想看一下是什么题,有意思才做。
    看题需要打开电脑,而且打开电脑也不一定记得要去看看题,要是能把每日一题直接推送到我邮箱里就好了,这样每天起床的时候就能在手机的邮箱里看一看每日一题,如果确实有意思,再打开电脑的时候去做。
    有几天没做后,甚至会忘记力扣的每日一题这件事情,然后就是很长一段时间不会去做题。
    其实有些需求主要是因为我懒(bushi),但是,程序员要学会偷懒!于是我简单整理了一下我的需求:

    每天早上的某个时候(最好是我起床的时候),能把每日一题推送到我的邮箱。
    我能直接通过邮箱看到题目什么难度、考察哪些点、题目的内容,并且能直接点一下就进入题目。
    说干就干,开工!于是花了一点时间把这样一个小玩意儿弄出来了。

    B.技术角度分析
    其实这件事情很简单,我只需要分析出力扣上每日一题的接口,然后写个python脚本把题目信息拿到,然后用smtp协议给我自己发封邮件即可,将这个脚本写入我服务器的crontab上,每天早上就自己跑了。接下来按照这些部分去分析即可。

    C.具体分析步骤
    大致就是:分析接口协议->获取题目信息->写脚本将信息发送到我的邮箱->将这个接口写入crontab。

    1.接口协议分析
    开始之前先讲点武德,我们先看一下力扣的robots.txt,看看哪些是不能爬的:

    然后再去抓个包,看下哪个包最后得到了每日一题的数据:

    发现这个包请求的结果就是每日一题:

    幸运的是,这个接口并没有在上述robots.txt中,我们可以写个脚本模拟一下这个发包,注意到请求头中有csrf-token:

    稍微找一下,可以发现返回包的cookie里面就带有csrf-token,所以我们提前请求一下即可,就可以从cookie中拿到csrf-token了。
    这个请求体,很明显是graphql的请求参数,仔细看一下,发现并不需要传啥参数,所以直接调用即可。

    再去看一下进入题目页面时候的关键请求接口,可以找到是这个接口:

    返回的数据都是json,格式化一下就可以找到关键数据。

    2.发邮件
    此篇若有不清楚的见下面使用说明

    3.写crontab放服务器上定时跑
    每天上午11点自动提醒:(替换成自己的路径)

    crontab –e
    0 11 * * * python3 /home/atfwus/sheduler-script/lc-today-question/lc-day-title.py
    1
    2
    D.成品
    1.源代码
    lc-day-title.py,自动采集每日一题的数据并发邮件提醒:

    import requests,json,time

    session = requests.session()

    lc_url = ‘https://leetcode.cn’
    graphql_url = ‘/graphql’

    def int_csrf():
    session.get(lc_url + graphql_url)

    int_csrf()

    user_agent = r’Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36’
    headers = {
    ‘x-requested-with’ : ‘XMLHttpRequest’,
    ‘accept’ : ‘/’,
    ‘User-Agent’: user_agent,
    ‘Connection’: ‘keep-alive’,
    ‘origin’: ‘https://leetcode.cn’,
    ‘Content-Type’ :‘application/json’,
    ‘X-Csrftoken’: session.cookies[‘csrftoken’]
    }

    def get_today_question():
    param = ‘’’
    query questionOfToday {
    todayRecord {
    date
    userStatus
    question {
    questionId
    frontendQuestionId: questionFrontendId
    difficulty
    title
    titleCn: translatedTitle
    titleSlug
    paidOnly: isPaidOnly
    freqBar
    isFavor
    acRate
    status
    solutionNum
    hasVideoSolution
    topicTags {
    name
    nameTranslated: translatedName
    id
    }
    extra {
    topCompanyTags {
    imgUrl
    slug
    numSubscribed
    }
    }
    }
    lastSubmission {
    id
    }
    }
    }
    ‘’’

    data = {
        "query": param,
        "variables": {}
    }
    
    r = session.post(lc_url+graphql_url, headers=headers, data=json.dumps(data))
    return r.json()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    def get_one_question(title_slug):
    param = ‘’’
    query questionData($titleSlug: String!) {
    question(titleSlug: $titleSlug) {
    questionId
    questionFrontendId
    categoryTitle
    boundTopicId
    title
    titleSlug
    content
    translatedTitle
    translatedContent
    isPaidOnly
    difficulty
    likes
    dislikes
    isLiked
    similarQuestions
    contributors {
    username
    profileUrl
    avatarUrl
    __typename
    }
    langToValidPlayground
    topicTags {
    name
    slug
    translatedName
    __typename
    }
    companyTagStats
    codeSnippets {
    lang
    langSlug
    code
    __typename
    }
    stats
    hints
    solution {
    id
    canSeeDetail
    __typename
    }
    status
    sampleTestCase
    metaData
    judgerAvailable
    judgeType
    mysqlSchemas
    enableRunCode
    envInfo
    book {
    id
    bookName
    pressName
    source
    shortDescription
    fullDescription
    bookImgUrl
    pressImgUrl
    productUrl
    __typename
    }
    isSubscribed
    isDailyQuestion
    dailyRecordStatus
    editorType
    ugcQuestionId
    style
    exampleTestcases
    jsonExampleTestcases
    __typename
    }
    }
    ‘’’
    data = {
    “operationName”: “questionData”,
    “variables”: {
    “titleSlug”: title_slug
    },
    “query”: param
    }
    r = session.post(lc_url + graphql_url, headers=headers, data=json.dumps(data))
    return r.json()

    def send_to_mail(q, sf):
    q = q[‘data’][‘question’]
    id = q[‘questionFrontendId’]
    title = q[‘translatedTitle’]
    url = ‘https://leetcode.cn/problems/’ + q[‘titleSlug’]
    ac_rate = ‘{:.2%}’.format(sf[‘acRate’])
    def generate_subject():
    day_str = time.strftime(‘%m月%#d日’, time.localtime(time.time()))
    return f’力扣{day_str}每日一题来咯!!!({id}.{title})’
    print(generate_subject())

    def generate_plain():
        content = q['translatedContent']
        difficulty = q['difficulty']
        tags = []
        for i in q['topicTags']:
            tags.append(i['translatedName'])
        tags_str = ' '.join(tags)
        return f'''
        题目名称:{id}.{title}     题目难度:{difficulty}     AC率:{ac_rate}     题目链接:{url}
    题目标签:{tags_str}
    {content} '''.strip() print(generate_plain()) from mail import send_mail send_mail(subject=generate_subject(), plain=generate_plain())
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    sf = get_today_question()
    s = get_one_question(sf[‘data’][‘todayRecord’][0][‘question’][‘titleSlug’])

    send_to_mail(s, sf[‘data’][‘todayRecord’][0][‘question’])
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    mail.py,用于发邮件:

    import smtplib
    from email.mime.text import MIMEText
    from email.header import Header

    def init_con():
    # 创建 SMTP 对象
    smtp = smtplib.SMTP()
    # 连接(connect)指定服务器
    smtp.connect(“smtp.qq.com”, port=25)
    # 登录,需要:登录邮箱和授权码
    smtp.login(user=““, password=””)

    return smtp
    
    • 1

    def send_mail(subject, plain):
    smtp = init_con()

    # 构造MIMEText对象,参数为:正文,MIME的subtype,编码方式
    message = MIMEText(plain, 'html', 'utf-8')
    message['From'] = Header("Leetcode 每日一题提醒 By ATFWUS", 'utf-8')  # 发件人的昵称
    message['To'] = Header("ATFWUS", 'utf-8')  # 收件人的昵称
    message['Subject'] = Header(subject, 'utf-8')  # 定义主题内容
    
    smtp.sendmail(from_addr="***", to_addrs="***", msg=message.as_string())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    2.效果
    每日一题来咯!!!

    3.使用说明
    只需要修改mail.py中的邮箱和密码即可,然后将两个py文件放在云服务器上,crontab定时任务自动执行。
    下面是密码的获取方式:
    qq邮箱中,点设置,在这个地方找到授权码,申请授权码,并填在上面mail.py脚本的password上。

  • 相关阅读:
    pandas Series矢量化的字符串函数——Series.str
    【ARM 嵌入式 编译 Makefile 系列 15.1 -- arm gcc 与 Makefile 一块使用示例】
    华为鸿蒙3.0的野望:技术、应用、生态
    低代码平台选型6大能力:品牌/产品/技术/服务/安全/价值
    2023年最新一面二面通关王炸java八股文面试题--持续更新
    给你 2 万条数据,怎么快速导入到 MySQL?写得太好了...
    macos端文件夹快速访问工具 Default Folder X 最新for mac
    ESP8266-Arduino编程实例-VEML6040颜色传感器驱动
    2.14日学习打卡----初学Zookeeper(一)
    wordpress的手工迁移
  • 原文地址:https://blog.csdn.net/m0_71905144/article/details/128196679