WebCollector多代理切换机制

使用同一IP长期爬取网站容易被网站的反爬虫机制封杀IP。爬虫往往使用多代理的方法来应对反爬虫机制。

本教程利用WebCollector爬取大众点评,展示WebCollector的多代理切换机制,相关内容都在代码注释中。

教程中仅仅将网页保存在download文件夹中,如果需要抽取,请参考WebCollector其他教程。

import cn.edu.hfut.dmic.webcollector.crawler.BreadthCrawler;
import cn.edu.hfut.dmic.webcollector.model.Links;
import cn.edu.hfut.dmic.webcollector.model.Page;
import cn.edu.hfut.dmic.webcollector.net.HttpRequest;
import cn.edu.hfut.dmic.webcollector.net.HttpRequesterImpl;
import cn.edu.hfut.dmic.webcollector.net.RandomProxyGenerator;
import cn.edu.hfut.dmic.webcollector.util.Config;
import cn.edu.hfut.dmic.webcollector.util.FileUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 利用多代理爬取大众点评,适用于webcollector 2.07版
 *
 * @author hu
 */
public class DazhongCrawler extends BreadthCrawler {

    AtomicInteger id = new AtomicInteger(0);

    /**
     * @param crawlPath crawlPath is the path of the directory which maintains
     * information of this crawler
     * @param autoParse if autoParse is true,BreadthCrawler will auto extract
     * links which match regex rules from pag
     */
    public DazhongCrawler(String crawlPath, boolean autoParse) {
        super(crawlPath, autoParse);
        /*start page*/
        this.addSeed("http://www.dianping.com/");

        /*fetch url like http://www.dianping.com/xxxxxxx*/
        this.addRegex("http://www.dianping.com/.*");

        /*do not fetch jpg|png|gif*/
        this.addRegex("-.*\\.(jpg|png|gif).*");
        /*do not fetch url contains #*/
        this.addRegex("-.*#.*");
    }

    @Override
    public void visit(Page page, Links nextLinks) {
        try {
            /*保存网页到download文件夹中*/
            FileUtils.writeFileWithParent("download/" + id.incrementAndGet() + ".html", page.getContent());
        } catch (IOException ex) {
            Logger.getLogger(DazhongCrawler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * 爬取页面中的代理信息,将代理信息放入随机代理生成器中
     * 为了演示2.06版本引入的HttpRequest,这里抓取代理的过程用HttpRequest完成
     * 实际应用中,我们建议使用BreadthCrawler来完成对代理的抓取
     *
     * @param url 包含代理信息的页面
     * @param proxyGenerator 随机代理生成器
     * @throws Exception
     */
    public static void addProxy(String url, RandomProxyGenerator proxyGenerator) throws Exception {
        /*HttpRequest是2.07版的新特性*/
        HttpRequest request = new HttpRequest(url);
        /*重试3次*/
        for (int i = 0; i <= 3; i++) {
            try {
                String html = request.getResponse().getHtmlByCharsetDetect();
                String regex = "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}).+?([0-9]{1,4})";
                Pattern pattern = Pattern.compile(regex);
                Matcher matcher = pattern.matcher(html);
                while (matcher.find()) {
                    System.out.println("add proxy:" + matcher.group(1) + ":" + matcher.group(2));
                    String ip = matcher.group(1);
                    int port = Integer.valueOf(matcher.group(2));
                    proxyGenerator.addProxy(ip, port);
                }
                break;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws Exception {

        DazhongCrawler crawler = new DazhongCrawler("crawl_dazhong", true);

        crawler.setThreads(50);
        crawler.setTopN(100);

        /*使用代理时,爬虫各种等待时间需要加长,否则容易出现超时*/
        /*连接超时*/
        Config.TIMEOUT_CONNECT = 5000;
        /*读取超时*/
        Config.TIMEOUT_READ = 20000;
        /*在整个爬取过程中,包括断点续爬,同一个URL如果爬取超过MAX_RETRY次爬取失败,则放弃这个URL
         使用多代理爬取时,失败概率增加,所以需要将MAX_RETRY设置为一个较大的值*/
        /*注意,如果某个URL在某层中爬取失败,不要担心,这个URL会在后面的层中继续被爬取,
           直到爬取失败次数达到MAX_RETRY*/
        Config.MAX_RETRY = 30;
        /*爬取线程池如果超过requestMaxInterval的时间没有发送http请求,则强制停止线程池*/
        Config.requestMaxInterval = 1000 * 60 * 2;

        /*随机代理生成器,RandomProxyGenerator是WebCollector代理切换的一个插件*/
        /*用户可以根据自己的业务需求,定制代理切换的插件,代理切换插件需要实现ProxyGenerator*/
        RandomProxyGenerator proxyGenerator = new RandomProxyGenerator() {

            /*每当用一个代理爬取成功,会触发markGood方法*/
            @Override
            public void markGood(Proxy proxy, String url) {
                InetSocketAddress address = (InetSocketAddress) proxy.address();
                System.out.println("Good Proxy:" + address.toString() + "   " + url);
            }

            /*每当用一个代理爬取失败,会触发markBad方法*/
            @Override
            public void markBad(Proxy proxy, String url) {
                InetSocketAddress address = (InetSocketAddress) proxy.address();
                System.out.println("Bad Proxy:" + address.toString() + "   " + url);

                /*可以利用markGood或者markBad给出的反馈,来调整随机代理生成器中的代理*/
                /*可以动态添加或删除代理,这些操作都是线程安全的*/
                //removeProxy(proxy);

                /*随机代理RandomProxyGenerator是一种比较差的策略,
                  我们建议用户自己编写符合自己业务的ProxyGenerator。
                  编写ProxyGenerator主要实现ProxyGenerator中的next方法。*/
            }

        };

        for (int i = 1; i <= 5; i++) {
            /*从这些页面中爬取代理信息,加入proxyGenerator*/
            addProxy("http://proxy.com.ru/list_" + i + ".html", proxyGenerator);
        }

        /*获取爬虫的http请求器*/
        HttpRequesterImpl requester = (HttpRequesterImpl) crawler.getHttpRequester();
        /*设置http请求器的随机代理请求器*/
        requester.setProxyGenerator(proxyGenerator);

        //crawler.setResumable(true);
         /*start crawl with depth of 4*/
        crawler.start(30);

    }

}
时间: 2024-11-26 00:46:12

WebCollector多代理切换机制的相关文章

uCGUI 按键窗口切换机制(更新篇)

在之前文章中,讲述了一个低内存使用量的的窗口切换机制.有人会问,低内存使用量是多低呢,我这里举个例子.我有一个项目中使用到本切换机制,128*64的单色屏,初步计算有105个窗口(后面还会增加),总内存使用量(包括任务栈)=105*3*4(窗口树) + 1024(公共buff) + 512(任务栈) + 1024*3(uCGUI动态内存) = 6k左右.从这个数字可以看出内存算是使用比较少的了.毕竟有100多个窗口,我在设计时也就只做了10多个窗口,这100多个窗口就是由10多个窗口大量复用组合

通过简单的时间片轮转多道程序分析进程的启动和进程的切换机制

备注:秋风 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 1.配置好环境之后,使用qemu模拟器启动内核,运行效果如下: 2.实现时间片轮转多道程序调度的源码: mymain.c /*  *  linux/mykernel/mymain.c  *  *  Kernel internal my_start_kernel  *  *  Copyright (C) 2013  Men

记一次全站代理切换

这是一个忧伤的故事,首先要从一次故障说起.... 事故 先简要介绍一下公司网站架构, 代理服务器分为一级代理和二级代理, 一级代理是腾讯云的负载均衡,抗压能力比较强,防范ddos能力比较强,重要业务入口放在一级代理之上,比如www,gameapi,game等,然后反向代理到我们自己服务器. 二级代理是我们机房的服务器,lvs+keepalived高可用的,主要有两个作用: 老的SNS业务入口,以及一些访问量少的业务都在二级代理上. 备用,如果一级代理故障,紧急切换到二级代理上,保障线上业务正常.

Spark系列(五)Master主备切换机制

Spark Master主备切换主要有两种机制,之中是基于文件系统,一种是基于Zookeeper.基于文件系统的主备切换机制需要在Active Master挂掉后手动切换到Standby Master上,而基于Zookeeper的主备切换机制可以实现自动切换Master. 切换流程图 流程说明: Standby Master模式 1. 使用持久化引擎读取持久化的storeApps.storeDrivers.storeWorkers,持久化引擎有FileSystemPersistenceEngin

[转载]x86 的 TSS 任务切换机制

segment descriptors 构建保护模式下的最基本.最根本的执行环境.system descriptors 则构建保护模式下的核心组件: 1.TSS descriptor 提供硬件级的进程切换机制 2.LDT descriptor 供进程使用多个 descriptor 3.Gate descriptor 提供 processor 权限级别的切换机制. 5.7.1. TSS 提供的进程切换机制 TSS 是一段内存区域,存放进程相关的执行环境信息.初始化的 TSS 是由用户提供,进程切换

Keepalived中Master和Backup主备切换机制浅析

在keepalived的VRRP实例配置中会一般会设置Master和Backup来指定初始状态,但是这并不意味着此节点一直就是Master角色.控制节点角色的是Keepalived配置文件中的"priority"值和vrrp_script模块中设置的"weight"值.下面分别分情况对主备机切换机制作详细说明. 配置简介: 主机 IP 操作系统 软件 VIP 备注 nginx01 172.27.9.91 Centos7 keepalived nginx 172.27

【腾讯御安全】基于代理Application机制的Anddroid应用加壳方法

壳是指一个程序的外面再包裹上另外一段代码,保护里面的代码不被非法修改或反编译的程序.它们一般都先于程序运行,拿到控制权,然后完成它们保护软件的任务.对于Android APP来说,所有的实现逻辑都集成于DEX文件中,DEX文件是一个APP的核心所在,因此保护DEX文件被逆向或者修改尤其重要.Android应用的DEX加壳过程如下: 尽管上述DEX加壳过程中,关于如何对DEX进行加密或者利用其它方法对其进行处理的方法有很多,但是通过自定义DexClassLoader替换原DexClassLoade

深度模拟java动态代理实现机制系类之三

这里的内容就比较复杂了,要实现的是对任意的接口,对任意指定的方法,以及对任意指定的代理类型进行代理,就更真实的模拟出java虚拟机的动态代理机制 罗列一下这里涉及的类.接口之间的关系,方便大家学习.1.InvocationHandler接口,用来处理指定的方法,即对特定方法的代理,处理的具体实现交由子类实现2.TimeHandler类,实现了InvocationHandler接口的子类,具体的代理实现是进行时间代理3.Proxy类,用于产生代理类的类4.Moveable接口,举例过程中各类要实现

java中动态代理实现机制

JAVA各种动态代理实现的比较 接口 interface AddInterface{ int add(int a, int b); } interface SubInterface{ int sub(int a, int b); } 实现类 class Arithmetic implements AddInterface, SubInterface{ @Override public int sub(int a, int b) { return a-b; } @Override public i