这几天要搞毕业设计,一个简单的大数据分析系统,既然是大数据分析系统,那么一定要有数据,目标是用python写一个爬虫,爬大约100w条数据
这就遇到了第一个问题,100w条数据单线程的话需要爬很久,所以要用多线程获取,这又会有一个新问题,爬取频率过高的话会被封ip,所以要使用https代理(貌似http不行,会被查出来)
然后又出现一个问题,免费的代理稳定性太差,而且访问速度慢,自己写了个爬虫获取免费代理然后进行测试,能稳定使用的大概只有2成,干脆就用付费的吧,平均一元100个左右,不算太贵
然后又出了问题,付费的虽然稳定,但也只是相对的,依然会有将近一成无法使用,而能用的会在半小时之内的某个时间失效(不确定失效时间这一点很蛋疼),大量数据需要多线程爬取很长的时间,怎么能在这么长的时间保持稳定就成了一大问题
单线程很好说,先获取一定数量的代理,从中选一个用做代理,不能使用就去掉,每个都不能用了再获取,不过这样会有一个问题,代理有的能维持5分钟,有的能维持30分钟,假设获取了5个代理,第一次就选了能用30分钟的代理,其余4个就白获取了,很浪费,而且还要对剩余4个一个一个检测是否能用,相当费时间,最好的方法是一次只申请一个
多线程就不行了,假如我开了20个线程的话,总共每轮要获取20次代理,浪费时间不说,如果同时获取,还容易引发错误或者被代理网站当做恶意访问。干脆一次多获取一些,用全局变量储存,每次网络请求时就从全局变量中选一个,不能访问就去除,不够用了就再获取少量代理,但是多线程对全局变量进行操作就会引发冲突,比如线程1发现第一个代理不能用的同时线程2也发现了,线程1去除坏代理之后线程2再试图去除时就会产生error,按下标去除的话可能会把能用的代理去除,所以多线程怎么协调就成了一个问题。
解决方法比较简单
利用锁,改变公共资源前获取锁,完成后释放锁
每个线程在进行网络请求时,先获取锁,从全局变量中pop一个,如果全局变量中代理不够,再从网上获取代理,再释放锁,代理能用的话再append()回全局变量,不能用的话直接return递归函数(不能用的那个已经不在全局变量中了)
代码
部分代码没有写出来,我是用bs4分析网页的,所有对网页的get请求都会用到get_bsobj(url),爬了几万个暂时没有问题,20个线程还是很快的,理论上还可以加,明天继续搞
threadLock = threading.Lock()
pro=[]
Cookie=''
wait =(1,9)
def get_proxies_list(num):
global pro
pro+=requests.get(url='https://xxx.xxxxxxxx.xxx&num='+str(num))#通过某些手段获取了一定数量的代理
return
def set_proxies():
global wait,pro
# time.sleep(random.randint(wait[0],wait[1]))
threadLock.acquire()#获取锁
if pro==[]:
get_proxies_list(10)
i=pro.pop(0)
threadLock.release()#释放锁
return pro
def get_bsobj(url):
global Cookie,pro
u=0
p=set_proxies()
headers =set_header(Cookie)
while u<3:#检测次数,超过timout时限u次便认为代理失效
try:
html=requests.get(url,headers=headers, proxies ={'https':p},timeout=5)#检测时间超过timout的话会认为请求失败
BsObj=BeautifulSoup(html.text,"html.parser")
pro.append(p)
return BsObj
except:
u=u+1
return get_bsobj(url)