当前位置:   article > 正文

【爬虫】力扣每日一题每天自动邮件提醒!!!_python 爬取leetcode 每日一题

python 爬取leetcode 每日一题

使用python实现了一个力扣每日一题每天自动邮件提醒的小爬虫,小但实用!!!



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()

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}&nbsp&nbsp&nbsp&nbsp&nbsp题目难度:<strong>{difficulty}</strong>&nbsp&nbsp&nbsp&nbsp&nbspAC率:{ac_rate}&nbsp&nbsp&nbsp&nbsp&nbsp题目链接:<a href="{url}">{url}</a><br>
        题目标签:{tags_str}<br>
        {content}
        '''.strip()
    print(generate_plain())

    from mail import send_mail
    send_mail(subject=generate_subject(), plain=generate_plain())

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

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
  • 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上。

在这里插入图片描述


免责申明

本文仅供技术交流学习使用,严禁使用于任何商业用途,若有任何侵权行为请联系我删除!


ATFWUS 2022-12-05

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/870240
推荐阅读
相关标签
  

闽ICP备14008679号