webmagic icon indicating copy to clipboard operation
webmagic copied to clipboard

更多的代理ProxyProvider实现

Open code4craft opened this issue 7 years ago • 32 comments

新版ProxyProvider只有一个默认实现SimpleProxyProvider ,主要是作者考虑到实现一个复杂版本,第一不一定能完全理解需求,另外实现也没有经过检验,所以就抛砖引玉,先写一个简单可用的版本。

如果有更复杂的场景,欢迎回复此issue。

code4craft avatar May 29 '17 12:05 code4craft

您好 我在设置代理的时候,总是认证失败,都是我在同一个环境使用httpclient的代理设置是好的。跟踪调试也找不到思路啊,还请帮忙看看,不甚感激。谢谢。 其中:ChinaMoneyBundPageProcessorTest.java.txt 是我的程序。 run-log.txt 是日志信息

盼复!

ChinaMoneyBundPageProcessorTest.java.txt run-log.txt

很怪异,把认证的用户名和密码去掉以后就可以了,可是实际上是需要认证的。会不会是调用的系操作统保存的认证信息。

你好,原因是 程序里没有设置认证信息。只能自己解决了。

原因是因为公司的代理采用的是微软的域控用户来认证的。需要使用 NTCredentials ,所以 我重写了us.codecraft.webmagic.downloader.HttpUriRequestConverter类里面的convertHttpClientContext方法来处理的。问题解决了。

KeozenEric avatar Jun 06 '17 07:06 KeozenEric

是说Proxy这部分没有设置认证信息吗?这里是有设置的:

 HttpClientContext httpContext = new HttpClientContext();
        if (proxy != null && proxy.getUsername() != null) {
            AuthState authState = new AuthState();
            authState.update(new BasicScheme(), new UsernamePasswordCredentials(proxy.getUsername(), proxy.getPassword()));
            httpContext.setAttribute(HttpClientContext.PROXY_AUTH_STATE, authState);
        }

是说没生效吗?

code4craft avatar Jun 08 '17 08:06 code4craft

抓包看了下,有设置Authorization: Basic dXNlcjpwYXNz,应该是设置进去了。 不过不知道代理到底是不是识别这个字段,有用过认证代理的同学可以试用一下。

code4craft avatar Jun 10 '17 04:06 code4craft

http://git.oschina.net/virjar/proxyipcenter/blob/master/dungproxy-webmagic7/src/main/java/com/virjar/dungproxy/webmagic7/DungProxyProvider.java 这里有一个,不过我也没有测试其效果

virjar avatar Jun 25 '17 06:06 virjar

我在公司使用的是微软的域控认证的方式(即: DOAMIN/USERNAME + 用户密码的方式),这类认证通过BASIC 方式是没有作用的。需要使用 NTCredentials ,所以 我重写了us.codecraft.webmagic.downloader.HttpUriRequestConverter类里面的convertHttpClientContext方法来处理的。

谢谢 大家。

KeozenEric avatar Jun 25 '17 12:06 KeozenEric

是否可以开放接口在抓取过程中动态设置代理ip。

liwei3232004 avatar Aug 10 '17 01:08 liwei3232004

@virjar 你是怎么重写的 能否贴个详细demo

dengxiny avatar Oct 17 '17 09:10 dengxiny

请问如何在运行期间更新代理池,现在没办法隔一段时间自动替换新的代理池

yangdingjie avatar Oct 26 '17 07:10 yangdingjie

想要在爬虫运行区间动态更换ip代理池,要怎么实现呀?

lianjianlin avatar Oct 30 '17 02:10 lianjianlin

目前的我的做法是这样的

public static void getInfo() throws InterruptedException {

		// 配置动态代理
		// HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
		// httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(new
		// Proxy("forward.xdaili.cn", 80)));
		// spider.setDownloader(httpClientDownloader);

		// 配置混播代理
		HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
		String json = httpGet();
		System.out.println("初始化代理");
		System.out.println("初始化代理");
		System.out.println("初始化代理");
		System.out.println("初始化代理");
		System.out.println(json);
		System.out.println("初始化代理");
		System.out.println("初始化代理");
		System.out.println("初始化代理");
		System.out.println("初始化代理");
		JSONObject jo = JSONObject.parseObject(json);
		List<Map<String, String>> ips = (List<Map<String, String>>) jo.get("RESULT");
		httpClientDownloader.setProxyProvider(
				SimpleProxyProvider.from(new Proxy(ips.get(0).get("ip"), Integer.parseInt(ips.get(0).get("port"))),
						new Proxy(ips.get(1).get("ip"), Integer.parseInt(ips.get(1).get("port"))),
						new Proxy(ips.get(2).get("ip"), Integer.parseInt(ips.get(2).get("port"))),
						new Proxy(ips.get(3).get("ip"), Integer.parseInt(ips.get(3).get("port"))),
						new Proxy(ips.get(4).get("ip"), Integer.parseInt(ips.get(4).get("port")))));

		spider.setDownloader(httpClientDownloader);
		spider.start();
		Thread.sleep(12000);
		spider.stop();
		Thread.sleep(3000);
		System.gc();
		getInfo();
	}
}

yangdingjie avatar Nov 02 '17 02:11 yangdingjie

终于弄明白了 我之前认证问题的环境是在公司内部访问外网: 1、需要设置代理服务器 2、需要通过windows域控认证(问题解决主要在这) 我的解决方案: 1、设置代理服务器: String proxyServer = “xxx.xxx.xxx.xxx”; int proxyPort = 80; HttpHost proxy = new HttpHost(proxyServer, proxyPort); …… // HttpClientDownloader httpClientDownloader = new HttpClientDownloader(); httpClientDownloader.setHttpUriRequestConverter(httpUriRequestConverter); // SimpleProxyProvider proxyProvider = SimpleProxyProvider .from(new Proxy(proxy.getHostName(), proxy.getPort(), user, passwd)); httpClientDownloader.setProxyProvider(proxyProvider); …… 2、windows域控认证: 我是通过重写类 NTHttpUriRequestConverter extends HttpUriRequestConverter 的 convertHttpClientContext 方法: private HttpClientContext convertHttpClientContext(Request request, Site site, Proxy proxy) { HttpClientContext httpContext = new HttpClientContext(); if (proxy != null && proxy.getUsername() != null) { NTCredentials creds = new NTCredentials(proxy.getUsername(), proxy.getPassword(), null, domain); CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(new AuthScope(proxy.getHost(), proxy.getPort(), AuthScope.ANY_REALM, "ntlm"), creds); httpContext.setCredentialsProvider(credsProvider); } if (request.getCookies() != null && !request.getCookies().isEmpty()) { CookieStore cookieStore = new BasicCookieStore(); for (Map.Entry<String, String> cookieEntry : request.getCookies().entrySet()) { BasicClientCookie cookie1 = new BasicClientCookie(cookieEntry.getKey(), cookieEntry.getValue()); cookie1.setDomain(UrlUtils.removePort(UrlUtils.getDomain(request.getUrl()))); cookieStore.addCookie(cookie1); } httpContext.setCookieStore(cookieStore); } return httpContext; }

KeozenEric avatar Nov 13 '17 02:11 KeozenEric

你好,我使用的webmagic0.6版本的,目前需要支持ip代理池功能,我设置可几个可用的ip,也在代码中通过site.setHttpProxyPool(poolHosts,false)设置了ip池,我如何才能看到是使用我本机的ip抓取的,还是使用设置的ip代理抓取的呢,刚开始研究,很多东西不知道从何下手,希望能给指点一二,不胜感激

wangxy20171225 avatar Dec 25 '17 06:12 wangxy20171225

数据无忧http://www.data5u.com `/**

  • data5u ip代理

  • @author zhanghua */ public class Data5uProxyProvider implements ProxyProvider {

    private Logger logger = LoggerFactory.getLogger(Data5uProxyProvider.class);

    private String apiUrl;

    private Long previousGetTime;

    private int ttl;

    private Proxy proxy;

    public Data5uProxyProvider(String apiUrl) { this.apiUrl = apiUrl; }

    public static Data5uProxyProvider from(String apiUrl) { return new Data5uProxyProvider(apiUrl); }

    @Override public void returnProxy(Proxy proxy, Page page, Task task) {

    }

    @Override public Proxy getProxy(Task task) { try { long currentTime = System.currentTimeMillis(); if (proxy != null && previousGetTime != null && (currentTime - previousGetTime < ttl - 10)) { return proxy; } String asString = Request.Get(apiUrl).execute().returnContent().asString(Charset.forName("UTF-8")); Data5uReturnContent data5uReturnContent = decodeReturnText(asString); if (data5uReturnContent != null) { previousGetTime = currentTime; ttl = data5uReturnContent.getTtl(); proxy = new Proxy(data5uReturnContent.getIp(), data5uReturnContent.getPort()); return proxy; } } catch (IOException e) { logger.error("get proxy error,apiUrl:{},previousGetTime:{},ttl:{},proxy:{}", apiUrl, previousGetTime, ttl, proxy, e); }

     return null;
    

    }

    private Data5uReturnContent decodeReturnText(String returnText) { if (StringUtils.isNotBlank(returnText)) { String[] proxyTtl = StringUtils.split(returnText, ","); String proxyText = proxyTtl[0].trim(); int ttl = Integer.parseInt(proxyTtl[1].trim());

         String[] ipPort = StringUtils.split(proxyText, ":");
         String ip = ipPort[0].trim();
         int port = Integer.parseInt(ipPort[1].trim());
    
         return new Data5uReturnContent(ip, port, ttl);
     }
     return null;
    

    }

    class Data5uReturnContent {

     public Data5uReturnContent() {
     }
    
     public Data5uReturnContent(String ip, int port, int ttl) {
         this.ip = ip;
         this.port = port;
         this.ttl = ttl;
     }
    
     private String ip;
    
     private int port;
    
     private int ttl;
    
     public String getIp() {
         return ip;
     }
    
     public void setIp(String ip) {
         this.ip = ip;
     }
    
     public int getPort() {
         return port;
     }
    
     public void setPort(int port) {
         this.port = port;
     }
    
     public int getTtl() {
         return ttl;
     }
    
     public void setTtl(int ttl) {
         this.ttl = ttl;
     }
    

    } }`

akwolf avatar Jan 19 '18 08:01 akwolf

为什么我的其中一个代理ip出现403,以后新加入的代理ip还是403呢,

luoj01 avatar Jan 25 '18 06:01 luoj01

selenium 运行中怎么切换代理

a100488 avatar Dec 04 '18 08:12 a100488

这一直返给我,HttpResponseProxy{HTTP/1.1 407 Proxy Authentication Required [] org.apache.http.entity.BufferedHttpEntity@61a99341} 部分代码: public MyHttpClientGenerator() { Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory> create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", SSLConnectionSocketFactory.getSocketFactory()).build(); connectionManager = new PoolingHttpClientConnectionManager(reg); connectionManager.setDefaultMaxPerRoute(100); }

HttpClientContext httpContext = HttpClientContext.create(); if (proxy != null && proxy.getUsername() != null) { CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(new AuthScope(proxy.getHost(), proxy.getPort()), new UsernamePasswordCredentials(proxy.getUsername(), proxy.getPassword()));

        httpContext.setCredentialsProvider(credsProvider);
    }
    if (request.getCookies() != null && !request.getCookies().isEmpty()) {
        CookieStore cookieStore = new BasicCookieStore();
        for (Map.Entry<String, String> cookieEntry : request.getCookies().entrySet()) {
            BasicClientCookie cookie1 = new BasicClientCookie(cookieEntry.getKey(), cookieEntry.getValue());
            cookie1.setDomain(UrlUtils.removePort(UrlUtils.getDomain(request.getUrl())));
            cookieStore.addCookie(cookie1);
        }
        httpContext.setCookieStore(cookieStore);
    }
    return httpContext;

} 我用httpclient ,代理服务器可以用

hackeer avatar Dec 19 '18 11:12 hackeer

为什么我的其中一个代理ip出现403,以后新加入的代理ip还是403呢,

把新ip放到浏览器里看下,如果也是403,很不幸,这个ip应该也在黑名单里。 如果浏览器里正常,cookie和user-agent一起换

z1y1m1 avatar Feb 27 '19 14:02 z1y1m1

可能还有隧道代理这种类型

rxxy avatar Jun 01 '20 02:06 rxxy

大佬你好,代理ip有可能会过期,需要在运行期间动态更新,现在是事先在downloader里面设置死的,但是这样不能在运行期间动态修改,是否可以在Request类中支持一下设置代理,这样在addTargetRequest中就可以实现动态代理ip功能了

jason2015lxj avatar Jun 28 '20 10:06 jason2015lxj

你可以在download里面设置动态更新代理

只需要保证你这里每次都是去重新拿一个新的代理或者是等代理失效以后再去获取代理,也是可以的,跟你想要在request里面设置是同一个原理,因为request最后构建完了还是在这里发的请求

------------------ 原始邮件 ------------------ 发件人: "notifications"<[email protected]>; 发送时间: 2020年6月28日(星期天) 晚上6:28 收件人: "code4craft/webmagic"<[email protected]>; 抄送: "家的N次方"<[email protected]>;"Comment"<[email protected]>; 主题: Re: [code4craft/webmagic] 更多的代理ProxyProvider实现 (#579)

大佬你好,代理ip有可能会过期,需要在运行期间动态更新,现在是事先在downloader里面设置死的,但是这样不能在运行期间动态修改,是否可以在Request类中支持一下设置代理,这样在addTargetRequest中就可以实现动态代理ip功能了

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

lianjianlin avatar Jun 28 '20 10:06 lianjianlin

原理,因为request最后构建完了还是在这里发的请求

哦,明白了,就是搞一个动态更新downloader里面的代理池就可以了把

jason2015lxj avatar Jun 28 '20 10:06 jason2015lxj

是的

------------------ 原始邮件 ------------------ 发件人: "notifications"<[email protected]>; 发送时间: 2020年6月28日(星期天) 晚上6:43 收件人: "code4craft/webmagic"<[email protected]>; 抄送: "家的N次方"<[email protected]>;"Comment"<[email protected]>; 主题: Re: [code4craft/webmagic] 更多的代理ProxyProvider实现 (#579)

原理,因为request最后构建完了还是在这里发的请求

哦,明白了,就是搞一个动态更新downloader里面的代理池就可以了把

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

lianjianlin avatar Jun 29 '20 01:06 lianjianlin

我实现了一个 DelayedProxyProvider,并开了一个独立的 Spider 去抓取网上一些免费的 Proxy。目前还在测试中。

sutra avatar Jun 29 '20 02:06 sutra

好的

------------------ 原始邮件 ------------------ 发件人: "notifications"<[email protected]>; 发送时间: 2020年6月29日(星期一) 上午10:26 收件人: "code4craft/webmagic"<[email protected]>; 抄送: "家的N次方"<[email protected]>;"Comment"<[email protected]>; 主题: Re: [code4craft/webmagic] 更多的代理ProxyProvider实现 (#579)

我实现了一个 DelayedProxyProvider,并开了一个新的 Spider 去抓取网上一些免费的 Proxy。目前还在测试中。

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

lianjianlin avatar Jun 29 '20 02:06 lianjianlin

我实现了一个 DelayedProxyProvider,并开了一个独立的 Spider 去抓取网上一些免费的 Proxy。目前还在测试中。

其中仅 DelayedProxyProvider 部分代码位于 sutra/webmagic-delayed-proxy

sutra avatar Jul 06 '20 15:07 sutra

是否可以把setProxy作为一个downloader的接口?我的项目中实现了自己的downloader,然后发现无法使用setProxy,这样的话就得自己重新写一遍代理实现,感觉不太方便

tribbleofjim avatar Dec 29 '20 14:12 tribbleofjim

是否可以把setProxy作为一个downloader的接口?我的项目中实现了自己的downloader,然后发现无法使用setProxy,这样的话就得自己重新写一遍代理实现,感觉不太方便

老铁,后来这个问题你怎么解决的

kiwi-field avatar Jan 28 '21 06:01 kiwi-field

是否可以把setProxy作为一个downloader的接口?我的项目中实现了自己的downloader,然后发现无法使用setProxy,这样的话就得自己重新写一遍代理实现,感觉不太方便

老铁,后来这个问题你怎么解决的

我最后还是自己重新写了代理实现……而且后来发现不太好用就没有用代理了

tribbleofjim avatar Jan 28 '21 07:01 tribbleofjim

后来我研究了一下,是在seleniumDownloader下载器中添加了代理,可以使用了

kiwi-field avatar Jan 31 '21 03:01 kiwi-field

一般爬虫都需要免费的或者收费的ip池来支持的,我这边实现了一个动态的获取代理,并在爬虫执行过程中对失效的代理剔除,并从ip代理商那边获取新的可用的代理来补充我们的ip池。不多说了,上代码,仅供参考。

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.proxy.Proxy;
import us.codecraft.webmagic.proxy.ProxyProvider;

import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class DynamicProxyProvider implements ProxyProvider {
    // 日志
    private Logger logger = LoggerFactory.getLogger(getClass());
    // 轮询起始位置
    private final AtomicInteger pointer = new AtomicInteger(-1);
    // url 一次获取一个ip
    private final String ipPoolProxyUrl;
    private final int ipPoolSize;
    // 代理池
    private List<Proxy> proxies;
	
    public DynamicProxyProvider(String ipPoolProxyUrl, int ipPoolSize) {
        this.ipPoolProxyUrl = ipPoolProxyUrl;
        this.ipPoolSize = ipPoolSize;
        //初始化 ip池,达到指定容量
        this.proxies = get(ipPoolProxyUrl);
        while (this.proxies.size() < ipPoolSize) {
            List<Proxy> rs = get(ipPoolProxyUrl);
            this.proxies.addAll(rs);
        }
    }

    /**
     * 反向求出哪个代理下载了页面
     *
     * @param proxy
     * @param page
     * @param task
     */
    @Override
    public void returnProxy(Proxy proxy, Page page, Task task) {
        // todo 暂时用不到
    }

    @Override
    public Proxy getProxy(Task task) {
        // todo 从redis获取ip和端口,然后在切换,采用轮循方式获取代理
        if (proxies.size() == 0) {
            logger.error("redis获取的ip数量为: 0!" + proxies.size());
            return null;
        }
        logger.info("redis获取的ip数量为: " + proxies.size());
        Proxy proxy = proxies.get(incrForLoop(proxies));
        // todo 验证 Ip 是否可用,如果不可用直接去代理方新获取一个来用,并补充道redis中,删除不可用的那个。
        Boolean isIpUsefull = checkIpUsefull(proxy.getHost(), proxy.getPort());
        if (!isIpUsefull) {
            // 删除不可用
            proxies.remove(proxy);
            // 获取新的可用ip,填充到list
            List<Proxy> rs = get(ipPoolProxyUrl);
            proxies.addAll(rs);
            // 重新获取Ip,这里是刚从ip池供应商获得的,没有必要去验证有效性了。
            proxy = rs.get(0);
        }
        return proxy;
    }

    // 轮循算法
    private int incrForLoop(List<Proxy> proxies) {
        int p = pointer.incrementAndGet();
        int size = proxies.size();
        if (p < size) {
            return p;
        }
        while (!pointer.compareAndSet(p, p % size)) {
            p = pointer.get();
        }
        return p % size;
    }

    //检查代理有效性
    private static boolean checkIpUsefull(String ip, Integer port) {
        URL url;
        try {
            url = new URL("http://www.baidu.com");
            InetSocketAddress addr = new InetSocketAddress(ip, port);
            java.net.Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, addr);
            InputStream in;
            try {
                URLConnection conn = url.openConnection(proxy);
                conn.setConnectTimeout(2000);
                conn.setReadTimeout(2000);
                in = conn.getInputStream();
            } catch (Exception e) {
                return false;
            }
            String s = IOUtils.toString(in);
            if (s.indexOf("baidu") > 0) {
                return true;
            }
            return false;
        } catch (Exception e) {
            return false;
        }
    }
    // 获取代理方的ip

    /**
     * Get请求
     *
     * @param url URL地址
     * @return 返回结果
     */
    private List<Proxy> get(String url) {
        List<Proxy> result = new ArrayList<>();
        try {
            OkHttpClient okHttpClient = new OkHttpClient();
            Request request = new Request.Builder().url(url).build();
            Response response = okHttpClient.newCall(request).execute();
            if (response.code() != 200) {
                logger.error("从Ip供应商获取ip失败:{}", result);
            }
            JSONObject jsonObject = (JSONObject) JSONObject.parse(response.body().string());
            JSONArray jsonArray = (JSONArray) jsonObject.get("data");
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonProxy = (JSONObject) jsonArray.get(i);
                Proxy proxyTmp = new Proxy(jsonProxy.get("ip").toString(), Integer.valueOf(jsonProxy.get("port").toString()));
                result.add(proxyTmp);
            }
            return result;
        } catch (Exception e) {
            logger.error("从Ip供应商获取ip请求异常:", e);
            return result;
        }
    }
}

CarlKong avatar Nov 16 '21 06:11 CarlKong