前情回顾~电影里面好像都是这么来的
前面两节分别介绍了构建爬取代理网站的类,和存储代理ip的数据库
没看过的小伙伴可以点击下面的传送门过去看
[*]python3代理池的搭建(一)
https://fishc.com.cn/thread-168107-1-1.html
(出处: 鱼C论坛)
[*]python3代理池的搭建(二)
https://fishc.com.cn/thread-168109-1-1.html
(出处: 鱼C论坛)
因为我们的目标是免费的代理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
del ips
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=
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,
'https':'https://' + proxy
}
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 = 100
update_ips.append(proxy)
print('代理可用', proxy)
time.sleep(5)
else:
proxy -= 1
update_ips.append(proxy)
print('请求响应码不合法', proxy)
time.sleep(5)
except Exception as e:
proxy -= 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=
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, proxy))
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了,果然免费的就是不好用啊{:5_100:}
好了这个系列到此就结束了,欢迎大家批评指正哈{:5_95:} @乘号 请@不二 十月故里 发表于 2020-5-5 22:08
这个帖子发重了。。。之前一直提示发贴失败,然后多发了一次,不知道怎么删除
加个敏感词,然后标题改成求删帖,应该过不了机器审核 @不二如是 十月故里 发表于 2020-5-5 22:08
这个帖子发重了。。。之前一直提示发贴失败,然后多发了一次,不知道怎么删除
已经帮你删除,这系列文章写的真棒,加油!{:5_106:} 小甲鱼 发表于 2020-5-5 23:20
已经帮你删除,这系列文章写的真棒,加油!
麻烦小甲鱼了,也感谢甲鱼老司机的捧场{:10_279:} Hello. 发表于 2020-5-5 22:13
@乘号 请@不二
为啥你不自己请{:10_307:} 不二如是 发表于 2020-5-6 08:09
为啥你不自己请
没好友
请不来{:10_266:}{:10_266:}
而且现在想加加不了{:10_266:} 惹,发现我的代码部分内容被吞了,晚点回去补回去 ok更新回来了 {:7_136:}
页:
[1]