分析页面
首先所有的自动化都是拟人操作,只需要模拟出正常的签到步骤并定时重复即可满足需求。
获取登录状态
- 签到总要知道是哪个用户签的嘛,所以所有的请求都要带上用户登录标识,也就是在header中添加对应的cookie
- 如何获取cookie呢,这里看了下掘金的登录请求还比较麻烦,对熟悉爬虫的同学可能小菜一碟,对后端来说虽然能做但是本着投入时间与收益的原则,我毅然决然的选择绕过登录,直接拦截请求把request header的cookie复制出来。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hrACs4W2-1665316799526)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/88660b222a284cc18302a4679346a624~tplv-k3u1fbpfcp-zoom-1.image “image.png”)]
处理cookie失效
- 没有登录逻辑的话等cookie失效签到请求会返回请登录,这里话通过邮箱提醒更换cookie也是可以接受的
处理https请求
- 随便从网上找一个http方法即可
落地方案
为了快速满足直接用最熟悉的Java+SpringBoot撸出来个jar包,打开IDEA创建SpringBoot脚手架
创建脚手架
pom里直接引入springboot-web、springboot-mail、lombok、fastjson,版本管理直接继承下spring-boot-dependencies。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.3</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
</dependencies>
启动类上添加@EnableScheduling注解开启定时任务
@EnableScheduling
@SpringBootApplication
public class SignApplication {
public static void main(String[] args) {
SpringApplication.run(SignApplication.class, args);
}
}
整体的骨架就这么简单。
通用http方法
public static String commonReuqest(String url, String method, String cookie) throws Exception {
URL serverUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();
conn.setRequestMethod(method);
conn.setRequestProperty("Content-type", "application/json");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36");
conn.setRequestProperty("Cookie", cookie);
//必须设置false,否则会自动redirect到重定向后的地址
conn.setInstanceFollowRedirects(false);
conn.connect();
String result = getReturn(conn);
return result;
}
/*请求url获取返回的内容*/
public static String getReturn(HttpURLConnection connection) throws IOException {
StringBuffer buffer = new StringBuffer();
//将返回的输入流转换成字符串
try (InputStream inputStream = connection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);) {
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
String result = buffer.toString();
return result;
}
}
封装成一个util类,入口加上cookie即可,这里直接从网上找了个方法。
邮箱逻辑
- 如何开启smtp邮箱服务就不说了,这里直接贴一下教程https://blog.csdn.net/weixin_46822367/article/details/123893527
- yml配置里需要配置上必要信息,授权码按照上述博客中的步骤获取。
spring:
mail:
host: smtp.qq.com
#发送者邮箱
username:
#申请到的授权码
password:
#端口号465或587
port: 587
#默认的邮件编码为UTF-8
default-encoding: UTF-8
#其他参数
properties:
mail:
#配置SSL 加密工厂
smtp:
ssl:
#本地测试,先放开ssl
enable: false
required: false
#开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
debug: true
- 邮箱业务类
@Service
@Slf4j
public class SendMailServiceImpl implements SendMailService {
@Autowired
private JavaMailSender javaMailSender;
@Value("${spring.mail.username}")
private String sendMailer;
public void checkMail(MailRequest mailRequest) {
Assert.notNull(mailRequest,"邮件请求不能为空");
Assert.notNull(mailRequest.getSendTo(), "邮件收件人不能为空");
Assert.notNull(mailRequest.getSubject(), "邮件主题不能为空");
Assert.notNull(mailRequest.getText(), "邮件收件人不能为空");
}
@Override
public void sendMail(MailRequest mailRequest) {
SimpleMailMessage message = new SimpleMailMessage();
checkMail(mailRequest);
//邮件发件人
message.setFrom(sendMailer);
//邮件收件人 1或多个
message.setTo(mailRequest.getSendTo().split(","));
//邮件主题
message.setSubject(mailRequest.getSubject());
//邮件内容
message.setText(mailRequest.getText());
//邮件发送时间
message.setSentDate(new Date());
// JavaMailSender javaMailSender = new JavaMailSenderImpl();
javaMailSender.send(message);
log.info("发送邮件成功:{}->{}",sendMailer,mailRequest.getSendTo());
}
}
JavaMailSender为spring-boot-starter-mail依赖中封装好的业务类,yml中添加对应的配置该类会被注入到容器中。
签到请求
@Slf4j
@Service
public class ActionService {
//签到
public static final String CHECK_IN = "https://api.juejin.cn/growth_api/v1/check_in";
//抽奖
public static final String DRAW = "https://api.juejin.cn/growth_api/v1/lottery/draw";
@Value("${spring.mail.username}")
private String username;
@Autowired
SendMailService sendMailService;
/**
* 签到
*/
public void checkIn(String cookie) throws Exception {
String response = BaseRequest.commonReuqest(CHECK_IN, "POST", cookie);
log.info("get result from juejin {}", response);
Map<String, Object> resultMap = JSONObject.parseObject(response, Map.class);
if((Integer) resultMap.get("err_no") != 0){
log.error((String) resultMap.get("err_msg"));
// 推送失败消息
MailRequest mailRequest = new MailRequest();
mailRequest.setText("掘金签到失败!err_msg: " + resultMap.get("err_msg"));
mailRequest.setSendTo(username);
mailRequest.setSubject("juejin sign");
sendMailService.sendMail(mailRequest);
}
}
/**
* 抽奖
*/
public void draw(String cookie) throws Exception {
String response = BaseRequest.commonReuqest(DRAW, "POST", cookie);
DrawResponce data= JSON.parseObject(response,new TypeReference<DrawResponce>(){});
log.info(response);
}
}
签到请求和抽奖请求都很简单没有参数,带上cookie发一下矿石就到账了,每天第一次抽奖不花矿石,默认第一次抽一下。签到成功就不发邮箱了发多了烦,签到失败会把失败原因发送到配置的邮箱内。
实体类
@NoArgsConstructor
@Data
public class DrawResponce {
@JsonProperty("err_no")
private Integer errNo;
@JsonProperty("err_msg")
private String errMsg;
@JsonProperty("data")
private DataDTO data;
@NoArgsConstructor
@Data
public static class DataDTO {
@JsonProperty("id")
private Integer id;
@JsonProperty("lottery_id")
private String lotteryId;
@JsonProperty("lottery_name")
private String lotteryName;
@JsonProperty("lottery_type")
private Integer lotteryType;
@JsonProperty("lottery_image")
private String lotteryImage;
@JsonProperty("history_id")
private String historyId;
}
}
@Data
public class MailRequest implements Serializable {
/**
* 接收人
*/
private String sendTo;
/**
* 邮件主题
*/
private String subject;
/**
* 邮件内容
*/
private String text;
/**
* 附件路径
*/
private String filePath;
}
@Configuration
@ConfigurationProperties(prefix = "uptown")
@Data
public class Users {
List<String> cookies;
}
定时任务
@Slf4j
@Component
public class task {
@Value("${uptown.cookie}")
String cookie;
@Resource
ActionService actionService;
@Scheduled(cron = "0 0 5 * * ?")
public void run() throws Exception {
log.info("{} start!", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
this.actionService.checkIn(cookie);
// 第一次免费抽奖
this.actionService.draw(cookie);
}
}
最后加上定时任务就完事了,每天5点签到,签到失败发邮箱提醒。用maven打个包放到服务器上nohup java -jar *.jar &
只需要半小时就可以往后只需要通过邮箱提醒处理cookie失效或失败的情况。