首页>代理ip知识与免费资源>正文
如何构建爬虫代理ip池
发布日期:2019/8/26 13:50:16    阅读量:10907

做网络爬虫一般对代理IP的需求量比较大。因为在爬取网站信息的过程中,很多网站做了反爬虫策略,可能会对每个IP做频次控制。这样我们在爬取网站时就需要很多代理IP。

QQ截图20190822111724.jpg

一般通过一下几种途径获取到ip:


1.从免费的网站上获取,获取的工作量非常大,ip可用率低,质量没有保障,获取到仅有的ip还未必安全,所以一般专业爬虫的工作者,不会去抓取免费代理ip。


2.在平台购买现成收费的代理ip,ip质量高,高匿安全稳定,ip速度快,从而节约更多的时间去增加工作效率,如开心代理ip就是非常不错代理软件平台,ip丰富,自建机房比遍布全国,专业技术团队,优质的售后服务,是一家值得信赖的高质量代理ip服务商。


3.自己搭建代理服务器,建立独立机房,完全按自己的要求去搭建,代理ip绝对完美,但是搭建成本非常高,后期还需要专业的人定期代理维护,在性价比的情况下,还是不太适合适用。

本文的代理IP池是通过爬虫事先从多个免费网站上获取代理IP之后,再做检查判断IP是否可用,可用的话就存放到MongoDB中,最后展示到前端的页面上。


获取可用Proxy


获取代理的核心代码是ProxyManager,它采用RxJava2来实现,主要做了以下几件事:


创建ParallelFlowable,针对每一个提供免费代理IP的页面并行地抓取。


Flowable.fromIterable(ProxyPool.proxyMap.keySet())

                .parallel()

针对每一个页面进行抓取,返回List


map(new Function() {

                @Override

                public List apply(String s) throws Exception {


                    try {

                        return new ProxyPageCallable(s).call();

                    } catch (Exception e) {

                        e.printStackTrace();

                    }


                    return null;

                }

            })

对每一个页面获取的代理IP列表进行校验,判断是否可用


flatMap(new Function() {

                @Override

                public Publisher apply(List proxies) throws Exception {


                    if (proxies == null) return null;


                    List result = proxies                            .stream()

                            .parallel()

                            .filter(new Predicate() {

                        @Override

                        public boolean test(Proxy proxy) {


                            HttpHost httpHost = new HttpHost(proxy.getIp(), proxy.getPort(), proxy.getType());

                            return HttpManager.get().checkProxy(httpHost);

                        }

                    }).collect(Collectors.toList());


                    return Flowable.fromIterable(result);

                }

            })

依次保存到proxyList


subscribe(new Consumer() {

                @Override

                public void accept(Proxy proxy) throws Exception {

                    log.debug("Result Proxy = "+proxy.getType()+"://"+proxy.getIp()+":"+proxy.getPort());

                    proxy.setLastSuccessfulTime(new Date().getTime());

                    ProxyPool.proxyList.add(proxy);

                }

            });


ProxyManager代码:


import com.cv4j.proxy.domain.Proxy;import com.cv4j.proxy.http.HttpManager;import com.cv4j.proxy.task.ProxyPageCallable;import io.reactivex.Flowable;import io.reactivex.functions.Consumer;import io.reactivex.functions.Function;import lombok.extern.slf4j.Slf4j;import org.apache.http.HttpHost;import org.reactivestreams.Publisher;import org.springframework.stereotype.Component;import java.util.Date;import java.util.List;import java.util.function.Predicate;import java.util.stream.Collectors;/**

 * Created by tony on 2017/10/25.

 */@Slf4j@Componentpublic class ProxyManager {


    /**

     * 抓取代理,成功的代理存放到ProxyPool中

     */

    public void start() {


        Flowable.fromIterable(ProxyPool.proxyMap.keySet())

                .parallel()

                .map(new Function() {

                    @Override

                    public List apply(String s) throws Exception {


                        try {

                            return new ProxyPageCallable(s).call();

                        } catch (Exception e) {

                            e.printStackTrace();

                        }


                        return null;

                    }

                })

                .flatMap(new Function() {

                    @Override

                    public Publisher apply(List proxies) throws Exception {


                        if (proxies == null) return null;


                        List result = proxies                                .stream()

                                .parallel()

                                .filter(new Predicate() {

                            @Override

                            public boolean test(Proxy proxy) {


                                HttpHost httpHost = new HttpHost(proxy.getIp(), proxy.getPort(), proxy.getType());

                                return HttpManager.get().checkProxy(httpHost);

                            }

                        }).collect(Collectors.toList());


                        return Flowable.fromIterable(result);

                    }

                })

                .sequential()

                .subscribe(new Consumer() {

                    @Override

                    public void accept(Proxy proxy) throws Exception {

                        log.debug("Result Proxy = "+proxy.getType()+"://"+proxy.getIp()+":"+proxy.getPort());

                        proxy.setLastSuccessfulTime(new Date().getTime());

                        ProxyPool.proxyList.add(proxy);

                    }

                });

    }}

定时任务


每隔几个小时跑一次定时任务,在抓取完任务之后先删除旧的数据,然后再把新的数据插入到MongoDB中。


import com.cv4j.proxy.ProxyManager;import com.cv4j.proxy.ProxyPool;import com.cv4j.proxy.dao.ProxyDao;import com.cv4j.proxy.domain.Proxy;import com.safframework.tony.common.utils.Preconditions;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;import java.util.concurrent.CopyOnWriteArrayList;/**

 * Created by tony on 2017/11/22.

 */@Componentpublic class ScheduleJobs {


    @Autowired

    ProxyDao proxyDao;


    @Autowired

    ProxyManager proxyManager;


    /**

     * 每六个小时跑一次任务

     */

    @Scheduled(cron = "0 0 */6 * * ?")

    public void cronJob() {

        System.out.println("Job Start...");


        proxyManager.start();


        CopyOnWriteArrayList list = ProxyPool.proxyList;


        // 先删除旧的数据

        proxyDao.deleteAll();


        // 然后再进行插入新的proxy

        if (Preconditions.isNotBlank(list)) {


            for (Proxy p:list) {


                proxyDao.saveProxy(p);

            }

        }


        System.out.println("Job End...");

    }}

展示到前端


在使用前,还可以再做一次检测,只要双击某个代理IP即可。


在第二次检测时,对于已经失效的IP会被ProxyPool删除


开心代理官网:http://www.kxdaili.com