如何用爬虫搭建一个免费的IP代理池

之前写过一篇python实战项目二:获取IP代理的文章,不过说实话,这个程序有几个缺点,以至于不能愉快玩耍之后,我就重新整理了思路,又写了一个关于获取免费IP代理的代码。在这儿我想写反思一下之前这个代码的主要不足:

第一点,由于数据很杂,所以在提取信息时频繁的使用了循环,但是循环使用的太频繁会使得程序执行的速度效率降低,而字典的索引效率就高效的多,所以这一次不必使用循环的地方就不使用循环,改用字典。

第二点,爬取下来的IP不是都能用的,所以得检验一下。我之前用的方法就是直接将得到的IP访问某个网站,若状态码status_code为200,则说明可用。但是实际上得到的IP不能用的还是很多。所以这次我增加了一些检验的条件。

这次我还是选择西刺代理。接下来就开始说一下过程:

首先先导入相关的模块

import requests
from lxml import etree
import re
import time

然后定义函数,爬取网页信息

def get_html(url):
    # 获取西刺代理高匿代理的每一页的html,return: 返回html
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 '
                             '(KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'}
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            return response.text
        else:
            print('IP不能用')
    except Exception as e:
        print(e)

得到响应后,就是要提取页面的信息了,先用xpath表达式得到信息,包括IP地址、是否匿名、类型、连接时间、存活时间等,提取之后的内容用字典来盛装,然后返回该字典

def get_data(html):
    # 获取每一页的IP信息, return:返回这一页IP的信息(封装成字典形式)
    info = etree.HTML(html)
    try:
        address = info.xpath('//tr[@class="odd" or class=""]/td[2]/text()')  # IP地址
        ports = info.xpath('//tr[@class="odd" or class=""]/td[3]/text()')  # 端口
        anonymous = info.xpath('//tr[@class="odd" or class=""]/td[5]/text()')  # 匿名形式
        http_https = info.xpath('//tr[@class="odd" or class=""]/td[6]/text()')  # http or https
        speed = info.xpath('//tr[@class="odd" or class=""]/td[7]/div[1]/@title')  # 连接速度
        speed_width = info.xpath('//tr[@class="odd" or class=""]/td[7]/div[1]/div/@style')  # 连接速度的比例
        conn_time = info.xpath('//tr[@class="odd" or class=""]/td[8]/div[1]/@title')  # 连接时间
        conn_time_width = info.xpath('//tr[@class="odd" or class=""]/td[8]/div[1]/div/@style')  # 连接时间的比例
        life = info.xpath('//tr[@class="odd" or class=""]/td[9]/text()')  # 存活时间
        test = info.xpath('//tr[@class="odd" or class=""]/td[10]/text()')  # 检验时间
        data_info = {}
        # print(len(address))
        for i in range(0, len(address)):
            data_info[str(i+1)] = {'IP地址': address[i]+':'+ports[i],
                                   '是否匿名': anonymous[i],
                                   '类型': http_https[i],
                                   '速度': eval((re.compile('(.*?)秒').findall(speed[i]))[0]),
                                   '速度比例': eval((re.compile('width:(.*?)%').findall(speed_width[i]))[0]),
                                   '连接时间': eval((re.compile('(.*?)秒').findall(conn_time[i]))[0]),
                                   '耗时比例': eval((re.compile('width:(.*?)%').findall(conn_time_width[i]))[0]),
                                   '存活时间': eval((re.compile('(\d+).*?').findall(life[i]))[0]),
                                   '验证时间': test[i]}
        return data_info  # 返回名为data_info的字典,字典的每个键值对就是一个IP的信息{{'1':{}},{'2':{}}}
    except Exception as er:
        print(er)

接下来就将上面得到的字典信息存入文件,存入之前要先进行第一次的检验,就是筛选出存活时间>100天,还有速度等限制条件的,这样至少可以保证得到的IP生命力是比一般的那些顽强。

def save_to_file(data):
    # 将得到的符合要求的IP写入文件;param data: 所有的IP集合(字典形式的)
    try:
        # print(data['1'])
        # print(type(data['2']['耗时比例']), type(data['2']['存活时间']))
        with open('IP代理池.csv', 'w', encoding='utf-8') as f:
            for i in range(0, len(data)):
                if data[str(i+1)]['速度比例'] >= 70 and data[str(i+1)]['耗时比例'] >= 75 and \
                        data[str(i+1)]['存活时间'] >= 100:
                    # 存活时间要>=100天,速度和耗时也有相关的限制
                    f.write(str(data[str(i+1)]))
                    f.write('\n')
                else:
                    pass
    except Exception as er:
        print(er)

然后就是主函数,在这个函数中实现调用其他函数,实现整个程序的功能,因为只是用来检验能否用该方法得到有效的IP,所以我的循环range(1, 2)只爬取了一页的内容。

def main():
    # 主逻辑函数,实现得到IP的功能;param page: 默认为爬取1页,其他函数调用时刻自行改变该值;return: None
    base_url = 'http://www.xicidaili.com/nn/'
    for i in range(1, 2):
        url = base_url + str(i)
        print('爬取第{}页'.format(str(i)).center(20, '='))
        html = get_html(url)  # 得到当前爬取页面的html信息
        data = get_data(html)  # 解析当前页面,得到想要获取的信息
        save_to_file(data)  # 将得到的信息写入文件保存
        if i % 2 == 0:  # 每爬取两页,停3秒
            time.sleep(3)

到这儿整个程序执行之后就可以得到有效的IP了,部分结果如下图

但是这只得到相对存活率高一点的IP,有些IP可能已经被一些网站加入黑名单了。那么我得到这个IP代理池之后,我怎么使用呢?就是检验的第二重,用带爬取的网站去检验,检验刚刚得到的文件中的那些有效IP,用这些IP去访问待爬取的网页,若返回状态码是200,则说明还能用,不能用的直接移除就行。

两种方法,一种是你在运行你的其他爬虫的时候,先将该程序运行一遍,得到有效IP,然后在那个爬虫里读取这个程序结束后的装有效代理IP的文件就行;另一种方法就是在这个程序中写一个函数,作为那个函数调用的接口,在那个程序中导入这个程序,然后运行的结果就是会先执行这个程序,函数返回的是一个有效IP的列表,在那个爬虫里直接使用random模块的choice方法就可以随机得到一个该列表的一个IP,每次执行得到IP的都是一个随机的,也会更好的防止被封。两种方法其实差不多,只不过第二种方法会将该爬虫获取有效IP代理的时间也会在那个程序中消耗,所以这个就看个人选择。我写的接口是

def get_ip():
    # 接口函数,其他爬虫可以直接导入这个程序(模块)之后调用该方法就行,返回的就是有效的IP列表
    test_url = "https://blog.csdn.net/"
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36\
     (KHTML, like Gecko) hrome/68.0.3440.106 Safari/537.36'}
    ip_data = []
    try:
        with open('IP代理池.csv', 'r', encoding='utf-8') as f:
            for item in f.readlines():
                ip_data.append(eval(item)['IP地址'])  # 读取文件并将IP全部提取出来检验
    except Exception as e:
        print(e)
    # print(ip_data)
    for data in ip_data:
        proxy = {'http': 'http://'+data}
        try:
            r = requests.get(test_url, headers=headers, proxies=proxy)
            if r.status_code == 200:
                pass  # 若此IP有效,则万事大吉
                # print('{}可用'.format(proxy).center(20, '='))
            else:
                ip_data.remove(data)  # 若不可用,则移除
                # print('{}不可用'.format(proxy).center(20, '%'))
        except Exception as e:
            print(e)
    # print('得到{}个有用个IP'.format(len(ip_data))
    return ip_data

猜你喜欢

转载自blog.csdn.net/wtwcsdn123/article/details/82710775