爬虫之IP免杀和多并发
目录
最近在爬一个网站的数据,代码写得差不多了,结果却发现这个网站一言不合就封IP……然而道高一尺魔高一丈,封IP这种小技巧这么奈何得了我!
代理IP
一般网站为了防止爬虫,都会对单个IP的请求频率进行限制,轻则短时间内屏蔽,重则永久黑名单……而我们的爬虫,一般公网 IP 都是固定的(当然,拨号上网可以获取到更多动态IP,不过也会局限与运营商的分配)。
因此网络上也就有很多公开的代理IP,不过大部分良莠不齐,而且也会受到实际网络比较大的影响(比如某个代理IP在电信网络可用,在移动网络可能就不行了)。这就需要一个抓取并筛选代理IP的过程,废话不多说,直接干!
IPProxy
参考:Github
通过这个工具,我们可以快速获取到大量可用的代理IP:
ip,port,anonymous,info,speed 111.1.23.189,80,3,中国-浙江-杭州,0.10514688491821289 111.1.23.141,80,3,中国-浙江-杭州,0.11330294609069824 111.1.23.169,80,3,中国-浙江-杭州,0.15795111656188965 120.52.72.53,80,3,中国-河北-廊坊,0.2509500980377197 120.52.72.59,80,3,中国-河北-廊坊,0.2850189208984375 183.61.236.55,3128,3,中国-广东-东莞,0.3199479579925537 183.61.236.54,3128,3,中国-广东-东莞,0.326632022857666 211.153.17.151,80,3,中国-北京-北京,0.3375699520111084 120.52.72.56,80,3,中国-河北-廊坊,0.3387718200683594 ……
然后在我们的爬虫的网络请求中加入代理IP:
def load_proxyip(fpath): """ proxyip list with weight """ total_weight, proxyip = 0, [] with open(fpath) as f: csvreader = csv.DictReader(f, restval=0, delimiter=",", quotechar="\"", quoting=csv.QUOTE_MINIMAL) for row in csvreader: ip = row["ip"] + ":" + row["port"] weight = 1/float(row["speed"]) total_weight += weight proxyip.append((ip, weight)) return total_weight, proxyip def get_proxyip(total_weight, proxyip): r = random.uniform(0, total_weight) upto = 0 for ip, weight in proxyip: if upto + weight >= r: return ip upto += weight print "Error: total_weight=%s, random_weight=%s" % (total_weight, r) return "localhost:8888" total_weight, proxyip = load_proxyip("proxyip.csv") requests.get(url, proxies={"http": "http://"+get_proxyip(total_weight, proxyip)}, timeout=10}
完整示例代码参考:example.py
轻轻松松,封IP这种雕虫小技奈我何了~~~~
多并发
使用代理IP之后,网络请求的速度将会大大降低,原本10分钟能够完成的任务现在可能需要超过1个小时,这种龟速如何能忍受得了,多并发 势在必行!
偷偷懒,我们直接 gevent 来实现:
from gevent import monkey from gevent.pool import Pool monkey.patch_all() # 设置线程池的大小 pool = Pool(128)
一个简单的 worker:
def worker(url, proxyip): if proxyip: r = requests.get(url, proxies={"http": "http://" + proxyip}, timeout=10) else: r = requests.get(url, timeout=10) print r.text
加入到 gevent pool 中:
def main(): for url in urls: proxyip = get_proxyip(...) pool.spawn(worker, url, proxyip) pool.join() if __name__ == "__main__": main()
实际应用时,在 worker 内需要对 requests 报错 进行处理。
同时,对于通过代理IP抓取的数据,如果有异常,应该将 url 重新加入到队列(这里没有实现)中,更换代理IP重新发起请求。
码字很辛苦,转载请注明来自ChenJiehua的《爬虫之IP免杀和多并发》
评论