上一篇博文已经完成了代理IP抓取的核心部分,这一篇主要讲一下代理IP的检测。
所谓HTTP代理,检测方法很简单,就是用代理去请求一个网址,看看是否能够拿到正确回应。因为我们抓取IP是周期进行的,所以代理IP的验证也要不停的进行,简单说来就是一个队列的形式,抓取系统不停往队列里扔IP,检测进程不停取出IP进行检测,检测合格的IP放入另一个队列,不合格的直接丢弃。当然,因为代理IP的时效性,存放检测合格的代理IP的队列,自身也需要进行检测维护。
实现方法很简单,Python就是个工具库,利用urllib2的绑定代理功能,可以很简单的完成这个工作,稍微注意一点的就是和mongodb的交互,python上使用pymongo库和mongodb交互,API比较简单。为了提高检测效率,我选择了多线程模式。检测IP看做一个任务,这些任务完全可以并行进行,唯一需要注意的就是取IP和放IP。利用mongodb里一些线程安全的API(find_and _delete),可以避开线程锁的使用。(当然,我没有严格验证是否存在数据冲突,作为练手,这个实现方式足以,而且错误的代价基本忽略)
这个检测的进程不停读取IP队列里的数据,如果读不到了,则sleep一些时间,然后继续尝试读取。
这样一套方案搭建好之后,mongodb里时刻都存有当前30分钟内搜索到代理IP资源,这可以作为其他scrapy的准备资源,妈妈再也不用担心我的IP- -。。
我在测试代理IP的时候,把测试用的网页链接设置成了上一篇博客的地址http://blog.csdn.net/mingz_free/article/details/45967725。。。于是乎。。。。
这个博客的阅读量就飞起来了。。。我没有恶意刷点击量的意思。。求别封我的博客- -。。
附上部分源码:
scan.py
#! /usr/bin/env python # -*- coding: utf-8 -*- # USAGE = """This is s special daemon used for checking whether the IP proxy works. It can be managed by supervisor or some other tools, just make sure it works all the time.""" import urllib import urllib2 import httplib import threading import pymongo import time import sys import os from optparse import OptionParser SETTING = { "SERVER":"localhost", "PORT":27017, } class IPPool(object): def __init__(self, server, port, src_path, dst_path): connection = pymongo.MongoClient(server, port) self.src_collection = self._get_collection_by_path(connection, src_path) self.dst_collection = self._get_collection_by_path(connection, dst_path) def _get_collection_by_path(self, client, path): collection = client for node in path.split('.'): collection = getattr(collection, node) return collection def check_one(self, handler, url): res = self.src_collection.find_one_and_delete({}) if res is None: time.sleep(360) return print "%s %s:%s, try to check" % (res["protocol"], res["ip"], res["port"]) # Delete existed one self.dst_collection.delete_many( {"ip":res["ip"], "port":res["port"], "protocol":res["protocol"]}) ret = handler(res["ip"], res["port"], res["protocol"], url) if ret: print "IP %s:%s good" % (res["ip"], res["port"]) self.dst_collection.insert({"ip":res["ip"], "port":res["port"], "protocol":res["protocol"]}) def get_one(self): return self.dst_collection.find_one() def get_all_iter(self): return self.dst_collection.find() def proxy_test(ip, port='80', protocol='http', url='http://www.baidu.com'): proxy_url = protocol + '://' + ip + ':' + port proxy_support = urllib2.ProxyHandler({'http':proxy_url}) opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler) request = urllib2.Request(url) request.add_header("Accept-Language", "zh-cn") request.add_header("Content-Type", "text/html; charset=gb2312") request.add_header("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.1.4322)") trycount = 1 while trycount <= 2: try: f = opener.open(request, timeout=3.0) data = f.read() if 'baidu.com' in data: break else: return False except: time.sleep(3) trycount = trycount + 1 if trycount > 2: return False return True class CheckTask(threading.Thread): def __init__(self, pool, handler, url): threading.Thread.__init__(self) self.pool = pool self.handler = handler self.url = url def run(self): while 1: self.pool.check_one(self.handler, url) class ClickTask(threading.Thread): def __init__(self, pool, handler, url): threading.Thread.__init__(self) self.pool = pool self.handler = handler self.url = url def run(self): while 1: for res in self.pool.get_all_iter(): self.handler(res["ip"], res["port"], res["protocol"], self.url) print "Click %s by %s" % (self.url, res["ip"]) if __name__ == "__main__": pool1 = IPPool(SETTING["SERVER"], SETTING["PORT"], "proxy.unchecked", "proxy.checked") pool2 = IPPool(SETTING["SERVER"], SETTING["PORT"], "proxy.checked", "proxy.checked") parser = OptionParser(usage=USAGE, version='0.0.1') parser.add_option("-n", "--number", dest="number", default='20', metavar='NUMBER', help="The number of threads used to scan.") parser.add_option("-u", "--url", dest="url", default='http://www.baidu.com', metavar='URL', help="The url to used to scan.") parser.add_option("-c", "--click", dest="click", default=False, action="store_true", help="Click url by proxy IP") (options, args) = parser.parse_args() thread_number = int(options.number) print "Thread number is %d + %d" % (thread_number, 1) url = options.url if options.click: tasktype =ClickTask else: tasktype =CheckTask tasks = [] for i in range(thread_number): tasks.append(tasktype(pool1, proxy_test, url)) tasks.append(tasktype(pool2, proxy_test, url)) for task in tasks: task.start() for task in tasks: task.join()
至此,这个练手的小方案就完成了,我时刻都有新鲜的代理IP使用啦。。大家有兴趣也可以做类似的事情,然后用这些代理IP绕开大部分网站对频繁抓取的限制。不过别高兴太早,数据抓取有很多问题需要攻克,绕开IP限制只是第一步,而且是很简单的一步,后面还会碰到很多具体问题,例如:身份验证,验证码,如何获取javascript生成的页面,如何处理大数据存储,如何提高服务器效率,额,后面的是进阶了。。。
有问题欢迎留言- -。。。不知道我还有没有兴致继续搞抓取了,下面会很忙。。。