python3自建ip代理池多线程爬虫

时间:2022-07-20 17:00:36

最近在做爬虫,但是一直遇到ip被封的问题,就想到了去自建一个ip代理池。

目标地址:西刺

http://www.xicidaili.com/nt/     # xicidaili国内普通代理
http://www.xicidaili.com/nn/    # xicidaili国内高匿代理
http://www.xicidaili.com/wn/   # xicidaili国内https代理

http://www.xicidaili.com/wt/'   # xicidaili国外http代理

还可以使用其他的代理网站66ip,data5u,proxydb

用到的第三方包:

import requests  
import threading
from bs4 import BeautifulSoup
import random
import sqlite3
from fake_useragent import UserAgent

整体思路:

第一步:创建sqlite3数据库和表

def creatdb():
    try:
        conn = sqlite3.connect('ip.db')
        cursor = conn.cursor()
        sql = '''create table ip (
              ip CHAR(50) PRIMARY KEY     NOT NULL)'''
        cursor.execute(sql)
        cursor.close()
    except:
        print("已经创建ip表")

第二步:爬取目标网站IP

def findip(type, pagenum, targeturl):  # ip类型,页码,目标url,存放ip的路径
    list = {'1': 'http://www.xicidaili.com/nt/',  # xicidaili国内普通代理
            '2': 'http://www.xicidaili.com/nn/',  # xicidaili国内高匿代理
            '3': 'http://www.xicidaili.com/wn/',  # xicidaili国内https代理
            '4': 'http://www.xicidaili.com/wt/'}  # xicidaili国外http代理
    url = list[str(type)] + str(pagenum)  # 配置url
    headers = getheaders()  # 定制请求头
    html = requests.get(url=url, headers=headers, timeout=5).text
    soup = BeautifulSoup(html, 'lxml')
    all = soup.find_all('tr', class_='odd')
    for i in all:
        t = i.find_all('td')
        ip = t[1].text + ':' + t[2].text
        is_avail = checkip(targeturl, ip)
        if is_avail == True:
            print(ip)
            insertdb(ip)

第三步,验证目标IP是否可用

def checkip(targeturl, ip):
    headers = getheaders()  # 定制请求头
    proxies = {"http": "http://" + ip, "https": "http://" + ip}  # 代理ip
    try:
        response = requests.get(url=targeturl, proxies=proxies, headers=headers, timeout=5).status_code
        if response == 200:
            return True
        else:
            return False
    except:
        return False

第四步,目标ip可用插入数据库表

def insertdb(ip):
    conn = sqlite3.connect('ip.db')
    cursor = conn.cursor()
    sql = ''' insert into ip
                  (ip)
                  values
                  (:st_ip)'''
    try:
        cursor.execute(sql, {'st_ip': ip})
    except:
        print("已经有了")
    conn.commit()
    cursor.close()

如果程序比较久没有使用想要清空数据库中的数据,可以使用以下程序

def truncatedb():
    conn = sqlite3.connect('ip.db')
    cursor = conn.cursor()
    sql = "DELETE FROM ip"
    cursor.execute(sql)
    cursor.close()

查询全部的表数据

def selectalldb():
    conn = sqlite3.connect('ip.db')
    cursor = conn.cursor()
    sql = "select rowid,ip from ip"
    results = cursor.execute(sql)
    all_students = results.fetchall()
    all_students=list(all_students)
    conn.commit()
    cursor.close()
    return all_students

查询表中随机的单个数据

def selectdb():
    conn = sqlite3.connect('ip.db')
    cursor = conn.cursor()
    sql = "select rowid,ip from ip"
    results = cursor.execute(sql)
    all_students = results.fetchall()
    all_students=list(all_students)
    rain=random.randint(0, len(all_students)-1)
    ip=(list(all_students)[rain][1])
    conn.commit()
    cursor.close()
    return ip

删除数据库表的指点的数据,这个用于再次的ip验证,可删除没有的数据

def deletedb(ip):
    conn = sqlite3.connect('ip.db')
    cursor = conn.cursor()
    sql ="delete from ip where ip ='"+ip+"'"
    cursor.execute(sql)
    conn.commit()
    cursor.close()

对于反反爬虫,请求头的修改是很有必要的,这里fake_useragent包,是一个很便捷的请求头的包

def getheaders():
    ua = UserAgent()
    headers = {'User-Agent': ua.random}
    return headers

单线程进行捉取太浪费时间了,不想等,于是使用了多线程四种类型ip,每种类型取前8页,共32条线程

def getip(targeturl):
    threads = []
    for type in range(4):  # 四种类型ip,每种类型取前8页,共32条线程
        for pagenum in range(0,8):
            t = threading.Thread(target=findip, args=(type + 1, pagenum + 1, targeturl))
            threads.append(t)
    print('开始爬取代理ip')
    for s in threads:  # 开启多线程爬取
        s.start()
    for e in threads:  # 等待所有线程结束
        e.join()
    print('爬取完成')

构建proxies,在使用前对ip进行验证,删除无用ip

def getproxies():
    ip=selectdb()
    targeturl = 'http://www.cnblogs.com/rianley/'  # 验证ip有效性的指定url
    is_avail = checkip(targeturl, ip)
    if is_avail == True:
        proxies = {"http": ip, "https": ip}  # 代理ip
        print("我有了")
        print(ip)
        return proxies
    else:
        deletedb(ip)
        print("删除"+ip)
        getproxies()

使用定时器对目标网站一定时间后运行。可以跟爬虫程序同时运行。

def running():
    truncatedb()  # 爬取前清空数据库
    targeturl = 'http://www.cnblogs.com/rianley/'  # 验证ip有效性的指定url
    getip(targeturl)
if __name__ == '__main__':
    n=300
    while True:
        print("--------------------------------------")
        running()  # 此处为要执行的任务
        time.sleep(n)

这种简便的ip池构建方便、而且能保证小需求的爬虫。加入的数据库后,爬虫程序可以直接去数据库取数据。

使用方法:

from ipdaili import getproxies
    
resp = requests.get(url, headers=headers,proxies=proxies(), timeout=5)

    自建多线程ip池就先做到这里,后面希望能通过其他方式提高ip质量和爬虫效率。

代码存放在码云:https://gitee.com/qq903165495/self_built_ip_pool