8年多爬虫经验的人告诉你,国内ADSL是王道,多申请些线路,分布在多个不同的电信机房,能跨省跨市更好,我这里写好的断线重拨组件,你可以直接使用。

ADSL拨号上网使用动态IP地址,每一次拨号得到的IP都不一样,所以我们可以通过程序来自动进行重新拨号以获得新的IP地址,以达到突破反爬虫封锁的目的。

那么我们如何进行自动重新拨号呢?

假设有10个线程在跑,大家都正常的跑,跑着跑着达到限制了,WEB服务器提示你“非常抱歉,来自您ip的请求异常频繁”,于是大家争先恐后(几乎是同时)请求拨号,这个时候同步的作用就显示出来了,只会有一个线程能拨号,在他结束之前其他线程都在等,等他拨号成功之后,其他线程会被唤醒并返回

算法描述: 1、假设总共有N个线程抓取网页,发现被封锁之后依次排队请求锁,注意:可以想象成是同时请求。 2、线程1抢先获得锁,并且设置isDialing = true后开始拨号,注意:线程1设置isDialing = true后其他线程才可能获得锁。 3、其他线程(2-N)依次获得锁,发现isDialing = true,于是wait。注意:获得锁并判断一个布尔值,跟后面的拨号操作比起来,时间可以忽略。 4、线程1拨号完毕isDialing = false。注意:这个时候可以断定,其他所有线程必定是处于wait状态等待唤醒。 5、线程1唤醒其他线程,其他线程和线程1返回开始抓取网页。 6、抓了一会儿之后,又会被封锁,于是回到步骤1。

在本场景中,3和4的断定是没问题的,就算是出现“不可能”的情况,即线程1已经拨号完成了,可2-N还没获得锁(汗),也不会重复拨号的情况,因为算法考虑了请求拨号时间和上一次成功拨号时间。

下面以腾达300M无线路由器,型号:N302 v2为例子来说明。

首先,设置路由器:上网设置 -》请根据需要选择连接模式 -》手动连接,由用户手动进行连接,如下图所示。其他的路由器使用方法类似,参照本方法替换相应的登录地址、断开连接及建立连接地址即可。

其次,利用Firefox的Firebug功能找到路由器的登录路径及参数、断开连接路径及参数、建立连接路径及参数,如下图所示。

接着,参考如下代码,替换自己相关的路径和参数:

  1. import org.jsoup.Connection;
  2. import org.jsoup.Jsoup;
  3. import org.jsoup.nodes.Document;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import java.util.*;
  7.  
  8. /**
  9. *
  10. * 自动更改IP地址反爬虫封锁,支持多线程
  11. *
  12. * ADSL拨号上网使用动态IP地址,每一次拨号得到的IP都不一样
  13. *
  14. * 使用腾达300M无线路由器,型号:N302 v2
  15. * 路由器设置中最好设置一下:上网设置 -》请根据需要选择连接模式 -》手动连接,由用户手动进行连接。
  16. * 其他的路由器使用方法类似,参照本类替换相应的登录地址、断开连接及建立连接地址即可
  17. *
  18. * @author 杨尚川
  19. */
  20. public class DynamicIp {
  21. private DynamicIp(){}
  22. private static final Logger LOGGER = LoggerFactory.getLogger(DynamicIp.class);
  23. private static final String ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
  24. private static final String ENCODING = "gzip, deflate";
  25. private static final String LANGUAGE = "zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3";
  26. private static final String CONNECTION = "keep-alive";
  27. private static final String HOST = "192.168.0.1";
  28. private static final String REFERER = "http://192.168.0.1/login.asp";
  29. private static final String USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:36.0) Gecko/20100101 Firefox/36.0";
  30. private static volatile boolean isDialing = false;
  31. private static volatile long lastDialTime = 0l;
  32.  
  33. public static void main(String[] args) {
  34. toNewIp();
  35. }
  36.  
  37. /**
  38. * 假设有10个线程在跑,大家都正常的跑,跑着跑着达到限制了,
  39. * 于是大家争先恐后(几乎是同时)请求拨号,
  40. * 这个时候同步的作用就显示出来了,只会有一个线程能拨号,
  41. * 在他结束之前其他线程都在等,等他拨号成功之后,
  42. * 其他线程会被唤醒并返回
  43. *
  44. * 算法描述:
  45. * 1、假设总共有N个线程抓取网页,发现被封锁之后依次排队请求锁,注意:可以想象成是同时请求。
  46. * 2、线程1抢先获得锁,并且设置isDialing = true后开始拨号,注意:线程1设置isDialing = true后其他线程才可能获得锁。
  47. * 3、其他线程(2-N)依次获得锁,发现isDialing = true,于是wait。注意:获得锁并判断一个布尔值,跟后面的拨号操作比起来,时间可以忽略。
  48. * 4、线程1拨号完毕isDialing = false。注意:这个时候可以断定,其他所有线程必定是处于wait状态等待唤醒。
  49. * 5、线程1唤醒其他线程,其他线程和线程1返回开始抓取网页。
  50. * 6、抓了一会儿之后,又会被封锁,于是回到步骤1。
  51. * 注意:在本场景中,3和4的断定是没问题的,就算是出现“不可能”的情况,
  52. * 即线程1已经拨号完成了,可2-N还没获得锁(汗),也不会重复拨号的情况,
  53. * 因为算法考虑了请求拨号时间和上一次成功拨号时间。
  54. * @return 更改IP是否成功
  55. */
  56. public static boolean toNewIp() {
  57. long requestDialTime = System.currentTimeMillis();
  58. LOGGER.info(Thread.currentThread()+"请求重新拨号");
  59. synchronized (DynamicIp.class) {
  60. if (isDialing) {
  61. LOGGER.info(Thread.currentThread()+"已经有其他线程在进行拨号了,我睡觉等待吧,其他线程拨号完毕会叫醒我的");
  62. try {
  63. DynamicIp.class.wait();
  64. } catch (InterruptedException e) {
  65. LOGGER.error(e.getMessage(), e);
  66. }
  67. LOGGER.info(Thread.currentThread()+"其他线程已经拨完号了,我可以返回了");
  68. return true;
  69. }
  70. isDialing = true;
  71. }
  72. //保险起见,这里再判断一下
  73. //如果请求拨号的时间小于上次成功拨号的时间,则说明这个请求来的【太迟了】,则返回。
  74. if(requestDialTime <= lastDialTime){
  75. LOGGER.info("请求来的太迟了");
  76. isDialing = true;
  77. return true;
  78. }
  79. LOGGER.info(Thread.currentThread()+"开始重新拨号");
  80. long start = System.currentTimeMillis();
  81. Map<String, String> cookies = login("username***", "password***", "phonenumber***");
  82. if("true".equals(cookies.get("success"))) {
  83. LOGGER.info(Thread.currentThread()+"登陆成功");
  84. cookies.remove("success");
  85. while (!disConnect(cookies)) {
  86. LOGGER.info(Thread.currentThread()+"断开连接失败,重试!");
  87. }
  88. LOGGER.info(Thread.currentThread()+"断开连接成功");
  89. while (!connect(cookies)) {
  90. LOGGER.info(Thread.currentThread()+"建立连接失败,重试!");
  91. }
  92. LOGGER.info(Thread.currentThread()+"建立连接成功");
  93. LOGGER.info(Thread.currentThread()+"自动更改IP地址成功!");
  94. LOGGER.info(Thread.currentThread()+"拨号耗时:"+(System.currentTimeMillis()-start)+"毫秒");
  95. //通知其他线程拨号成功
  96. synchronized (DynamicIp.class) {
  97. DynamicIp.class.notifyAll();
  98. }
  99. isDialing = false;
  100. lastDialTime = System.currentTimeMillis();
  101. return true;
  102. }
  103. isDialing = false;
  104. return false;
  105. }
  106.  
  107. public static boolean connect(Map<String, String> cookies){
  108. return execute(cookies, "3");
  109. }
  110. public static boolean disConnect(Map<String, String> cookies){
  111. return execute(cookies, "4");
  112. }
  113. public static boolean execute(Map<String, String> cookies, String action){
  114. String url = "http://192.168.0.1/goform/SysStatusHandle";
  115. Map<String, String> map = new HashMap<>();
  116. map.put("action", action);
  117. map.put("CMD", "WAN_CON");
  118. map.put("GO", "system_status.asp");
  119. Connection conn = Jsoup.connect(url)
  120. .header("Accept", ACCEPT)
  121. .header("Accept-Encoding", ENCODING)
  122. .header("Accept-Language", LANGUAGE)
  123. .header("Connection", CONNECTION)
  124. .header("Host", HOST)
  125. .header("Referer", REFERER)
  126. .header("User-Agent", USER_AGENT)
  127. .ignoreContentType(true)
  128. .timeout(30000);
  129. for(String cookie : cookies.keySet()){
  130. conn.cookie(cookie, cookies.get(cookie));
  131. }
  132.  
  133. String title = null;
  134. try {
  135. Connection.Response response = conn.method(Connection.Method.POST).data(map).execute();
  136. String html = response.body();
  137. Document doc = Jsoup.parse(html);
  138. title = doc.title();
  139. LOGGER.info("操作连接页面标题:"+title);
  140. }catch (Exception e){
  141. LOGGER.error(e.getMessage());
  142. }
  143. if("LAN | LAN Settings".equals(title)){
  144. if(("3".equals(action) && isConnected())
  145. || ("4".equals(action) && !isConnected())){
  146. return true;
  147. }
  148. }
  149. return false;
  150. }
  151. public static boolean isConnected(){
  152. try {
  153. Document doc = Jsoup.connect("http://www.baidu.com/s?wd=杨尚川&t=" + System.currentTimeMillis())
  154. .header("Accept", ACCEPT)
  155. .header("Accept-Encoding", ENCODING)
  156. .header("Accept-Language", LANGUAGE)
  157. .header("Connection", CONNECTION)
  158. .header("Referer", "https://www.baidu.com")
  159. .header("Host", "www.baidu.com")
  160. .header("User-Agent", USER_AGENT)
  161. .ignoreContentType(true)
  162. .timeout(30000)
  163. .get();
  164. LOGGER.info("搜索结果页面标题:"+doc.title());
  165. if(doc.title() != null && doc.title().contains("杨尚川")){
  166. return true;
  167. }
  168. }catch (Exception e){
  169. if("Network is unreachable".equals(e.getMessage())){
  170. return false;
  171. }else{
  172. LOGGER.error("状态检查失败:"+e.getMessage());
  173. }
  174. }
  175. return false;
  176. }
  177. public static Map<String, String> login(String userName, String password, String verify){
  178. try {
  179. Map<String, String> map = new HashMap<>();
  180. map.put("Username", userName);
  181. map.put("Password", password);
  182. map.put("checkEn", "0");
  183. Connection conn = Jsoup.connect("http://192.168.0.1/LoginCheck")
  184. .header("Accept", ACCEPT)
  185. .header("Accept-Encoding", ENCODING)
  186. .header("Accept-Language", LANGUAGE)
  187. .header("Connection", CONNECTION)
  188. .header("Referer", REFERER)
  189. .header("Host", HOST)
  190. .header("User-Agent", USER_AGENT)
  191. .ignoreContentType(true)
  192. .timeout(30000);
  193.  
  194. Connection.Response response = conn.method(Connection.Method.POST).data(map).execute();
  195. String html = response.body();
  196. Document doc = Jsoup.parse(html);
  197. LOGGER.info("登陆页面标题:"+doc.title());
  198. Map<String, String> cookies = response.cookies();
  199. if(html.contains(verify)){
  200. cookies.put("success", Boolean.TRUE.toString());
  201. }
  202. LOGGER.info("*******************************************************cookies start:");
  203. cookies.keySet().stream().forEach((cookie) -> {
  204. LOGGER.info(cookie + ":" + cookies.get(cookie));
  205. });
  206. LOGGER.info("*******************************************************cookies end:");
  207. return cookies;
  208. }catch (Exception e){
  209. LOGGER.error(e.getMessage(), e);
  210. }
  211. return Collections.emptyMap();
  212. }
  213. }

最后,就可以使用了,例子如下:

  1. public static void classify(Set<Word> words){
  2. LOGGER.debug("待处理词数目:"+words.size());
  3. AtomicInteger i = new AtomicInteger();
  4. Map<String, List<String>> data = new HashMap<>();
  5. words.forEach(word -> {
  6. if(i.get()%1000 == 999){
  7. save(data);
  8. }
  9. showStatus(data, i.incrementAndGet(), words.size(), word.getWord());
  10. String html = getContent(word.getWord());
  11. LOGGER.debug("获取到的HTML:" +html);
  12. while(html.contains("非常抱歉,来自您ip的请求异常频繁")){
  13. //使用新的IP地址
  14. DynamicIp.toNewIp();
  15. html = getContent(word.getWord());
  16. }
  17. if(StringUtils.isNotBlank(html)) {
  18. parse(word.getWord(), html, data);
  19. }else{
  20. NOT_FOUND_WORDS.add(word.getWord());
  21. }
  22.  
  23. });
  24. //写入磁盘
  25. save(data);
  26. LOGGER.debug("处理完毕,总词数目:"+words.size());
  27. }

本文讲述的方法和代码来源于本人的开源目superword,superword是一个Java实现的英文单词分析软件,主要研究英语单词音近形似转化规律、前缀后缀规律、词之间的相似性规律等等。

代码链接:

1、https://github.com/ysc/superword/blob/master/src/main/java/org/apdplat/superword/tools/DynamicIp.java

2、https://github.com/ysc/superword/blob/master/src/main/java/org/apdplat/superword/tools/WordClassifier.java

http://yangshangchuan.iteye.com/blog/2195287

自动更改IP地址反爬虫封锁,支持多线程(转)的更多相关文章

  1. 爬虫实现:根据IP地址反查域名

    域名解析与IP地址 域名解析是把域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站的一种服务:IP地址是网络上标识站点的数字地址,为了方便记忆,采用域名来代替IP地址标识站点地址.域名解析 ...

  2. 批处理快速更改ip地址

    在各种网络中切换,windows更换ip地址步骤:  进入控制面板--网络和internet--网络和共享中心--理性适配器设置--然后找到网卡--进入属性--然后internet 协议--更改ip信 ...

  3. 详解如何设置CentOS 7开机自动获取IP地址

    本例中以CentOS 7举例说明如何设置Linux开机自动获取IP地址和设置固定IP地址. 自动获取动态IP地址 1.输入“ip addr”并按回车键确定,发现无法获取IP(CentOS 7默认没有i ...

  4. 使用DOS批处理更改IP地址

    有时候电脑在家需要自动获取IP,在公司需要手动填写IP,改来改去很麻烦,于是做一个批处理一键修改很方便: @echo off cls color 0A @echo off echo. echo === ...

  5. 自动配置IP地址.bat

    ※※※※※※※※※※※※※※※※※※※※※※※※※※※※ @echo     ※                                                    ※ @echo  ...

  6. ARM-Linux配置DHCP自动获取IP地址

    备注:内核版本:2.6.30.9busybox版本:1.15.2 PC Linux和开发板Linux的工作用户:root 1. 配置内核:[*] Networking support --->N ...

  7. Oracle VM Virtual 下CentOS不能自动获取IP地址

    在CentOS配置网卡开机自动获取IP地址: vi /etc/sysconfig/network-scripts/ifcfg-eth0 将 ONBOOT="no" 改为 ONBOO ...

  8. Centos7(Linux)网络配置,自动获取ip地址

    Centos7.0 Vmware 网络桥接配置,利用DHCP自动获取ip地址 首先要将Vmware10.0.3设置为桥接模式. CentOS 7.0默认安装好之后是没有自动开启网络连接的! cd  / ...

  9. linux如何自动获取ip地址

    第一步:激活网卡 系统装好后默认的网卡是eth0,用下面的命令将这块网卡激活. # ifconfig eth0 up 第二步:设置网卡进入系统时启动 想要每次开机就可以自动获取IP地址上网,就要设置网 ...

随机推荐

  1. CCNP交换实验(5) -- 网关热备冗余

    HSRP:1.启用HSRP功能,并设置虚拟地址IP, 1为standby的组号.2.相同组号的路由器属于同一个HSRP组,所有属于同一个HSRP组的路由器的虚拟地址必须一致.3.HSRP的优先级默认为 ...

  2. Servlet过滤器——异常捕获过滤器

    1.概述 介绍如何实现异常捕获过滤器. 2.技术要点 本实例主要是在过滤器Filter的doFilter()方法中,对执行过滤器链的chain的doFilter()语句处添加try…catch异常捕获 ...

  3. HDU-1039-Easier Done Than Said?(Java &amp;&amp; 没用正則表達式是我的遗憾.....)

    Easier Done Than Said? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/O ...

  4. [Win]进程间通信——邮槽Mailslot

    进程间通信 进程的地址空间是私有的.出于安全性的目的,如果一个进程不具有特殊的权限,是无法访问另外一个进程的内存空间的,也无法知道内存中保存的数据的意义.但是在一些具体的应用情况下需要多个进行相互配合 ...

  5. 积累的VC编程小技巧之树操作

    1.如何在TreeList中加图标? [问题提出]  请问treeview控件和treectrl控件的用法有何不同呢?向如何imagelist控件中加图象呀?  [解决方法]  1)    HICON ...

  6. win32 sdk 列表视图控件绘制

    ////////////////////////////////////////////////////////////// LRESULT ListViewCustomDraw(HWND hwnd, ...

  7. [Usaco2008 Feb]Meteor Shower流星雨

    去年偶们湖南遭受N年不遇到冰冻灾害,现在芙蓉哥哥则听说另一个骇人听闻的消息: 一场流星雨即将袭击整个霸中,由于流星体积过大,它们无法在撞击到地面前燃烧殆尽, 届时将会对它撞到的一切东西造成毁灭性的打击 ...

  8. 键盘游戏之canvas--用OO方式写

    虽然写的不是很好,但 解释权以及版权仍然归13东倍所有!  <!DOCTYPE HTML> <html> <head> <title>canvas-00 ...

  9. 可能性dp+减少国家HDU4336

    Card Collector Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Subm ...

  10. anthelion编译

    编程工程 $ cd ./anthelion/anthelion/target/classes$ java -Xmx15G -cp ../Anthelion-1.0.0-jar-with-depende ...