【爬虫修炼和实战】三、免费IP/数据收集——爬取筛选和更新免费IP池(工具篇)

第三篇记录一下如何解决学习爬虫经常产生的IP的需求问题,时常苦苦搜寻,一个个复制粘贴测试是否可用甚是辛苦,突发奇想,尝试一劳多得,遂自己造个轮子,调用即可,岂不美哉,作为日常学习使用。

2020.5.26再次更新(添加了IPSearch_0使用方法)

项目已上传到GitHub:https://github.com/dataformydream/IPsearch

本文为IPSearch第一版本:IPSearch_0.py,可用来参考实现思路和学习总结。

申明:本文和相关代码只做学习用途,请合理合法使用。

目录

一、IP相关

二、功能需求

三、实现

思路

代码

使用方法

输出


一、IP相关

暂只知道以下能获取到IP的途径:

1、付费:高匿,稳定(相对),调用接口省事,适合大规模爬虫,个人学习一般用不到。

2、免费:IP提供商收集的免费IP,当然大多不能用,需要自己筛选。

本篇暂选择了三个免费IP网站(国内)抓取IP,有些网站也提供了免费的调用接口,为练习爬虫还是直接爬取。

免费IP网站也有反爬措施和robot协议,建议遵守规则,详细阅读,控制访问频率,勿商用。

网站:西刺、西拉、高可用全球免费代理IP库

二、功能需求

为了方便使用,将以下功能封装在一个py文件中:

1、封装通用的requests库get方法为一个函数,返回get结果,主要实现随机选取user-agent,随机选取IP,若IP被识别则更换重新爬取,直到爬取成功,同时删去IP池中被封的IP,维持IP池可靠可用。

2、headers和proxies作为全局变量调用,headers可灵活添加xm-sign,referer,cookies等参数(即可变字典),IP池proxies为不重复列表

3、通过有限的IP或本机地址,抓取免费IP网站,对可用IP进行筛选,筛选条件可为类型(高匿透明等),IP类型(http、https)、响应速度、存活时间。筛选后存入IP池。(数据来源于IP网站)

4、能针对特定的网站对IP池进行筛选,维护IP池对爬虫网站访问的稳定高效,借此提高抓取效率,而不是网站用来检测是否可用的百度等首页。(能访问百度不一定能爬要爬的网站)

5、要获取的IP数可选(没有上限,数目不够自动遍历网站更多页面,甚至可爬光IP网站所有IP列表页面,不过时间极长,极不推荐,给网站服务器造成不必要的负担,甚至可能有法律风险,一般学习用爬虫10到20个IP足够了),并能在当前爬虫程序目录下生成json文件保存可用IP列表(只要能通过4中的筛选一般不会失效,生成后下次运行爬虫直接调用进IP池,会提前筛选一次,不够设定数目才会请求IP网站,大大减少重复获取IP时间)

暂未实现功能:

1、测试IP是否可用的函数的多线程实现(本质为request请求,IO密集型,可大幅提高效率)

2、IP池的存储方式的优化,由本地的json文件改为数据库存储

3、支持更多的免费IP网站,暂只实现了三个网站,且IP范围限制在国内。

三、实现

思路

第一步:分析三个网站,从IP网站抓取最新可用IP并筛选(网站会定时地检测和排序,最新的一般都在前几页,从前往后获取即可)

第二步:对筛选后的IP列表用要爬的网站地址进行测试访问,删去访问失败或速度较慢的。

第三步、主函数调用各函数,实现本地导入导出、显示和统计、IP数设置、自动检测和补充IP、网页遍历等功能。

代码

文件名:IPSearch.py  第一版本为 IPSearch_0.py

这里只贴出如何获取网站免费IP的爬虫代码,完整代码请访问开头GitHub链接。

#!user/bin/env python
# -*- encoding=utf-8 -*-
import os
import requests
from lxml import etree
import json
import random


# 单独的请求西刺网站
def get_xici(url):
    pro_list = list()
    response = get_response(url).text
    pro_htmls = etree.HTML(response).xpath("//tr[@class]")
    for num in pro_htmls:
        ip = num.xpath('./td[2]/text()')[0]
        port = num.xpath('./td[3]/text()')[0]
        types = num.xpath('./td[5]/text()')[0]
        type_url = num.xpath('./td[6]/text()')[0].lower()
        speed = float(num.xpath('.//div[@class="bar"]')[0].xpath('./@title')[0].replace('秒', ''))
        connect_speed = float(num.xpath('.//div[@class="bar"]')[1].xpath('./@title')[0].replace('秒', ''))
        life = num.xpath('./td[last()-1]/text()')[0]
        if types == '高匿' and speed < 2 and connect_speed < 1 and '天' in life or '小时' in life:  # 筛选条件,可自己添加
            pro = type_url + '://' + ip + ':' + port
            # print(pro)
            pro_list.append(pro)
    print(pro_list)
    print(len(pro_list))
    return pro_list


# 单独的请求西拉网站
def get_xila(url):
    pro_list = []
    response = get_response(url).text
    pro_htmls = etree.HTML(response).xpath("//tbody/tr")
    for num in pro_htmls:
        ip = num.xpath('./td[1]/text()')[0]
        types = num.xpath('./td[3]/text()')[0]
        type_url = num.xpath('./td[2]/text()')[0].replace('代理', '').lower()
        if 'https' in type_url:
            type_url = 'https'
        speed = float(num.xpath('./td[5]/text()')[0])
        life = num.xpath('./td[6]/text()')[0]
        score = int(num.xpath('./td[last()]/text()')[0])
        if '高匿' in types and speed < 3 or '天' in life and score > 10:  # 筛选条件,可自己添加
            pro = type_url + '://' + ip
            pro_list.append(pro)
    print(pro_list)
    print(len(pro_list))
    return pro_list


# 单独的请求免费代理库网站
def get_mianfei(url):
    pro_list = list()
    response = get_response(url).text
    pro_htmls = etree.HTML(response).xpath("//tbody/tr")
    for num in pro_htmls:
        ip = num.xpath('./td[1]/text()')[0]
        port = num.xpath('./td[2]/text()')[0]
        types = num.xpath('./td[3]/text()')[0]
        type_url = num.xpath('./td[4]/text()')[0].lower()
        speed = num.xpath('./td[8]/text()')[0]
        if '毫秒' in speed:
            speed = float(speed.replace('毫秒', '')) / 1000
        else:
            speed = float(speed.replace('秒', ''))
        life = num.xpath('./td[last()-2]/text()')[0]
        if types == '高匿' and speed < 2 and '天' in life or '小时' in life:  # 筛选条件,可自己添加
            pro = type_url + '://' + ip + ':' + port
            # print(pro)
            pro_list.append(pro)
    print(pro_list)
    print(len(pro_list))
    return pro_list


if __name__ == "__main__":
    search_proxies('https://www.ximalaya.com/', 20)

使用方法

1、引用

from IPSearch_0 import *

或者

from IPSearch_0 import proxies,header_add,get_response,add_test,search_proxies

2、各变量用途和用法 

proxies

全局变量,代理池,列表类型,如:[ip1,ip2,ip3,...],初始为空。

header_add

和请求头的设置有关,默认headers中只有随机选取的"User-Agent"这一项,要添加更多参数如下:

from xm_sign import get_sign  # 要传入headers的函数
# 将要添加的参数新建键值对传入header_add
# 同时支持变量和函数传入,注意:传入函数时传入的是函数名get_sign,而不是函数get_sign()
header_add['xm-sign']=get_sign
header_add['Referer']='https://www.ximalaya.com/'

get_response(url)

封装后的request.get()函数,实现随机user-agent,随机proxies请求网页,若proxies为空则使用本地IP,若IP失效则从proxies中删去,直到请求成功为止。

输入:要请求网页的URL,返回值:request.get()返回类型,可用r.text,r.content等

header_add['xm-sign']=get_sign
header_add['Referer']='https://www.ximalaya.com/'
# ----------------之前需要设置好添加的headers内容,get_response内部调用-----------------------
# get_response(url) 传入要爬取的网站即可,在函数内部实现代理的随机选取、
# 同时支持http和https代理、while语句保证请求成功和删除失效代理等
response = get_response('https://www.ximalaya.com/category/')
# 函数返回值即为requests.get()返回类型
print(response.text)
print(response.content)
print(response.status_code)
print(response.request.headers)

 add_test(url,test_list=None)

用来检测test_list中的代理,是否对URL可用(能否请求成功),可用则添加进proxies,若没有要检测的ip池可忽略。

输出:无,函数直接对proxies进行操作,同时新的proxies覆盖到本地json文件保存(ip.json,项目文件夹中生成)

test_list=[ip1,ip2,ip3,.....]
# 测试test_list中ip是否可用并更新到proxies
add_test('http://www.baidu.com',test_list)
# 测试proxies中ip是否仍有效,不指定test_list
add_test('http://www.baidu.com')

search_proxies(url,need_number=30,test_flag=True)

 主函数,输入为要爬取网站的URL,需要的ip数量的最小值,test_flag为True时,本地之前保存的ip重新测试,为False时不测试,直接传入proxies

实现功能:

1、导入本地已有json测试并导入proxies。

2、文件数判断,获取ip到满足设置的need_number为止。

3、更新proxies和本地json文件

完整使用方法:

# 喜马拉雅获取xm_sign 的引用,get_sign()函数
from xm_sign import get_sign
from IPSearch_0 import proxies, header_add, get_response, add_test, search_proxies
header_add['xm-sign'] = get_sign
header_add['Referer'] = 'https://www.ximalaya.com/'
print(header_add)
search_proxies('https://www.ximalaya.com/', 20)
print(proxies)
'''
在此调用get_response()实现爬虫程序
response = get_response('https://www.ximalaya.com/category/')
# 可实时调用,补充代理池
search_proxies('https://www.ximalaya.com/', 100)
'''

输出

{'xm-sign': , 'Referer': 'https://www.ximalaya.com/'}
获取代理中...
未发现本地IP文件,从免费代理网站重新获取...
离要求的IP数还差20个,从免费代理网站开始获取
获取中:西刺
无代理,使用本机IP
西刺获取成功,共有38个IP
开始验证对爬取网站https://www.ximalaya.com/的有效性,时间较长,请等待...
[---------------ip 列表(已省略)-------------]
IP适用,已保存
IP适用,已保存
IP适用,已保存
已删去请求失败IP
IP适用,已保存
---------检测日志(已省略)-----------------------
已删去请求失败IP
已删去请求失败IP
IP适用,已保存
已删去请求失败IP
测试完成,适用IP已添加到proxies
proxies已有ip为26个。
代理获取完毕,最终获取到26个可用代理,已写入本地。

因为爬取的IP以页为单位批量爬取,所以实际获取到的可能是大于要求的,大概是以30到40为一个区间,search_proxies也可在函数内部做检测IP池用,调用一次即可。

获取IP中...
已导入本地备份
开始验证对爬取网站的有效性,时间较长,请等待...
IP适用,已保存
IP适用,已保存
IP适用,已保存
IP适用,已保存
.............略过...........
IP适用,已保存
已删去请求失败IP。

本地请求成功IP为42个。
本地IP数已达到要求,暂不请求网站获取IP。

 

你可能感兴趣的:(爬虫修炼和实战)