如何在Java爬虫中设置动态延迟以避免API限制

小白学大数据
• 阅读 20

一、动态延迟与代理服务器的重要性

  1. 动态延迟的重要性 动态延迟是指根据爬虫运行时的环境和API的响应情况,动态调整请求之间的间隔时间。与静态延迟(固定时间间隔)相比,动态延迟能够更灵活地应对API的限制策略,同时最大化爬虫的效率。动态延迟的重要性体现在以下几个方面: 避免被封禁:通过合理调整请求间隔,爬虫可以避免因请求频率过高而触发API的限制机制。 提高效率:动态延迟可以根据API的响应时间调整请求间隔,从而在不触发限制的情况下,尽可能提高爬取速度。 适应性更强:不同API的限制策略可能不同,动态延迟可以根据具体的API响应调整策略,具有更强的适应性。 二、动态延迟的实现策略 在Java爬虫中,动态延迟可以通过以下几种策略实现:
  2. 基于API响应时间的延迟调整 API的响应时间可以作为动态延迟的重要参考。如果API响应时间较短,说明当前请求频率可能较低,可以适当减少延迟;如果响应时间较长,说明可能接近API的限制,需要增加延迟。
  3. 基于错误码的延迟调整 许多API在达到请求频率限制时会返回特定的错误码(如429 Too Many Requests)。爬虫可以根据这些错误码动态调整延迟。
  4. 基于滑动窗口算法的延迟调整 滑动窗口算法是一种常用的流量控制算法,可以动态调整请求频率,确保在一定时间窗口内的请求次数不超过API的限制。 三、基于API响应时间的动态延迟实现(结合代理服务器) 以下是基于API响应时间的动态延迟实现代码示例,同时结合了代理服务器的使用:
    import java.net.HttpURLConnection;
    import java.net.InetSocketAddress;
    import java.net.Proxy;
    import java.net.URL;
    import java.util.concurrent.TimeUnit;
    

public class DynamicDelayCrawlerWithProxy { private static final String PROXY_HOST = "www.16yun.cn"; private static final int PROXY_PORT = 5445; private static final String PROXY_USER = "16QMSOML"; private static final String PROXY_PASS = "280651";

private static final int MIN_DELAY = 100; // 最小延迟时间(毫秒)
private static final int MAX_DELAY = 5000; // 最大延迟时间(毫秒)
private static final int TARGET_RESPONSE_TIME = 500; // 目标响应时间(毫秒)

public static void main(String[] args) {
    String apiUrl = "https://api.example.com/data";
    int delay = MIN_DELAY;

    // 设置代理服务器
    System.setProperty("java.net.useSystemProxies", "true");
    System.setProperty("http.proxyHost", PROXY_HOST);
    System.setProperty("http.proxyPort", String.valueOf(PROXY_PORT));
    System.setProperty("https.proxyHost", PROXY_HOST);
    System.setProperty("https.proxyPort", String.valueOf(PROXY_PORT));

    // 设置代理认证
    System.setProperty("java.net.useSystemProxies", "true");
    System.setProperty("http.proxyUser", PROXY_USER);
    System.setProperty("http.proxyPassword", PROXY_PASS);
    System.setProperty("https.proxyUser", PROXY_USER);
    System.setProperty("https.proxyPassword", PROXY_PASS);

    while (true) {
        long startTime = System.currentTimeMillis();
        try {
            // 发起请求
            URL url = new URL(apiUrl);
            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
            HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(5000);
            connection.connect();

            int responseCode = connection.getResponseCode();
            if (responseCode == 200) {
                // 请求成功,处理响应数据
                System.out.println("Data fetched successfully.");
            } else {
                System.out.println("Failed to fetch data. Response Code: " + responseCode);
            }
        } catch (IOException e) {
            System.out.println("Error occurred: " + e.getMessage());
        }

        long endTime = System.currentTimeMillis();
        long responseTime = endTime - startTime;

        // 根据响应时间调整延迟
        if (responseTime < TARGET_RESPONSE_TIME) {
            delay = Math.max(MIN_DELAY, delay - 100); // 减少延迟
        } else {
            delay = Math.min(MAX_DELAY, delay + 100); // 增加延迟
        }

        // 等待下一次请求
        try {
            TimeUnit.MILLISECONDS.sleep(delay);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

}

代码解析
最小和最大延迟时间:通过MIN_DELAY和MAX_DELAY设置动态延迟的范围。
目标响应时间:通过TARGET_RESPONSE_TIME设置期望的API响应时间。
请求与响应处理:使用HttpURLConnection发起请求,并根据响应时间调整延迟。
动态调整延迟:如果响应时间小于目标响应时间,则减少延迟;否则增加延迟。
四、基于错误码的动态延迟实现
当API返回429错误码时,说明请求频率过高。此时可以动态增加延迟,直到API恢复正常响应。以下是基于错误码的动态延迟实现代码示例:
``` import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.TimeUnit;

public class ErrorBasedDynamicDelayCrawlerWithProxy {
    private static final String PROXY_HOST = "www.16yun.cn";
    private static final int PROXY_PORT = 5445;
    private static final String PROXY_USER = "16QMSOML";
    private static final String PROXY_PASS = "280651";

    private static final int MIN_DELAY = 100; // 最小延迟时间(毫秒)
    private static final int MAX_DELAY = 5000; // 最大延迟时间(毫秒)
    private static final int INITIAL_DELAY = 500; // 初始延迟时间(毫秒)

    public static void main(String[] args) {
        String apiUrl = "https://api.example.com/data";
        int delay = INITIAL_DELAY;

        // 设置代理服务器
        System.setProperty("java.net.useSystemProxies", "true");
        System.setProperty("http.proxyHost", PROXY_HOST);
        System.setProperty("http.proxyPort", String.valueOf(PROXY_PORT));
        System.setProperty("https.proxyHost", PROXY_HOST);
        System.setProperty("https.proxyPort", String.valueOf(PROXY_PORT));

        // 设置代理认证
        System.setProperty("java.net.useSystemProxies", "true");
        System.setProperty("http.proxyUser", PROXY_USER);
        System.setProperty("http.proxyPassword", PROXY_PASS);
        System.setProperty("https.proxyUser", PROXY_USER);
        System.setProperty("https.proxyPassword", PROXY_PASS);

        while (true) {
            try {
                URL url = new URL(apiUrl);
                Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
                HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000);
                connection.setReadTimeout(5000);
                connection.connect();

                int responseCode = connection.getResponseCode();

                if (responseCode == 200) {
                    // 请求成功,处理响应数据
                    System.out.println("Data fetched successfully.");
                    delay = Math.max(MIN_DELAY, delay / 2); // 成功时减少延迟
                } else if (responseCode == 429) {
                    // 请求频率过高,增加延迟
                    System.out.println("Rate limit exceeded. Increasing delay.");
                    delay = Math.min(MAX_DELAY, delay * 2);
                } else {
                    System.out.println("Failed to fetch data. Response Code: " + responseCode);
                }
            } catch (IOException e) {
                System.out.println("Error occurred: " + e.getMessage());
            }

            // 等待下一次请求
            try {
                TimeUnit.MILLISECONDS.sleep(delay);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

代码解析 初始延迟时间:通过INITIAL_DELAY设置初始延迟。 错误码处理:当API返回429错误码时,增加延迟;当请求成功时,减少延迟。 动态调整延迟:根据API的响应状态动态调整请求间隔。 五、基于滑动窗口算法的动态延迟实现 滑动窗口算法是一种常用的流量控制算法,可以动态调整请求频率,确保在一定时间窗口内的请求次数不超过API的限制。以下是基于滑动窗口算法的动态延迟实现代码示例:

import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

public class SlidingWindowCrawlerWithProxy {
    private static final String PROXY_HOST = "www.16yun.cn";
    private static final int PROXY_PORT = 5445;
    private static final String PROXY_USER = "16QMSOML";
    private static final String PROXY_PASS = "280651";

    private static final int WINDOW_SIZE = 60000; // 时间窗口大小(毫秒)
    private static final int MAX_REQUESTS_PER_WINDOW = 100; // 每个时间窗口内的最大请求次数
    private static final ConcurrentLinkedQueue<Long> requestTimes = new ConcurrentLinkedQueue<>();

    public static void main(String[] args) {
        String apiUrl = "https://api.example.com/data";

        // 设置代理服务器
        System.setProperty("java.net.useSystemProxies", "true");
        System.setProperty("http.proxyHost", PROXY_HOST);
        System.setProperty("http.proxyPort", String.valueOf(PROXY_PORT));
        System.setProperty("https.proxyHost", PROXY_HOST);
        System.setProperty("https.proxyPort", String.valueOf(PROXY_PORT));

        // 设置代理认证
        System.setProperty("java.net.useSystemProxies", "true");
        System.setProperty("http.proxyUser", PROXY_USER);
        System.setProperty("http.proxyPassword", PROXY_PASS);
        System.setProperty("https.proxyUser", PROXY_USER);
        System.setProperty("https.proxyPassword", PROXY_PASS);

        while (true) {
            // 清理超出时间窗口的请求记录
            long currentTime = System.currentTimeMillis();
            while (!requestTimes.isEmpty() && currentTime - requestTimes.peek() > WINDOW_SIZE) {
                requestTimes.poll();
            }

            // 检查是否达到请求频率限制
            if (requestTimes.size() >= MAX_REQUESTS_PER_WINDOW) {
                long delay = WINDOW_SIZE - (currentTime - requestTimes.peek());
                System.out.println("Rate limit exceeded. Waiting for " + delay + " ms.");
                try {
                    TimeUnit.MILLISECONDS.sleep(delay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // 发起请求
            try {
                URL url = new URL(apiUrl);
                Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT));
                HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000);
                connection.setReadTimeout(5000);
                connection.connect();

                int responseCode = connection.getResponseCode();
                if (responseCode == 200) {
                    // 请求成功,处理响应数据
                    System.out.println("Data fetched successfully.");
                } else {
                    System.out.println("Failed to fetch data. Response Code: " + responseCode);
                }
            } catch (IOException e) {
                System.out.println("Error occurred: " + e.getMessage());
            }

            // 记录请求时间
            requestTimes.add(System.currentTimeMillis());
        }
    }
}

代码解析 时间窗口大小:通过WINDOW_SIZE设置时间窗口的大小。 最大请求次数:通过MAX_REQUESTS_PER_WINDOW设置每个时间窗口内的最大请求次数。 请求时间记录:使用ConcurrentLinkedQueue记录每次请求的时间。 动态调整延迟:根据时间窗口内的请求次数动态调整请求间隔。 六、总结 在Java爬虫开发中,设置动态延迟是避免API限制的关键技术,而代理服务器的使用则进一步提高了爬虫的稳定性和安全性。通过基于API响应时间、错误码或滑动窗口算法的动态延迟策略,爬虫可以在不触发API限制的情况下,高效地抓取数据。

点赞
收藏
评论区
推荐文章
Kubrnete Kubrnete
3年前
基于01背包问题的动态规划算法
目录初步认识动态规划(初步认识动态规划)与动态规划有关的理论知识:(与动态规划有关的理论知识:)动态规划中的最优决策表(基于填表的动态规划算法)最终版动态规划(最终版动态规划)总结(总结:)初步认识动态规划动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推
mosn基于延迟负载均衡算法 -- 走得更快,期待走得更稳 | 京东云技术团队
这篇文章主要是介绍mosn在v1.5.0中新引入的基于延迟的负载均衡算法。对分布式系统中延迟出现的原因进行剖析,介绍mosn都通过哪些方法来降低延迟,构建来与生产环境性能分布相近的测试用例来对算法进行验证
Stella981 Stella981
3年前
HTTPS 传输优化详解之动态 TLS Record Size
笔者在过去分析了诸多可以减少HTTPS传输延迟的方法,如分布式Session的复用;启用HSTS,客户端默认开启HTTPS跳转;采用HTTP/2传输协议;使用ChaCha20Poly1305算法减少移动端CPU运算时间等。通过这些方法,可以在很大程度上优化HTTPS在传输上的延迟,给网站用户带来较好的访问体验。最近
Easter79 Easter79
3年前
SpringCloud系列十五:使用Hystrix实现容错
1\.回顾上文讲解了容错的重要性,以及容错需要实现的功能。本文来讲解使用Hystrix实现容错。2\.Hystrix简介Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或第三方库,防止级联失败,从而提高系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错:
Stella981 Stella981
3年前
HBase高性能查询揭秘
!(https://oscimg.oschina.net/oscnet/322865892dd3796f52e6af521063fd0a6fc.gif)先给结论吧:HBase利用compaction机制,通过大量的读延迟毛刺和一定的写阻塞,来换取整体上的读取延迟的平稳。1.为什么要compaction
Stella981 Stella981
3年前
Servlet知识小结
什么是Servlet?Servlet是运行于web服务器中的一个特殊java应用小程序,能够接受来自每个客户端的请求并给予响应,双方遵循http协议。Servlet是做什么的?1能够接收客户端的HTTP的请求,并给予不同的响应,即动态响应。2能做一些需要动态显示的Web资源内容。可自行查看JAVA的API了解Servlet、Gene
玩转云端|演唱会一票难求?快用天翼云边缘安全加速平台AccessOne!
天翼云AccessOne基于覆盖全球的海量边缘节点,能够智能分离动静态内容,通过智能负载均衡技术,让静态内容在边缘节点进行缓存,保障用户就近接入边缘节点获取资源;对于动态内容,可通过智能选路、协议优化等技术,选择最/佳链路回源,同时提供协议优化、链路优化等多项优化技术,大大缩短内容传输距离和时间,加速抢购过程中的数据流转,有效降低延迟和抖动,保障用户交易。
京东云开发者 京东云开发者
6个月前
【京东云新品发布月刊】2024年7月产品动态
京东云7月产品动态:1.【消息队列RocketMQ】新品上线消息队列RocketMQ是京东云基于ApacheRocketMQ打造的一款低延迟、高并发、高可用、高可靠的分布式消息队列服务。支持开源客户端零改造接入,同时具备计算存储分离,灵活扩缩容的优势。能够
小白学大数据 小白学大数据
3个月前
网络延迟对Python爬虫速度的影响分析
Python爬虫因其强大的数据处理能力和灵活性而被广泛应用于数据抓取和网络信息收集。然而,网络延迟是影响爬虫效率的重要因素之一。本文将深入探讨网络延迟对Python爬虫速度的影响,并提供相应的代码实现过程,以帮助开发者优化爬虫性能。网络延迟的定义与影响网络
融云IM即时通讯 融云IM即时通讯
3个月前
融云IM干货丨推送通知的延迟问题如何解决?
解决推送通知的延迟问题,可以从以下几个方面进行优化:网络优化:切换到稳定的网络环境,尽量使用WiFi连接,避免在信号较弱的地方使用移动数据。优化网络设置,如APN设置等。关闭不必要的后台应用,它们可能会占用网络资源,导致推送延迟。推送服务配置:检查应用推送
小白学大数据
小白学大数据
Lv1
男 · 亿牛云 · python技术
宁为代码类弯腰,不为bug点提交!
文章
96
粉丝
5
获赞
18