本帖最后由 十月故里 于 2020-5-6 18:38 编辑
前情回顾~电影里面好像都是这么来的
前面两节分别介绍了构建爬取代理网站的类,和存储代理ip的数据库
没看过的小伙伴可以点击下面的传送门过去看
因为我们的目标是免费的代理ip,所以有很大一部分ip是无效的,要么是刚好被多人用过来爬取目标网站导致被封,所以这一节主要讲解怎么检测这些ip的可用性,同时由于ip数量比较多,这里会用多线程的方法来进行检测
这一节用到的库有sqlite3, time, requests, threading
废话不多说,进入主题,测试ip之前,我们需要把存放在数据库中的ip读取出来,代码如下lock=threading.Lock()
conn = sqlite3.connect('ip.sqlite')
c = conn.cursor()
cursor = c.execute("select ip,score from ips")
ips = []
for row in cursor:
ips.append(list(row))
def get_ip():
lock.acquire()
if len(ips)==0:
lock.release()
return ''
else:
proxy=ips[0]
del ips[0]
lock.release()
return proxy
关于从sqlite中读取信息的操作就不细说了,想深入了解的童鞋可以回去看第二节内容里面提到的链接,这里主要说一下threading.Lock(),这是一个线程锁,前面也提到了,我们要采用多线程的方法来检测ip,那么在每个线程读取ip的过程中,不可避免的会出现一个ip多个线程同时读取,或者在读取ip的过程中,其他线程在进行删除ip的操作,导致程序异常,为了避免这个情况,我们用到线程锁来把这个读取ip的操作变成原子操作,当其中一个线程在读取ip的时候,把这个存放IP的ips集合锁起来=》 lock.acquire(),不让别的线程读取,待读取完之后再释放=》 lock.release(),允许别的线程读取ip,这样我们就能避免上述提到的问题的发生了。
接下来我们就开始搭建线程的执行代码了headers={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
}
update_ips=[]
VALID_STATUS_CODES=[200]
TEST_URL='https://www.baidu.com'
class SpiderThread(threading.Thread):
def __init__(self,name):
threading.Thread.__init__(self)
self.name='thread'+str(name)
def run(self):
while True:
proxy=get_ip()
try:
real_proxies = {
'http':'http://' + proxy[0],
'https':'https://' + proxy[0]
}
except Exception:
break
if proxy !='':
try:
response=requests.get(url=TEST_URL,headers=headers,proxies=real_proxies,timeout=5)
if response.status_code in VALID_STATUS_CODES:
proxy[1] = 100
update_ips.append(proxy)
print('代理可用', proxy[0])
time.sleep(5)
else:
proxy[1] -= 1
update_ips.append(proxy)
print('请求响应码不合法', proxy[0])
time.sleep(5)
except Exception as e:
proxy[1] -= 1
update_ips.append(proxy)
#print('请求失败',e.args)
else:
break
关于请求网站的操作就不细说了,主要说一下这个线程类的框架,分为两部分:
- 一部分是初始化的'__init__',它继承threading.Thread的基类,里面的self.name是用来定义线程的名字的,不设置就采用默认的格式,thread1 thread2 。。。(没记错的话);
- 第二部分就是执行方法,它会在线程启动之后自动执行,这里面我们设置了三种情况,有合法响应,有不合法响应,无响应。其中合法响应其实就是上面定义的VALID_STATUS_CODES,一般200就表示网站正常响应请求了,但也有特殊情况,按需添加,而请求的网站放在了TEST_URL中,可以弄成集合,对多个网站测试,看个人需求。这里面能合法响应就把proxy(ip,score)里面的score分值设为100,这个主要是为了后期在读取可用ip的时候用来筛选的,看个人需求可以设置别的,二另外两种情况都给分值减分,当减到0的时候,我们就可以把ip从数据库中剔除(这部分代码还没写),保证数据库的'纯净'。
接下来就是执行线程检测ip可用性,并把得到的新的(ip,score)的集合update_ips从新储存到数据库中class Tester(object):
def __init__(self):
self.threads=[SpiderThread(i) for i in range(1,5)]
def run(self):
for threa in self.threads:
threa.start()
threa.join()
for proxy in update_ips:
#这里必须要用双引号,且ip部分需要用单引号括起来,否则sqlite无法识别多.号的结构,会认为是错误的浮点数格式
c.execute("update ips set score ={} where ip = '{}'".format(proxy[1], proxy[0]))
conn.commit()
conn.close()
第一部分用来初始化线程,range(1,5)有两个含义,一个是定义了4个线程,另一个是每个线程的名字依次为1,2,3,4,这里可以看需求更改,而且线程不是越多越好哈
第二部分就是执行线程的部分,通过.start来启动线程,.join来保证线程运行完才结束,最后再把得到的update_ips刷新到数据库中
if __name__ == '__main__':
r=Tester()
r.run()
conn = sqlite3.connect('ip.sqlite')
c = conn.cursor()
ips = c.execute("select ip,score from ips where score>10")
for ip in ips:
print(ip)
最后测试一下,运行了一下,发现就5个ip是好使的,其他都gg了,果然免费的就是不好用啊
好了这个系列到此就结束了,欢迎大家批评指正哈 |