jhao104 / proxy_pool

Python ProxyPool for web spider
https://jhao104.github.io/proxy_pool/
MIT License
21.48k stars 5.17k forks source link

功能建议 #441

Open lazyxiaoming opened 4 years ago

lazyxiaoming commented 4 years ago

在这两天的个人使用中,增加了一些接口并发现了一些功能完善建议,看下是否合理。

新增接口

1. 获取最优代理接口

爬取过程中我不并想要随机代理,即使我已经把校验代理可用性的周期已经改为了10s,但还是有很多的随机代理连续不可用的情况,导致抓取失败率较高。我更希望每次获取最优代理,在代理有效期内最大程度地利用此代理。选取标准基于check_count以及fail_count的差值。 核心语句: choice = max(item_list, key=lambda d: json.loads(d)['check_count'] - json.loads(d)['fail_count'])

2. 更新check_count接口以及更新fail_count接口

当前check_count仅仅在校验过程中发生更新。但由于我新增了最优代理接口,希望在实际使用代理过程中可以实时更新其check_count以及fail_count,从而可以持续保持代理的最优性。 核心语句:

    def update_check_count(self, proxy_str):
        self.db.changeTable(self.useful_proxy_queue)
        value = self.db.get(proxy_str)
        if value:
            dic = json.loads(value)
            dic['check_count'] += 1
            value = json.dumps(dic)
            try:
                self.db.update(proxy_str, value)
                return {'code': 1, 'msg': 'success', 'proxy': dic['proxy'], 'check_count': dic['check_count']}
            except Exception as e:
                print(e)
                return {'code': 0, 'msg': 'update failed'}
        else:
            return {'code': 0, 'msg': 'proxy is not exist'}

    def update_fail_count(self, proxy_str):
        self.db.changeTable(self.useful_proxy_queue)
        value = self.db.get(proxy_str)
        if value:
            dic = json.loads(value)
            dic['fail_count'] += 1
            value = json.dumps(dic)
            try:
                self.db.update(proxy_str, value)
                return {'code': 1, 'msg': 'success', 'proxy': dic['proxy'], 'fail_count': dic['fail_count']}
            except Exception as e:
                print(e)
                return {'code': 0, 'msg': 'update failed'}
        else:
            return {'code': 0, 'msg': 'proxy is not exist'}

功能建议

1. fail_count的变化逻辑

/proxy_pool/ProxyHelper/ProxyUtil.py中,校验成功则check_count+=1, if fail_count>0: fail_count-=1; 校验失败则check_count+=1, fail_count+=1。可以看到,check_count代表校验次数没有问题,而fail_count既承担了失败数的角色,又承担了成功数的角色,我认为模糊了此字段,不能达到应有的效果。比如说,我想找一个最优代理,自然是看成功次数或是成功率,但由于fail_count在校验成功反而会减1,那么我无法通过这两个字段得到确切的成功次数或是成功率。也就是说,在原有逻辑中,我想找最优代理,只能从fail_count最小的代理中去找,但这样是不足以找最优代理的,比如一个fail_count=0, check_count=10的代理可能代表成功10次,也可能代表成功5次失败5次,是一个模糊的组合。 改进逻辑: 要么增加一个新字段suc_count代表成功数,要么在校验成功时fail_count不自减,这样字段组合就可以唯一性确定代理的校验情况。我采用的是后者。见:

def checkProxyUseful(proxy_obj):
    """
    检测代理是否可用
    :param proxy_obj: Proxy object
    :return: Proxy object, status
    """

    if validUsefulProxy(proxy_obj.proxy):
        # 检测通过 更新proxy属性
        proxy_obj.check_count += 1
        proxy_obj.last_status = 1
        proxy_obj.last_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        # if proxy_obj.fail_count > 0:
        #     proxy_obj.fail_count -= 1
        return proxy_obj, True
    else:
        proxy_obj.check_count += 1
        proxy_obj.last_status = 0
        proxy_obj.last_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        proxy_obj.fail_count += 1
        return proxy_obj, False

以上新增功能及建议均不影响原有功能,可以讨论下合理性以及必要性。

jhao104 commented 4 years ago

3q, 这就相当于某种评分机制,代理某次请求不通了还保留认为其可能为恢复 . 其实在实际使用中,代理一次不通直接就扔掉,拿下一个,代理本身失效就快,代码里面默认fail_count大于0就扔掉的,设计之初就不想加入这个机制,因为这样会到整个池的ip有部分是不确定100%能用的,保留不好的代理来增加池子数量并不好。

如果说要筛选最优代理的话,应该是从响应速度和有效时长来考虑,对垃圾代理零容忍是我本身初衷,但是后面很多要求保留某次请求失败的代理才加入fail_count这个概念,后面应该不会在这方面往下扩展,

zhang-wangz commented 4 years ago

我觉得最优代理这个概念是可取的,不过max取值,并且更新,不会造成一段时间内取到得都是同一个ip代理嘛,有此疑惑

lazyxiaoming commented 4 years ago

我觉得最优代理这个概念是可取的,不过max取值,并且更新,不会造成一段时间内取到得都是同一个ip代理嘛,有此疑惑

可以在/proxy_pool/Schedule/UsefulProxyCheck.py中自定义FAIL_COUNT为你认为合适的值,在fail_count大于此值时会删除此代理。源代码中FAIL_COUNT为0。

zhang-wangz commented 4 years ago

我觉得最优代理这个概念是可取的,不过max取值,并且更新,不会造成一段时间内取到得都是同一个ip代理嘛,有此疑惑

可以在/proxy_pool/Schedule/UsefulProxyCheck.py中自定义FAIL_COUNT为你认为合适的值,在fail_count大于此值时会删除此代理。源代码中FAIL_COUNT为0。

可是假设这个ip检测时一直可用,到之后max就算fail_count不断增加,也会造成使用的一直是此ip,即使爬虫已经封闭了此ip的访问

lazyxiaoming commented 4 years ago

我觉得最优代理这个概念是可取的,不过max取值,并且更新,不会造成一段时间内取到得都是同一个ip代理嘛,有此疑惑

可以在/proxy_pool/Schedule/UsefulProxyCheck.py中自定义FAIL_COUNT为你认为合适的值,在fail_count大于此值时会删除此代理。源代码中FAIL_COUNT为0。

可是假设这个ip检测时一直可用,到之后max就算fail_count不断增加,也会造成使用的一直是此ip,即使爬虫已经封闭了此ip的访问

针对这个我在第一个comment里写了,我新增了主动更新check_countfail_count接口,在爬虫中植入了此代理中间件,所以check_countfail_count是随着爬虫进行而实时更新的,并不是只在校验代理时更新,所以如果某代理失效了,其fail_count会立刻剧增,从而不会出现在长时间内都是同一代理的情况。

zhang-wangz commented 4 years ago

我觉得最优代理这个概念是可取的,不过max取值,并且更新,不会造成一段时间内取到得都是同一个ip代理嘛,有此疑惑

可以在/proxy_pool/Schedule/UsefulProxyCheck.py中自定义FAIL_COUNT为你认为合适的值,在fail_count大于此值时会删除此代理。源代码中FAIL_COUNT为0。

可是假设这个ip检测时一直可用,到之后max就算fail_count不断增加,也会造成使用的一直是此ip,即使爬虫已经封闭了此ip的访问

针对这个我在第一个comment里写了,我新增了主动更新check_countfail_count接口,在爬虫中植入了此代理中间件,所以check_countfail_count是随着爬虫进行而实时更新的,并不是只在校验代理时更新,所以如果某代理失效了,其fail_count会立刻剧增,从而不会出现在长时间内都是同一代理的情况。

可以咨询下这两个中间代理是放在哪里的嘛..因为我找了一圈发现找不到,谢谢🙏

lazyxiaoming commented 4 years ago

我觉得最优代理这个概念是可取的,不过max取值,并且更新,不会造成一段时间内取到得都是同一个ip代理嘛,有此疑惑

可以在/proxy_pool/Schedule/UsefulProxyCheck.py中自定义FAIL_COUNT为你认为合适的值,在fail_count大于此值时会删除此代理。源代码中FAIL_COUNT为0。

可是假设这个ip检测时一直可用,到之后max就算fail_count不断增加,也会造成使用的一直是此ip,即使爬虫已经封闭了此ip的访问

针对这个我在第一个comment里写了,我新增了主动更新check_countfail_count接口,在爬虫中植入了此代理中间件,所以check_countfail_count是随着爬虫进行而实时更新的,并不是只在校验代理时更新,所以如果某代理失效了,其fail_count会立刻剧增,从而不会出现在长时间内都是同一代理的情况。

可以咨询下这两个中间代理是放在哪里的嘛..因为我找了一圈发现找不到,谢谢🙏

这两个更新接口我在第一个comment里面贴了代码。中间件逻辑很简单,就是在爬虫中针对每一次请求都更新check_count,如果请求失败就更新fail_count,并更换代理。我的爬虫是基于scrapy的,贴一下我的代理中间件:

class ProxyMiddleware(object):
    """代理中间件
    """
    def process_request(self, request, spider):
        """对request加代理设置

        Args:
            request (obj): 请求对象
            spider (obj): 爬虫对象
        """
        proxy = self.get_proxy()
        print(proxy)
        request.meta['proxy'] = 'http://' + proxy
        self.update_check_count(proxy)

    def get_proxy(self):
        """获取代理

        Returns:
            str: 代理
        """
        while True:
            try:
                proxy = requests.get('http://127.0.0.1:5010/get').json()['proxy']
                break
            except:
                pass
        return proxy

    def update_check_count(self, proxy):
        """更新代理的校验次数

        Args:
            proxy (str): 代理
        """
        params = {
            'proxy': proxy
        }
        requests.get('http://127.0.0.1:5010/update_check_count', params=params)

    def update_fail_count(self, proxy):
        """更新代理的失败次数

        Args:
            proxy (str): 代理
        """
        params = {
            'proxy': proxy
        }
        requests.get('http://127.0.0.1:5010/update_fail_count', params=params)

    def process_exception(self, request, exception, spider):
        """当代理异常时,更换代理

        Args:
            request (obj): 请求对象
            exception (obj): 异常
            spider (obj): 爬虫对象
        """
        if 'proxy' in request.meta:
            proxy = request.meta['proxy']
            self.update_fail_count(proxy)
            print("Change proxy")
            request.meta['proxy'] = 'http://' + self.get_proxy()
zhang-wangz commented 4 years ago

get

zhang-wangz commented 4 years ago

我现在使用了一种暴力的方法..一旦status_code 不在[200, 300)范围内,直接delete,就不造成重复了