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

news2024/11/13 16:02:16

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


文章目录

  • A.需求来源与分析
  • B.技术角度分析
  • C.具体分析步骤
    • 1.接口协议分析
    • 2.发邮件
    • 3.写crontab放服务器上定时跑
  • D.成品
    • 1.源代码
    • 2.效果
    • 3.使用说明
  • 免责申明


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

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'])

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

2.效果

每日一题来咯!!!

请添加图片描述请添加图片描述

3.使用说明

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

在这里插入图片描述


免责申明

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


ATFWUS 2022-12-05

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/64443.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面试官:你先回去等通知吧!这个 Java 岗位我还有机会吗?

面试官&#xff1a;看你简历写的不错&#xff0c;先简单自我介绍下&#xff1f; 我&#xff1a;大佬好&#xff01;我是小程&#xff0c;工作时长两年半&#xff0c;目前负责在公司打杂&#xff0c;啊不&#xff0c;负责维护公司的两个项目… 面试官&#xff1a;hmmm&#xf…

[激光原理与应用-37]:《光电检测技术-4》- 光学测量基础 - 噪声与光学中的常见电路

目录 第1章 噪声 1.1 什么是噪声 1.2 分类 第2章 电子电路的常见噪声 2.1 通过放电产生的噪声 2.2 因为辐射干扰而产生的噪声 2.3 特定器件固有的噪声源 2.4 电子电路中噪声监测办法 第3章 光学电路常见噪声 3.1 常见噪声 3.2 噪声处理 第4章 光学电路中常见的电路…

双指针题目

比较含退格的字符串 给定 s 和 t 两个字符串&#xff0c;当它们分别被输入到空白的文本编辑器后&#xff0c;如果两者相等&#xff0c;返回 true 。# 代表退格字符。 class Solution {public boolean backspaceCompare(String S, String T) {int S_Len S.length(), T_Len T.…

[附源码]计算机毕业设计数字乡村基础治理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

NFV网络云落地过程中若干问题分析

Labs 导读NFV技术从诞生起&#xff0c;从根本上来说就是为了解决运营商网络演进中部署成本高&#xff0c;迭代更新慢&#xff0c;架构僵化等痛点问题。同时&#xff0c;在引入NFV技术前&#xff0c;旧有产业链相对单一&#xff0c;核心成员主要包括设备制造商、芯片制造商等&am…

[Mysql]数据库约束

文章目录前言1. 数据库约束1.1 not null1.2 unique1.3 primary key,主键约束1.4 default,设置默认值1.5 foreign key 外键约束前言 数据库约束,在实际应用中&#xff0c;由于某些特定的要求&#xff0c;例如学生的学号不能为空&#xff0c;学生表中的班级id,在班级表中要能存在…

python足球作画

努力是为了不平庸~ 学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。 足球&#xff08;Football[英]、 Soccer[美]&#xff09;是一项以脚为主&#xff0c;控制和支配球&#xff0c;两支球队按照一定规则在同一块长方形…

HTML如何制作公司网站首页(web前端期末大作业)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

新老用户该如何选择腾讯云服务器!

随着云计算的快速发展&#xff0c;很多用户都选择上云&#xff0c;上运中最常见的产品就是云服务器CVM和轻量应用服务器了&#xff0c;那么怎么选购最优惠呢&#xff0c;这篇文章将介绍新老用户选购腾讯云服务器的几个优惠方法。 一、买赠专区 第一个介绍的就是买赠专区&…

软考高级——系统架构设计师通关宝库

关于报考时间。每年8到9月进行报名&#xff0c;11月考试。一定要关注报名时间&#xff0c;各省有些许差别。系统架构设计师一年只有一次考试机会。 关于考试科目。考试科目分为 综合题&#xff08;选择题案例分析题论文题。 其中综合题只有75道&#xff0c;考试时间两个半小时…

企企通成功入选「亿欧EqualOcean 2022 中国SaaS 50强」榜单!

近日&#xff0c;由EqualOcean全球化智库主办的2022 EqualOcean Summit for Globalization (ESG) 2022 全球化峰会顺利召开,并重磅发布《2022年中国SaaS 50强》榜单。作为行业领先的数字化采购SaaS服务商&#xff0c;企企通凭借在SRM领域的持续创新和深厚的SaaS服务经验成功入选…

CSS栅格布局(Grid)

今天写页面布局&#xff0c;突然想到了栅格布局&#xff0c;以往习惯了弹性布局&#xff0c;然后发现栅格布局有点香&#xff0c;然后就简单的整理了一下&#xff0c;用于学习与分享。 一、什么是栅格布局 可以理解为将一个元素分成行列&#xff0c;然后可以设置对应的大小、布…

接口自动化测试:mock server之Moco工具

什么是mock server mock&#xff1a;英文可以翻译为模仿的&#xff0c;mock server是我们用来解除依赖&#xff08;耦合&#xff09;&#xff0c;假装实现的技术&#xff0c;比如说&#xff0c;前端需要使用某些api进行调试&#xff0c;但是服务端并没有开发完成这些api&#…

机器学习:在SAS中运行随机森林

为了在SAS中运行随机森林&#xff0c;我们必须使用PROC HPFOREST指定目标变量&#xff0c;并说明天气变量是“类别”还是“定量”。 最近我们被客户要求撰写关于随机森林的研究报告&#xff0c;包括一些图形和统计输出。为了进行此分析&#xff0c;我们使用了目标&#xff08;…

Kamiya艾美捷抗胸腺嘧啶二聚体单抗(环丁烷嘧啶二聚体CPD)说明书

Kamiya艾美捷抗胸腺嘧啶二聚体单抗相关性质&#xff1a; 同义词&#xff1a;环丁烷嘧啶二聚体&#xff08;CPD&#xff09; 特异性&#xff1a;与由以下物质产生的胸腺嘧啶二聚体发生特异性反应&#xff1a;双链或单链DNA的紫外线照射。不与&#xff08;6-4&#xff09;照片产…

基于安卓的校园信息助手系统设计(Eclipse开发)

使用说明 1.1 软件的安装 将.api文件安装到iphone手机上&#xff0c;点击图标即可使用。 2.2 软件的使用 2.2.1 初始界面 软件安装好之后&#xff0c;在手机上显示初始界面。 2.2.2 程序主界面 主要有【课程表模块】、【新闻模块】、【学校概况模块】、【黄页模块】、【考生问答…

程序员的刻板印象,都是真的吗?

自从当了程序员&#xff0c;身边人对于我的职业一直好奇不断&#xff0c;刚好看到网上大家的刻板印象&#xff0c;整理几个最常见的问题&#xff0c;实事求是地解答一下&#xff01; “青春饭、35岁危机、会修电脑、年薪10w、还有戴眼镜、格子衫、发际线高” 这些大家都在网上见…

8-事件组或标志

1-事件位&#xff08;标志&#xff09; 事件位用于指示事件是否发生。事件位通常称为事件标志。例如&#xff0c;一个应用程序可以&#xff1a; 定义一个标志&#xff0c;当为1时&#xff0c;表示消息已经接收并进行处理&#xff0c;当为0时&#xff0c;表示没有消息要处理。…

【王道计算机网络笔记】数据链路层-数据链路层的功能

文章目录数据链路层的研究思想数据链路层基本概念数据链路层功能概述为网络层提供服务链路管理帧定界、帧同步与透明传输&#xff08;组帧&#xff09;封装成帧透明传输组帧方法字符计数法字符填充法零比特填充法违规编码法流量控制停止-等待协议停等协议-无差错情况停等协议-有…

es搜索功能——DSL查询文档——DSL基本语法

1、查询的基本语法 # GET请求方式&#xff08;固定写法&#xff09; # indexName 要查询的索引库 # _search 查询语句的固定格式 GET /indexName/_search {"query": {"查询类型": {"查询条件": "条件值"}} } 2、无条件查询&#xff…