监听mac价格变动,降价了就通知。

场景

想买二手mac,但又需要他降价,给自己一个安慰说是降价买的,其实还是那么贵。

第一步,确认接口与参数,

确认价格获取接口地址,mac找了半天,发现他是通过url直接拼接参数的,而且还很独特,具体的自己试着点两下。
我是选择了条件后再从url复制地址的,如果当前无法选择你所需要的配置,你也可以先自己在url里面拼接上具体条件。
https://www.apple.com.cn/cn-k12/shop/refurbished/mac/1tb-13-英寸-macbook-air-macbook-pro-16gb
image

第二步,确认返回数据格式

他这个数据是直接在script脚本里面声明一个全局变量,后面页面渲染应该是从这个window变量渲染的,我们不管他怎么渲染的,直接从这里拿到数据就行
image
image

第三步,写代码

这个就简单了,简单的一个页面爬取,获取数据,添加满足就发邮件通知自己。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <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>
        <!--httpclient引用 用于辅助配置restTemplate连接池-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>
        <!--WebMagic引用-->
        <dependency>
            <groupId>us.codecraft</groupId>
            <artifactId>webmagic-core</artifactId>
            <version>0.7.3</version>
        </dependency>
        <dependency>
            <groupId>us.codecraft</groupId>
            <artifactId>webmagic-extension</artifactId>
            <version>0.7.3</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.properties

# 使用 smtp 协议
spring.mail.protocol=smtp
spring.mail.port=465
# 邮箱服务器host,不同的邮箱厂商,这个地址不同。
spring.mail.host=smtp.qq.com
spring.mail.username=自己的邮箱账号
# 这个密码填的不是邮箱登录密码,而是邮箱smtp服务授权码
spring.mail.password=自己的邮箱授权码
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
# SSL Config
spring.mail.default-encoding=UTF-8
spring.mail.properties.mail.smtp.ssl.enable=true
spring.mail.properties.mail.smtp.socketFactory.port=465
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory

open=false

项目启动运行类CustomRunner.java

package com.example.demo;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * @author ListJiang
 * @since 2021/9/22 10:52
 * description 自定义延迟执行
 */
@Component
public class CustomRunner implements ApplicationRunner {

    @Autowired
    JavaMailSender javaMailSender;

    @Value("${spring.mail.username}")
    String username;
    @Value("${open}")
    boolean open;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        while (open) {
            Document document = Jsoup.connect("http://www.apple.com.cn/cn-k12/shop/refurbished/mac/" +
                    "1tb-macbook-air-macbook-pro-16gb-32gb")
                    .userAgent("Mozilla/5.0 (Windows NT 6.1; rv:30.0) Gecko/20100101 Firefox/30.0").get();
            Elements scripts = document.select("script");
            List<JSONObject> tiles = scripts.stream()
                    .filter(s -> s.data().contains("window.REFURB_GRID_BOOTSTRAP"))
                    .flatMap(s -> {
                        // 获取数据
                        String replace = s.data().replace("window.REFURB_GRID_BOOTSTRAP = ", "")
                                .replace("\n", "").trim();
                        String substring = replace.substring(0, replace.length() - 1);
                        JSONObject parse = (JSONObject) JSONObject.parse(substring);
                        JSONArray title = parse.getJSONArray("tiles");
                        return title.stream();
                    })
                    .map(o -> (JSONObject) o)
                    .filter(jsonObject -> {
                        // 配置信息筛选
                        JSONObject json1 = jsonObject.getJSONObject("filters").getJSONObject("dimensions");
                        // 固态
                        boolean bool1 = Arrays.asList("1tb", "512gb").contains(json1.getString("dimensionCapacity"));
                        // 内存
                        boolean bool2 = Arrays.asList("16gb", "32gb").contains(json1.getString("tsMemorySize"));
                        // 出场年限
                        boolean bool3 = Arrays.asList("2019", "2020", "2021")
                                .contains(json1.getString("dimensionRelYear"));
                        // 价格,
                        boolean bool4 = jsonObject.getJSONObject("price").getJSONObject("currentPrice")
                                .getFloat("raw_amount") > 10000L;
                        return bool1 && bool2 && bool3 && bool4;
                    })
                    .map(json -> {
                        // 构建短信需要的json
                        JSONObject j = new JSONObject();
                        JSONObject dimensions = json.getJSONObject("filters").getJSONObject("dimensions");
                        j.put("tsMemorySize", dimensions.getString("tsMemorySize"));
                        j.put("dimensionCapacity", dimensions.getString("dimensionCapacity"));
                        j.put("dimensionScreensize", dimensions.getString("dimensionScreensize"));
                        j.put("version", dimensions.getString("refurbClearModel") + dimensions.getString("dimensionRelYear"));
                        j.put("dimensionColor", dimensions.getString("dimensionColor"));
                        j.put("remark", json.getString("title"));
                        j.put("price", json.getJSONObject("price").getJSONObject("currentPrice")
                                .getFloat("raw_amount"));
                        return j;
                    })
                    .collect(Collectors.toList());
            tiles.sort((a, b) -> a.getFloat("price") > b.getFloat("price") ? -1 : 1);
            tiles.stream().forEach(System.out::println);

            // 筛选除超过预期的价格
            tiles = tiles.stream().filter(t -> {
                if (t.getString("tsMemorySize").equals("16gb")
                        && t.getString("dimensionCapacity").equals("1tb")
                        && t.getString("version").equals("macbookpro2020")
                        && t.getFloat("price") < 12319L) {
                    return true;
                }
                if (t.getString("tsMemorySize").equals("16gb")
                        && t.getString("dimensionCapacity").equals("1tb")
                        && t.getString("version").equals("macbookair2020")
                        && t.getString("remark").contains("8 核图形处理器")
                        && t.getFloat("price") < 10939L) {
                    return true;
                }
                if (t.getString("tsMemorySize").equals("16gb")
                        && t.getString("dimensionCapacity").equals("1tb")
                        && t.getString("version").equals("macbookair2020")
                        && t.getString("remark").contains("7 核图形处理器")
                        && t.getFloat("price") < 10619L) {
                    return true;
                }
                if (t.getString("tsMemorySize").equals("16gb")
                        && t.getString("dimensionCapacity").equals("512gb")
                        && t.getFloat("price") < 11049L) {
                    return true;
                }
                return false;
            }).collect(Collectors.toList());

            if (tiles.size() > 0) {
                String text = "";
                SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
                // 发件人,这里的值必须是配置的发送邮箱账号的可用账号名,例如qq邮箱可以配置多个账号,这里是其中任意一个即可
                simpleMailMessage.setFrom(username);
                // 收件人
                simpleMailMessage.setTo("2754005464@qq.com");
                // 邮件主题
                simpleMailMessage.setSubject("降价啦");
                // 邮件内容
                for (JSONObject jsonObject : tiles) {
                    text += jsonObject.toJSONString() + "\n";
                }
                simpleMailMessage.setText(text);
                javaMailSender.send(simpleMailMessage);
                open = false;
            }
            System.out.println("\n\n\n"+LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")));
            Thread.sleep(5000);
        }
    }
}

posted @ 2021-09-26 20:41  临渊不羡渔  阅读(79)  评论(0)    收藏  举报