zoukankan      html  css  js  c++  java
  • 监听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);
            }
        }
    }
    
    
  • 相关阅读:
    BestCoder17 1001.Chessboard(hdu 5100) 解题报告
    codeforces 485A.Factory 解题报告
    codeforces 485B Valuable Resources 解题报告
    BestCoder16 1002.Revenge of LIS II(hdu 5087) 解题报告
    codeforces 374A Inna and Pink Pony 解题报告
    codeforces 483B Friends and Presents 解题报告
    BestCoder15 1002.Instruction(hdu 5083) 解题报告
    codeforces 483C.Diverse Permutation 解题报告
    codeforces 483A. Counterexample 解题报告
    NSArray中地内存管理 理解
  • 原文地址:https://www.cnblogs.com/jiangdewen/p/15339986.html
Copyright © 2011-2022 走看看