天天看點

Python 多線程爬蟲連接配接池 PooledDB

前言

在爬取目标網站時采用的多線程操作,理論上速度應該是比串行快很多但是實際效果并沒有想象中的那麼快,剛開始速度還是可以的,過幾個小時以後就慢下來了,重新開機以後又變快了,這肯定不是網速的原因。

發現問題

我是按照網上的方式使用連接配接池來連接配接資料庫的

import pymysql
from DBUtils.PooledDB import PooledDB
pool = PooledDB(pymysql,5,host='localhost',user='root',passwd='pwd',db='myDB',port=3306) #5為連接配接池裡的最少連接配接數

 
conn = pool.connection()  #以後每次需要資料庫連接配接就是用connection()函數擷取連接配接就好了
cur=conn.cursor()
SQL="select * from table1"
r=cur.execute(SQL)
r=cur.fetchall()
cur.close()
conn.close()
           

每次執行語句的時候都是用 pool.connection(), 執行完畢以後就把 cur 和 conn 都斷開,實際上隻是放到了連接配接池中,下次調用并非重連,是以理論速度會快很多。

但是我的代理IP是放到了另一個檔案中,使用的調用函數從資料庫中取 IP ,當時以為這個操作不是很頻繁,就沒有用代理池,用的最古老的 pymysql 連接配接,在100個線程中都建立了一個 conn 對象,導緻不知什麼原因這些連接配接全部都處于sleep狀态,而且100個線程竟然又200多個 mysql 連接配接!!不能忍了,這應該才是爬蟲速度變慢的原因。

于是改了一下政策

from crawl_xici_ip import GetIP
class CaiJiThread(threading.Thread):
    def __init__(self, thread_id, name):
        threading.Thread.__init__(self)
        self.p = GetIP()


    # 随機從配置檔案裡選擇一條IP:PORT
    def ip_choice_one(self):
        http = self.p.get_random_ip(pool)
        self.proxies_random = {
            "http": http,
            # "https": http_ip,
        }

if __name__ == "__main__":
    pool = PooledDB(pymysql,5,host='localhost',user='root',passwd='pwd',db='myDB',port=3306)
           

這樣每次調用的時候都把 pool 傳給 get_random_ip(pool)

class GetIP(object):

    def judge_ip(self, ip, port, pool):
        #判斷ip是否可用
        http_url = "測試網址"
        proxy_url = "http://{0}:{1}".format(ip, port)
        headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML,'
                          ' like Gecko) Chrome/55.0.2883.95 Safari/537.36',

        }
        try:
            proxy_dict = {
                "http":proxy_url,
            }
            response = requests.get(http_url, headers=headers, proxies=proxy_dict)
        except Exception as e:
            # print("invalid ip and port")
            self.delete_ip(ip, port, pool)
            return False
        else:
            code = response.status_code
            if code >= 200 and code < 300:
                # print("effective ip")
                return True
            else:
                # print("invalid ip and port")
                self.delete_ip(ip, port, pool)
                return False

    def get_random_ip(self, pool):

        #從資料庫中随機擷取一個可用的ip
        conn = pool.connection()
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        random_sql = """
              SELECT ip, port FROM ip_proxies 
            ORDER BY created_time desc LIMIT 100;
            """
        cursor.execute(random_sql)
        result = random.choice(cursor.fetchall())

        ip = result['ip']
        port = result['port']
        # print(ip,port)
        judge_re = self.judge_ip(ip, port, pool)
        if judge_re:
            # print("http://{0}:{1}".format(ip, port))
            cursor.close()
            conn.close()
            return "http://{0}:{1}".format(ip, port)
        else:
            return self.get_random_ip(pool)

    def delete_ip(self,ip, port, pool):
        conn = pool.connection()
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        sql = """DELETE FROM ip_proxies WHERE ip='%s' and port='%s';"""
        data = (ip, port)
        cursor.execute(sql % data)
        conn.commit()
        cursor.close()
        conn.close()
           

最終發現那些 sleep 的連接配接都沒了,而且連接配接也恢複到了 100 個左右,大功告成!