爬虫(第一篇) IP代理池

爬虫(第一篇) IP代理池搞虫子的都知道,IP代理是必要的方法,为什么?这个大家知道就好了,最近写了个IP代理池,给大家围观一下:开始。首先咱们找到一个免费的IP代理网站,读取人家的数据,但是注意了,注意频率别把人家给搞崩了第一:线程池,多个线程检测packagecom.*.util.thread;importorg.apache.log4j.Logger;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Ex

大家好,又见面了,我是你们的朋友全栈君。

搞虫子的都知道,IP代理是必要的方法,为什么?这个大家知道就好了,最近写了个IP代理池,给大家围观一下:开始。

首先咱们找到一个免费的IP代理网站,读取人家的数据,但是注意了,注意频率 别把人家给搞崩了

本服务采用的依赖:Springboot、apache util、jsoup、fastjson、Redis 等

第一:线程池,多个线程检测

package com.*.util.thread;

import org.apache.log4j.Logger;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/***
 * *
 * 类名称:		CustomExecutorService   
 * 类描述:		线程池配置   
 * 创建人:		
 * 创建时间:	2014-11-21 下午4:45:26   
 * 修改人:		
 * 修改时间:	2016-10-21 下午4:45:26   
 * 修改备注:   
 * @version	1.1
 */
public class CustomExecutorService {

	private static Logger log = Logger.getLogger( CustomExecutorService.class ) ;
	
	private static ExecutorService executorService; 		// 线程池
	private static final int POOL_MULTIPLE = 0x0000000a ; 	// 线程池中单个CPU分配工作线程的数目(十六进制)
	private static CustomExecutorService instance;

	private CustomExecutorService() {
		// 创建一个线程池                            可用处理器数*线程池工作数
		executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * POOL_MULTIPLE * 5 );
	}
	
	/****
	 * 返回线程池
	 * @return
	 */
	public static ExecutorService getExecutorService(){
		return executorService ;
	}

	/***
	 * 一次调用就可以了,很多地方其实不再需要再次调用,在系统启动的时候调用一次就可以了
	 * @return
	 */
	public synchronized static CustomExecutorService getInstance() {
		if (instance == null) {
			instance = new CustomExecutorService();
			log.info( "Thread pool instance success" ) ;
		}
		return instance;
	}
	
	/****
	 * 一次调用就可以了,在系统关闭的时候调用一次就可以了
	 */
	public static void destory() {
		log.info( "Thread pool shutdown ..." ) ;
		executorService.shutdown() ;
	}

	/****
	 * 具体执行线程的调用
	 * @param thread
	 */
	public static void execute( Runnable thread ) {
		if( executorService != null ){
			executorService.execute(thread);
		}else{
			CustomExecutorService.getInstance() ;
			try{ Thread.sleep( 1 ); }catch (Exception e){}
			execute( thread ) ;
			log.error( "Thread pool haven't instance,please instance Thread pool." ) ;
		}
	}
	
	public static void main(String[] args) {
		CustomExecutorService.getInstance() ;		//线程池初始化 --- 系统启动时调用一次就ok
		CustomExecutorService.execute( new Thread() ) ;		
		CustomExecutorService.destory() ;
	}

}

第二:定时器,定时处理Redis中无效的IP

package *.*.*.ipproxy;

import com.alibaba.fastjson.JSONObject;
import com.*.util.thread.CustomExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @Date 创建时间: 2021-01-18 15:58
 * @Author 作者姓名: WuNengDeShiXiong
 * @Version 1.0
 * @Copyright Copyright by
 * @Direction 类说明
 */
@Component
@Lazy(value = false)
@EnableScheduling
public class IpSourceValid {

    private final static Logger logger = LoggerFactory.getLogger( IpSourceValid.class ) ;

    @Autowired
    private RedisTemplate redisTemplate;

    /****
     * 已有的代理池,则需要30秒刷新一次,不可用的IP代理池、全部丢弃
     */
    @Scheduled(cron = "0 * * * * ?")
    void update() {
        logger.info( " redis IP代理池检测开始 ....................."  );
        List<String> range = redisTemplate.opsForList().range("ip", 0, -1);
        for (String ipInfoJson : range) {
            //线程池检测-代理IP池-有效的IP有哪些
            CustomExecutorService.execute(new Thread() {
                @Override
                public void run() {
                    IPInfo ipInfo = JSONObject.parseObject( ipInfoJson , IPInfo.class ) ;
                    //使用时-要求偏低,不可用时从Redis删除
                    Boolean useLess = IpInvalidUtils.useless( ipInfo.getIp() , Integer.parseInt(ipInfo.getPort() ) , false ) ;
                    if ( useLess ) {
                        logger.info( " {} 从redis移除" , ipInfoJson );
                        redisTemplate.opsForList().remove("ip", 0, ipInfoJson );
                    }
                }
            }) ;
        }
        logger.info( " redis IP代理池检测结束 ....................."  );
    }
    
}

第三: 从开源网站读取的IP转换成对象

package *.*.*.ipproxy;

import org.jsoup.select.Elements;

import java.io.Serializable;

/**
 * @Date 创建时间: 2021-01-14 11:56
 * @Author 作者姓名: WuNengDeShiXiong
 * @Version 1.0
 * @Copyright Copyright by
 * @Direction 类说明
 */
public class IPInfo implements Serializable {

    private String ip ;
    private String port ;
    private String city ;
    private String type ;
    private String validTime ;
    private String source ;

    public IPInfo(){}

    public IPInfo(Elements tdChilds){
        if ( tdChilds != null  && tdChilds.size() > 1 ) {
            this.ip = tdChilds.get(0).text() ;
            this.port = tdChilds.get(1).text() ;
            this.city = "中国" + tdChilds.get(2).text() ;
            this.type = tdChilds.get(3).text() ;
            this.validTime = tdChilds.get(4).text() ;
        }
    }

    get set ......
}

 第四:使用简单的java连接使用代理去访问牛皮的网站,此处使用的是QQ的地址,响应速度快

package *.*.*.ipproxy;

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Date 创建时间: 2021-01-14 10:22
 * @Author 作者姓名: WuNengDeShiXiong
 * @Version 1.0
 * @Copyright Copyright by
 * @Direction 类说明       验证代理的IP与端口
 */
public class IpInvalidUtils {

    private final static Logger logger = LoggerFactory.getLogger( IpSourceValid.class ) ;
    private final static String valid_url = "https://graph.qq.com/oauth2.0" ;      //自定义一个网站地址就可以了,百度、腾讯、阿里能够访问即可,返回信息越少越好

    private final static String User_Agent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4240.183 Safari/537.36" ;

    private final static int connent_timeout_normal = 2000 ;
    private final static int connent_timeout_slow = 5000 ;

    /****
     * 内部代理方法
     * @param ip
     * @param port
     * @return
     */
    public static boolean useless( String ip , Integer port){
        return useless(  ip ,  port , true ) ;
    }

    /****
     * 验证IP、端口 是否可用
     * @param ip
     * @param port
     * @return 无效的ip 返回true 有效的ip返回false
     */
    public static boolean useless( String ip , Integer port, Boolean high) {
        Connection jsoupConn = null;
        boolean requestIsFail = true ;
        try {
            //创建请求
            jsoupConn = Jsoup.connect( valid_url ).userAgent( User_Agent ) ;
            jsoupConn.proxy( ip , port ) ;
            if( high ) {
                //要求高-则检测时间必须在2秒内响应
                jsoupConn.timeout( connent_timeout_normal );     // timeout:设置连接主机超时(单位:毫秒)
            }else{
                //要求低-则检测时间必须在响应
                jsoupConn.timeout( connent_timeout_slow );       // timeout:设置连接主机超时(单位:毫秒)
            }
            //解析请求信息 能够解析到请求结果的一律认为访问成功
            Connection.Response resp = jsoupConn.execute();
            if ( resp.statusCode() > 0 ) {
                //String body = jsoupConn.get().getElementsByTag("body").html();
                requestIsFail = false ;
            }
        } catch (Exception e) {
            logger.error( "数据识别 此IP: {} 端口:{} 失败的原因:{}" , ip , port , e.getMessage() );
        }
        return requestIsFail ;
    }


}

第五步:咱们就要定时去网站取数据了

package *.*.*.ipproxy;

import com.alibaba.fastjson.JSON;
import com.*.util.thread.CustomExecutorService;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.List;

/**
 * @Date 创建时间: 2021-01-14 10:21
 * @Author 作者姓名: WuNengDeShiXiong
 * @Version 1.0
 * @Copyright Copyright by
 * @Direction 类说明       定时取一个免费的源,然后对免费的源获取的IP做数据检查
 *                        多线程获取-然后做数据检测
 */
@Component
@Lazy(value = false)
@EnableScheduling
public class UpdateIpSource1 {

    private final static Logger logger = LoggerFactory.getLogger( UpdateIpSource1.class ) ;

    @Autowired
    private RedisTemplate redisTemplate;

    /***
     * 获取可用的IP代理池,10分钟运行一次
     */
    @Scheduled(cron = "0 */10 * * * ?")
    public void ips() {
        String prefix = "http://www.66ip.cn/areaindex_" ;
        String suxfix = "/1.html" ;
        StringBuilder url = null ;
       //只取前5页的有效数据
       for(int i=1 ;i<5 ;i++){
           url = new StringBuilder( prefix ).append( i ).append( suxfix ) ;
           parseUrl( url.toString() ) ;
       }
    }

    /****
     * 解析指定地址,然后从地址内获取IP、端口;
     * @param url
     */
    public void parseUrl( String url ){
        try {
            //Document document = Jsoup.connect( "http://www.66ip.cn/areaindex_1/1.html" ).timeout(3000).get();
            Document document = Jsoup.connect( url ).timeout(3000).get();
            Elements tags = document.select("table > tbody > tr");
            for (Element element : tags) {
                //取得ip地址节点、端口号节点
                Elements tdChilds = element.select("td");
                logger.info( " 准备验证数据 {}    ------source1------" , tdChilds.text() );
                IPInfo ipInfo = new IPInfo( tdChilds ) ;
                ipInfo.setSource("1");
                redisHandler( ipInfo ) ;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*****
     * 验证读取到的IP地址是否有效,并且写入到Redis服务
     * @param ipInfo
     */
    public void redisHandler( IPInfo ipInfo ){
        //第一步检测数据有效性,且只有有效数据才做数据检测
        if ( ipInfo != null && StringUtils.isNotBlank( ipInfo.getIp() ) && StringUtils.isNotBlank( ipInfo.getPort() ) ) {
            try {
                Integer.parseInt(ipInfo.getPort() ) ;
            }catch (Exception e){
                logger.info("端口号为非数字,丢弃请求.................------source1------");
                return ;
            }
            //第二步 采用多线程对IP端口做代理检测
            CustomExecutorService.execute(new Thread(){
                @Override
                public void run() {
                    Boolean useLess = IpInvalidUtils.useless( ipInfo.getIp() , Integer.parseInt(ipInfo.getPort() )) ;
                    if ( ! useLess ) {
                        String ipInfoJson = JSON.toJSONString( ipInfo ) ;
                        //将有效的数据移动到最左边,第一位
                        List<String> range = redisTemplate.opsForList().range("ip", 0, -1 );
                        if ( ! range.contains( ipInfoJson ) ) {
                            logger.info( " {} 有效,开始-存进redis {}    ------source1------" ,  ipInfoJson );
                            if (redisTemplate.opsForList().size("ip") > 1000) {
                                redisTemplate.opsForList().rightPopAndLeftPush("ip", ipInfoJson);
                            }else{
                                redisTemplate.opsForList().leftPush("ip", ipInfoJson );
                            }
                        }
                    }
                }
            }) ;
        }
    }

}

好了,今天的代理池获取就完成了。引用的Maven请看下文  Springboot基础包就不发了

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.47</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
	<groupId>org.jsoup</groupId>
	<artifactId>jsoup</artifactId>
	<version>1.11.3</version>
</dependency>


apache的Utils包就不贴了,大家都用

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/134090.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • sklearn linear regression_auto sklearn

    sklearn linear regression_auto sklearnK折交叉验证:sklearn.model_selection.KFold(n_splits=3,shuffle=False,random_state=None)思路:将训练/测试数据集划分n_splits个互斥子集,每次用其中一个子集当作验证集,剩下的n_splits-1个作为训练集,进行n_splits训练和测试,得到n_splits个结果注意点:对于不能均等份的数据集,其前n_sa

  • 面向对象的数据库db4o: 安装并使用db4o

    面向对象的数据库db4o: 安装并使用db4o为什么80%的码农都做不了架构师?>>>…

  • 设计模式(二十一)状态模式

    设计模式(二十一)状态模式

  • 浅谈golang中的代理模式

    浅谈golang中的代理模式来自一个大佬的博客,建议食用设计模式不分语言,是一种思维层面的体现,但是不能在不同语言中使用同一套实现(每种语言有不同的特性),比如go,本身是没有继承一说,但是通过结构体的组合来实现语义上的继承。而多态也是通过接口的方式来实现的。下方的图来自于大佬博客,贴在这里方便查看!!!设计原则设计模式结构型模式代理模式首先,我们知道代理模式中分为静态代理和动态代理。静态代理需要在编译前就要写好,而动态代理需要在运行时通过反射来实现方法增强。上述的话,太过粗糙,下面列举一下双方的区别:静态代理:

  • android 点餐系统 构思

    android 点餐系统 构思一.          为什么要做这个项目? 记的有一次看新闻,其中报道过台湾一家酒店使用ipad让客人自己点餐,客人可以使用这个ipad从全部菜中挑选自己喜欢的,又可以选择自己的特色的。还可以直接结帐。我就想了一下,为什么不在android 系统上做一个人呢,因为以后这个系统的普及度一定很高的。于是我就上网查了一下相关的项目。发现有好多人已经开始做了,我自己并没有调研,就附上别人调研的情

  • 圆柱体积在线计算机,(完整版)圆柱体积计算练习题.docx

    圆柱体积在线计算机,(完整版)圆柱体积计算练习题.docx柱的表面和体积的计算练习题一个蓄水池是圆柱形的,底面面积为31.4平方分米,高2.8分米,这个水池最多能容多少升水?一个圆柱体的高是37.68厘米,它的侧面展开后恰好是正方形,这个圆柱体的体积是多少?一个圆柱形水桶的体积是24立方分米,底面积是6平方分米,桶的装满了水,求水面高是多少分米?一个圆柱形量桶,底面半径是5厘米,把一块铁块从这个量桶里取出后,水面下降厘米,这块…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号