• 监听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("
    ", "").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() + "
    ";
                    }
                    simpleMailMessage.setText(text);
                    javaMailSender.send(simpleMailMessage);
                    open = false;
                }
                System.out.println("
    
    
    "+LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")));
                Thread.sleep(5000);
            }
        }
    }
    
    
  • 相关阅读:
    541. Reverse String II
    540. Single Element in a Sorted Array
    初识python
    openCV
    openCV
    win环境下安装配置openCV-4.3.0
    力扣Leetcode 198. 打家劫舍
    力扣Leetcode 680. 验证回文字符串 Ⅱ
    力扣Leetcode 560. 和为K的子数组
    华师2019软件专硕复试机试题最后一题G:找数
  • 原文地址:https://www.cnblogs.com/jiangdewen/p/15339986.html
Copyright © 2020-2023  润新知