十月故里 发表于 2020-5-5 22:08:51

本帖最后由 十月故里 于 2020-5-6 18:38 编辑

前情回顾~电影里面好像都是这么来的
前面两节分别介绍了构建爬取代理网站的类,和存储代理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:}

Hello. 发表于 2020-5-5 22:13:43

@乘号 请@不二

老八秘制 发表于 2020-5-5 22:15:42

十月故里 发表于 2020-5-5 22:08
这个帖子发重了。。。之前一直提示发贴失败,然后多发了一次,不知道怎么删除

加个敏感词,然后标题改成求删帖,应该过不了机器审核

乘号 发表于 2020-5-5 22:15:54

@不二如是

小甲鱼 发表于 2020-5-5 23:20:05

十月故里 发表于 2020-5-5 22:08
这个帖子发重了。。。之前一直提示发贴失败,然后多发了一次,不知道怎么删除

已经帮你删除,这系列文章写的真棒,加油!{:5_106:}

十月故里 发表于 2020-5-5 23:35:27

小甲鱼 发表于 2020-5-5 23:20
已经帮你删除,这系列文章写的真棒,加油!

麻烦小甲鱼了,也感谢甲鱼老司机的捧场{:10_279:}

不二如是 发表于 2020-5-6 08:09:29

Hello. 发表于 2020-5-5 22:13
@乘号 请@不二

为啥你不自己请{:10_307:}

Hello. 发表于 2020-5-6 08:10:50

不二如是 发表于 2020-5-6 08:09
为啥你不自己请

没好友
请不来{:10_266:}{:10_266:}
而且现在想加加不了{:10_266:}

十月故里 发表于 2020-5-6 08:40:54

惹,发现我的代码部分内容被吞了,晚点回去补回去

十月故里 发表于 2020-5-6 18:42:13

ok更新回来了

zwhe 发表于 2020-5-29 10:14:52

{:7_136:}
页: [1]
查看完整版本: python3代理池的搭建(三)