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

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

  1. 动态延迟的重要性 动态延迟是指根据爬虫运行时的环境和API的响应情况,动态调整请求之间的间隔时间。与静态延迟(固定时间间隔)相比,动态延迟能够更灵活地应对API的限制策略,同时最大化爬虫的效率。动态延迟的重要性体现在以下几个方面: 避免被封禁:通过合理调整请求间隔,爬虫可以避免因请求频率过高而触发API的限制机制。 提高效率:动态延迟可以根据API的响应时间调整请求间隔,从而在不触发限制的情况下,尽可能提高爬取速度。 适应性更强:不同API的限制策略可能不同,动态延迟可以根据具体的API响应调整策略,具有更强的适应性。 二、动态延迟的实现策略 在Java爬虫中,动态延迟可以通过以下几种策略实现:
  2. 基于API响应时间的延迟调整 API的响应时间可以作为动态延迟的重要参考。如果API响应时间较短,说明当前请求频率可能较低,可以适当减少延迟;如果响应时间较长,说明可能接近API的限制,需要增加延迟。
  3. 基于错误码的延迟调整 许多API在达到请求频率限制时会返回特定的错误码(如429 Too Many Requests)。爬虫可以根据这些错误码动态调整延迟。
  4. 基于滑动窗口算法的延迟调整 滑动窗口算法是一种常用的流量控制算法,可以动态调整请求频率,确保在一定时间窗口内的请求次数不超过API的限制。 三、基于API响应时间的动态延迟实现(结合代理服务器) 以下是基于API响应时间的动态延迟实现代码示例,同时结合了代理服务器的使用:
1import java.net.HttpURLConnection; 2import java.net.InetSocketAddress; 3import java.net.Proxy; 4import java.net.URL; 5import java.util.concurrent.TimeUnit; 6 7public class DynamicDelayCrawlerWithProxy { 8 private static final String PROXY_HOST = "www.16yun.cn"; 9 private static final int PROXY_PORT = 5445; 10 private static final String PROXY_USER = "16QMSOML"; 11 private static final String PROXY_PASS = "280651"; 12 13 private static final int MIN_DELAY = 100; // 最小延迟时间(毫秒) 14 private static final int MAX_DELAY = 5000; // 最大延迟时间(毫秒) 15 private static final int TARGET_RESPONSE_TIME = 500; // 目标响应时间(毫秒) 16 17 public static void main(String[] args) { 18 String apiUrl = "https://api.example.com/data"; 19 int delay = MIN_DELAY; 20 21 // 设置代理服务器 22 System.setProperty("java.net.useSystemProxies", "true"); 23 System.setProperty("http.proxyHost", PROXY_HOST); 24 System.setProperty("http.proxyPort", String.valueOf(PROXY_PORT)); 25 System.setProperty("https.proxyHost", PROXY_HOST); 26 System.setProperty("https.proxyPort", String.valueOf(PROXY_PORT)); 27 28 // 设置代理认证 29 System.setProperty("java.net.useSystemProxies", "true"); 30 System.setProperty("http.proxyUser", PROXY_USER); 31 System.setProperty("http.proxyPassword", PROXY_PASS); 32 System.setProperty("https.proxyUser", PROXY_USER); 33 System.setProperty("https.proxyPassword", PROXY_PASS); 34 35 while (true) { 36 long startTime = System.currentTimeMillis(); 37 try { 38 // 发起请求 39 URL url = new URL(apiUrl); 40 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT)); 41 HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy); 42 connection.setRequestMethod("GET"); 43 connection.setConnectTimeout(5000); 44 connection.setReadTimeout(5000); 45 connection.connect(); 46 47 int responseCode = connection.getResponseCode(); 48 if (responseCode == 200) { 49 // 请求成功,处理响应数据 50 System.out.println("Data fetched successfully."); 51 } else { 52 System.out.println("Failed to fetch data. Response Code: " + responseCode); 53 } 54 } catch (IOException e) { 55 System.out.println("Error occurred: " + e.getMessage()); 56 } 57 58 long endTime = System.currentTimeMillis(); 59 long responseTime = endTime - startTime; 60 61 // 根据响应时间调整延迟 62 if (responseTime < TARGET_RESPONSE_TIME) { 63 delay = Math.max(MIN_DELAY, delay - 100); // 减少延迟 64 } else { 65 delay = Math.min(MAX_DELAY, delay + 100); // 增加延迟 66 } 67 68 // 等待下一次请求 69 try { 70 TimeUnit.MILLISECONDS.sleep(delay); 71 } catch (InterruptedException e) { 72 e.printStackTrace(); 73 } 74 } 75 } 76}

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

1import java.net.HttpURLConnection; 2import java.net.InetSocketAddress; 3import java.net.Proxy; 4import java.net.URL; 5import java.util.concurrent.TimeUnit; 6 7public class ErrorBasedDynamicDelayCrawlerWithProxy { 8 private static final String PROXY_HOST = "www.16yun.cn"; 9 private static final int PROXY_PORT = 5445; 10 private static final String PROXY_USER = "16QMSOML"; 11 private static final String PROXY_PASS = "280651"; 12 13 private static final int MIN_DELAY = 100; // 最小延迟时间(毫秒) 14 private static final int MAX_DELAY = 5000; // 最大延迟时间(毫秒) 15 private static final int INITIAL_DELAY = 500; // 初始延迟时间(毫秒) 16 17 public static void main(String[] args) { 18 String apiUrl = "https://api.example.com/data"; 19 int delay = INITIAL_DELAY; 20 21 // 设置代理服务器 22 System.setProperty("java.net.useSystemProxies", "true"); 23 System.setProperty("http.proxyHost", PROXY_HOST); 24 System.setProperty("http.proxyPort", String.valueOf(PROXY_PORT)); 25 System.setProperty("https.proxyHost", PROXY_HOST); 26 System.setProperty("https.proxyPort", String.valueOf(PROXY_PORT)); 27 28 // 设置代理认证 29 System.setProperty("java.net.useSystemProxies", "true"); 30 System.setProperty("http.proxyUser", PROXY_USER); 31 System.setProperty("http.proxyPassword", PROXY_PASS); 32 System.setProperty("https.proxyUser", PROXY_USER); 33 System.setProperty("https.proxyPassword", PROXY_PASS); 34 35 while (true) { 36 try { 37 URL url = new URL(apiUrl); 38 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT)); 39 HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy); 40 connection.setRequestMethod("GET"); 41 connection.setConnectTimeout(5000); 42 connection.setReadTimeout(5000); 43 connection.connect(); 44 45 int responseCode = connection.getResponseCode(); 46 47 if (responseCode == 200) { 48 // 请求成功,处理响应数据 49 System.out.println("Data fetched successfully."); 50 delay = Math.max(MIN_DELAY, delay / 2); // 成功时减少延迟 51 } else if (responseCode == 429) { 52 // 请求频率过高,增加延迟 53 System.out.println("Rate limit exceeded. Increasing delay."); 54 delay = Math.min(MAX_DELAY, delay * 2); 55 } else { 56 System.out.println("Failed to fetch data. Response Code: " + responseCode); 57 } 58 } catch (IOException e) { 59 System.out.println("Error occurred: " + e.getMessage()); 60 } 61 62 // 等待下一次请求 63 try { 64 TimeUnit.MILLISECONDS.sleep(delay); 65 } catch (InterruptedException e) { 66 e.printStackTrace(); 67 } 68 } 69 } 70}

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

1import java.net.HttpURLConnection; 2import java.net.InetSocketAddress; 3import java.net.Proxy; 4import java.net.URL; 5import java.util.concurrent.ConcurrentLinkedQueue; 6import java.util.concurrent.TimeUnit; 7 8public class SlidingWindowCrawlerWithProxy { 9 private static final String PROXY_HOST = "www.16yun.cn"; 10 private static final int PROXY_PORT = 5445; 11 private static final String PROXY_USER = "16QMSOML"; 12 private static final String PROXY_PASS = "280651"; 13 14 private static final int WINDOW_SIZE = 60000; // 时间窗口大小(毫秒) 15 private static final int MAX_REQUESTS_PER_WINDOW = 100; // 每个时间窗口内的最大请求次数 16 private static final ConcurrentLinkedQueue<Long> requestTimes = new ConcurrentLinkedQueue<>(); 17 18 public static void main(String[] args) { 19 String apiUrl = "https://api.example.com/data"; 20 21 // 设置代理服务器 22 System.setProperty("java.net.useSystemProxies", "true"); 23 System.setProperty("http.proxyHost", PROXY_HOST); 24 System.setProperty("http.proxyPort", String.valueOf(PROXY_PORT)); 25 System.setProperty("https.proxyHost", PROXY_HOST); 26 System.setProperty("https.proxyPort", String.valueOf(PROXY_PORT)); 27 28 // 设置代理认证 29 System.setProperty("java.net.useSystemProxies", "true"); 30 System.setProperty("http.proxyUser", PROXY_USER); 31 System.setProperty("http.proxyPassword", PROXY_PASS); 32 System.setProperty("https.proxyUser", PROXY_USER); 33 System.setProperty("https.proxyPassword", PROXY_PASS); 34 35 while (true) { 36 // 清理超出时间窗口的请求记录 37 long currentTime = System.currentTimeMillis(); 38 while (!requestTimes.isEmpty() && currentTime - requestTimes.peek() > WINDOW_SIZE) { 39 requestTimes.poll(); 40 } 41 42 // 检查是否达到请求频率限制 43 if (requestTimes.size() >= MAX_REQUESTS_PER_WINDOW) { 44 long delay = WINDOW_SIZE - (currentTime - requestTimes.peek()); 45 System.out.println("Rate limit exceeded. Waiting for " + delay + " ms."); 46 try { 47 TimeUnit.MILLISECONDS.sleep(delay); 48 } catch (InterruptedException e) { 49 e.printStackTrace(); 50 } 51 } 52 53 // 发起请求 54 try { 55 URL url = new URL(apiUrl); 56 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, PROXY_PORT)); 57 HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy); 58 connection.setRequestMethod("GET"); 59 connection.setConnectTimeout(5000); 60 connection.setReadTimeout(5000); 61 connection.connect(); 62 63 int responseCode = connection.getResponseCode(); 64 if (responseCode == 200) { 65 // 请求成功,处理响应数据 66 System.out.println("Data fetched successfully."); 67 } else { 68 System.out.println("Failed to fetch data. Response Code: " + responseCode); 69 } 70 } catch (IOException e) { 71 System.out.println("Error occurred: " + e.getMessage()); 72 } 73 74 // 记录请求时间 75 requestTimes.add(System.currentTimeMillis()); 76 } 77 } 78}

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

点赞
收藏

评论区

加载中...

相关推荐

mosn基于延迟负载均衡算法 -- 走得更快,期待走得更稳 | 京东云技术团队

这篇文章主要是介绍mosn在v1.5.0中新引入的基于延迟的负载均衡算法。对分布式系统中延迟出现的原因进行剖析,介绍mosn都通过哪些方法来降低延迟,构建来与生产环境性能分布相近的测试用例来对算法进行验证

HTTPS 传输优化详解之动态 TLS Record Size

笔者在过去分析了诸多可以减少HTTPS传输延迟的方法,如分布式Session的复用;启用HSTS,客户端默认开启HTTPS跳转;采用HTTP/2传输协议;使用ChaCha20Poly1305算法减少移动端CPU运算时间等。通过这些方法,可以在很大程度上优化HTTPS在传输上的延迟,给网站用户带来较好的访问体验。最近

SpringCloud系列十五:使用Hystrix实现容错

1\.回顾上文讲解了容错的重要性,以及容错需要实现的功能。本文来讲解使用Hystrix实现容错。2\.Hystrix简介Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或第三方库,防止级联失败,从而提高系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错:

融云IM干货丨推送通知的延迟问题如何解决?

解决推送通知的延迟问题,可以从以下几个方面进行优化:网络优化:切换到稳定的网络环境,尽量使用WiFi连接,避免在信号较弱的地方使用移动数据。优化网络设置,如APN设置等。关闭不必要的后台应用,它们可能会占用网络资源,导致推送延迟。推送服务配置:检查应用推送

玩转云端|演唱会一票难求?快用天翼云边缘安全加速平台AccessOne!

天翼云AccessOne基于覆盖全球的海量边缘节点,能够智能分离动静态内容,通过智能负载均衡技术,让静态内容在边缘节点进行缓存,保障用户就近接入边缘节点获取资源;对于动态内容,可通过智能选路、协议优化等技术,选择最/佳链路回源,同时提供协议优化、链路优化等多项优化技术,大大缩短内容传输距离和时间,加速抢购过程中的数据流转,有效降低延迟和抖动,保障用户交易。

【京东云新品发布月刊】2024年7月产品动态

京东云7月产品动态:1.【消息队列RocketMQ】新品上线消息队列RocketMQ是京东云基于ApacheRocketMQ打造的一款低延迟、高并发、高可用、高可靠的分布式消息队列服务。支持开源客户端零改造接入,同时具备计算存储分离,灵活扩缩容的优势。能够