在做爬取网站信息的时候,通常网站都会这反爬虫设计,如果一个ip访问太过频繁,会被列入黑名单或者出现验证码需要输入。星期天在爬取58同城信息的时候也遇到这个问题,百度了一下,发现以下两个方法最为省心省力。
1.设置时间间隔
既然一个ip访问太过频繁会被列入黑名单,那就不频繁的访问,导入time包,每一次加载完成后就强制休眠2s。例如
import time
wb_data = requests.get("http://cn.58.com/bijibendiannao/33724988041130x.shtml")
time.sleep(2)
2.设置代理IP
方法1虽然很有效但是会浪费很多不必要的时间,既然同一个ip访问太频繁会被加入黑名单,那不同的ip访问也会解决这个问题。
代理ip获取地址
代理ip获取网站:http://www.xicidaili.com/nn/1
获取代理ip的时候一定要注意协议是http还是https,血泪教训,不然很可能发生狗血报错,找半天找不出原因,就是因为http与https的协议搞错。
def get_ip_list(url, headers):
web_data = requests.get(url, headers=headers)
soup = BeautifulSoup(web_data.text, 'lxml')
ips = soup.find_all('tr')
ip_list = []
for i in range(1, len(ips)):
ip_info = ips[i]
tds = ip_info.find_all('td')
ip_list.append(tds[5].text+'://'+tds[1].text + ':' + tds[2].text)
return ip_list
验证代理ip
假如我们从上述代理ip的网站获取了如下ip:
IP_LIST ='''
HTTP://61.135.217.7:80
HTTPS://114.215.83.184:3128
HTTP://122.114.31.177:808
HTTP://123.56.89.238:60443
HTTPS://49.79.193.138:61234
HTTPS://183.159.93.28:18118
HTTPS://183.159.80.14:18118
HTTP://114.99.28.110:18118
HTTPS://183.159.88.201:18118
HTTPS://183.159.80.246:18118
HTTP://49.79.193.93:61234
HTTP://111.155.116.237:8123
HTTPS://117.68.194.66:18118
HTTPS://60.177.228.169:18118
HTTPS://183.159.87.2:18118
HTTPS://183.159.88.75:18118
HTTP://183.159.83.235:18118
HTTP://202.104.184.5:808
HTTP://14.120.181.241:61234
HTTP://14.118.254.216:6666
HTTP://124.235.121.216:8118
'''
(再说一遍,这个ip前的http还是https要与代理ip网站上的类型相同)
获取的ip可能不能用,所以我们需要验证可以用的ip
def cip(ip):
try:
if ip.keys()[0]=="http":
requests.get('http://ip.chinaz.com/getip.aspx',proxies={'http':ip["http"]},timeout=3)
else:
requests.get('http://ip.chinaz.com/getip.aspx', proxies={'https': ip["https"]}, timeout=3)
except:
# print("failure")
return False
else:
print(ip)
return True
而这个验证ip我一开始以为验证过的就可以一直用了,没想到刚验证过的,一秒之后就不能用了,所以我是在使用ip的时候才验证,而不是验证好了存一边使用的时候在抓取,例如。
def get_random_ip(ip_list):#从上文的IP_LIST随机获取ip的函数
proxy_list = []
for ip in ip_list.split():
proxy_list.append( ip)
proxy_ip = random.choice(proxy_list)
if proxy_ip[4] == "S":
proxies = {'https': proxy_ip}
else:
proxies = {'http': proxy_ip}
return proxies
proxy = get_random_ip(IP_LIST)#先随机获取一个ip
while (not cip(proxy)) :#验证ip,如果ip有效则不在继续获取ip
proxy = get_random_ip(IP_LIST)#如果无效则继续获得ip
wb_data =requests.get(url,proxies=proxy)#使用代理ip获取数据
3.补充
如果不出意外,上面两点就可以满足爬取需求了,但是还是会发生很多奇葩报错,所以建议把,wb_data = requests.get()换成如下代码,否则很容易因为http连接太多报出错误。
requests.adapters.DEFAULT_RETRIES = 5 #重试连接次数
s = requests.session()
s.keep_alive = False
wb_data =s.get(url,headers=header,proxies=proxy)
如果使用多进程去爬取数据,建议加上try....except去包裹request请求,虽然这样可能得不到出错原因,但是可以保证程序平稳运行下去,否则会出现wb_data的UnboundLocalError: local variable 'wb_data' referenced before assignment错误:
try:
s = requests.session()
s.keep_alive = False
wb_data = s.get(url, headers=header, proxies=proxy)
time.sleep(1)
soup = BeautifulSoup(wb_data.text,"lxml")
no_longer_exist = soup.find("p", attrs={'class':'et'})
if no_longer_exist:
print "nolonger"
none_list_info.insert_one({"url":url})
pass
else:
title = soup.select("head > title")[0].text if soup.find_all("title","") and soup.select("head > title").__len__()>0 else None
price = soup.select("span.price")[0].text if soup.find_all("span","price") and soup.select("span.price ").__len__()>0 else None
date =soup.select("li.time")[0].text if soup.find_all("li","time") and soup.select("li.time ").__len__()>0 else None
area = list(soup.select("span.c_25d ")[0].stripped_strings) if soup.find_all("span","c_25d") and soup.select("span.c_25d ").__len__()>0 else None
list_info.insert_one({"title":title,"price":price,"date":date,"area":area})
except:
time.sleep(2)
print "error "
多进程运行可以使用Pool
from multiprocessing import Pool
pool = Pool()#python根据你电脑的cpu数去决定开启几个进程
pool.map(get_list_info,urllist.split() )#get_list_info(url)是通过给定url获取相应界面信息的函数,urllist是目标url的字符串。
还有requests请求中的header也可以尝试更换,如果不经常更换我也忘了会不会报错,建议还是随机选取一个:
Headers='''
Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13:
Mozilla/5.0 (iPhone; U; CPU like Mac OS X) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A93 Safari/419.3:
Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1:
Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070309 Firefox/2.0.0.3:
Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070803 Firefox/1.5.0.12
'''
def Random_header(Headers):
header_list = []
for header in Headers.split(':'):
header_list.append(header.strip())
proxy_header = random.choice(header_list)
return proxy_header
header['User-Agent'] = Random_header(Headers)
s = requests.session()
s.keep_alive = False
wb_data = s.get(url, headers=header, proxies=proxy)
最后附上git地址,大家有兴趣的可以看一下:
https://github.com/lixiaozhe666/58project
网友评论