目标
爬虫中经常遇到被封杀IP的情况,最有效的方式就是使用代理IP。我们可以在一些平台上购买代理IP,但是价格比较昂贵。另外很多IP代理网站也提供了一些免费的代理IP,可以爬取下这些代理IP,并使用webAPI方式提供代理IP服务。
为什么要用代理池?
- 许多网站有专门的反爬虫措施,可能遇到封IP等问题。
- 互联网上公开了大量免费代理,利用好资源。
- 通过定时的检测维护同样可以得到多个可用代理。
代理池的要求?
- 多站抓取,异步检测
- 定时筛选,持续更新
- 提供接口,易于提取
代理池架构?
代理池的实现
项目完整代码已托管到github:
项目结构如下:
从程序的入口run.py
开始分析:
from proxypool.api import appfrom proxypool.schedule import Scheduledef main(): s = Schedule() // 运行调度器 s.run() // 运行接口 app.run()if __name__ == '__main__': main()
从run.py
中不难看出,首先运行了一个调度器,接着运行了一个接口。
调度器schedule.py
代码:
class Schedule(object): @staticmethod def valid_proxy(cycle=VALID_CHECK_CYCLE): """ Get half of proxies which in redis """ conn = RedisClient() tester = ValidityTester() while True: print('Refreshing ip') count = int(0.5 * conn.queue_len) if count == 0: print('Waiting for adding') time.sleep(cycle) continue raw_proxies = conn.get(count) tester.set_raw_proxies(raw_proxies) tester.test() time.sleep(cycle) @staticmethod def check_pool(lower_threshold=POOL_LOWER_THRESHOLD, upper_threshold=POOL_UPPER_THRESHOLD, cycle=POOL_LEN_CHECK_CYCLE): """ If the number of proxies less than lower_threshold, add proxy """ conn = RedisClient() adder = PoolAdder(upper_threshold) while True: if conn.queue_len < lower_threshold: adder.add_to_queue() time.sleep(cycle) def run(self): print('Ip processing running') valid_process = Process(target=Schedule.valid_proxy) check_process = Process(target=Schedule.check_pool) valid_process.start() check_process.start()
在Schedule
中首先声明了valid_proxy()
,用来检测代理是否可用,其中ValidityTester()
方法中的test_single_proxy()
方法是实现异步检测的关键。
check_pool()
方法里面传入了三个参数:两个代理池的上下界限,一个时间。其中PoolAdder()
的add_to_queue()
方法中使用了一个从网站抓取ip的类FreeProxyGetter()
,FreeProxyGetter()
定义在getter.py里面。 接口api.py
的代码:
from flask import Flask, gfrom .db import RedisClient__all__ = ['app']app = Flask(__name__)def get_conn(): """ Opens a new redis connection if there is none yet for the current application context. """ if not hasattr(g, 'redis_client'): g.redis_client = RedisClient() return g.redis_client@app.route('/')def index(): return 'Welcome to Proxy Pool System
'@app.route('/get')def get_proxy(): """ Get a proxy """ conn = get_conn() return conn.pop()@app.route('/count')def get_counts(): """ Get the count of proxies """ conn = get_conn() return str(conn.queue_len)if __name__ == '__main__': app.run()
不难看出,在api.py
中利用了flask
框架的特性定义了各种接口。
具体代码实现请参考github。