python3–数据可视化-破解IP查询接口 将6万个IP地址可视化展示(附源码)

本次先使用Python撰写爬虫,将爬取的数据存入数据库最后使用pyecharts进行图表的绘制,可视化地展示58677个IP地址,包括IP地址分类,IP地址归属地分布,IP地址运营商分布。


一.准备工作

整体操作是基于Python,我的环境:

  1. Python3.8
  2. JetBrains PyCharm 2018.2.2 x64
  3. pyecharts 1.9.0

二.思路

1.整体思路

在这里插入图片描述

2.爬虫思路

在这里插入图片描述

这里说一句,数据抽取完了其实直接存入数据库即可,我这里是先存入txt文本文件,再读取文本文件写入了excel中,最后手动导入了MySQL数据库,有些多此一举了。

3.爬虫实现

1.查找接口
在百度上搜索IP,百度会给我们提供一个IP138的查询接口
在这里插入图片描述
在输入框输入IP,打开F12开发者工具,点击查询按钮。
在这里插入图片描述

发现点完查询按钮后,浏览器向服务器发送了一个GET请求,服务器返回了一串jQuery字符串,其中data[0]中的location字段为我们需要的IP地址归属地、运营商信息使用空格分隔,请求接口为:

https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=8.8.8.8&co=&resource_id=5809&t=1636340450159&ie=utf8&oe=gbk&cb=op_aladdin_callback&format=json&tn=baidu&cb=jQuery1102030225422148935555_1636339389380&_=1636339389385

2.分析接口
对接口参数进行分析,具体操作步骤是:每次删除一组参数,然后放到浏览器去访问,看服务器能否返回需要的数据。

路径:https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php
参数:
query: 8.8.8.8 (要查询的IP地址,必传
co: (不知什么参数,可以不传
resource_id: 5809 (资源ID,必传
t: 1636341598109 (时间戳,可以不传
ie: utf8 (输入编码,可以不传
oe: gbk (输出编码,可以不传
cb: op_aladdin_callback (好像是回调参数,可以不传
format: json (返回值格式,可以不传
tn: baidu (提交搜索请求来源,可以不传
cb: jQuery1102030225422148935555_1636339389380 (返回值类型,可以不传
_: 1636339389386 (也是时间戳,可以不传

3.确定接口
经过第二步的操作,得到一个可用的接口,返回值类型为json字符串:

https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=8.8.8.8&co=&resource_id=5809

放到浏览器访问,能够得到下图中的结果:
在这里插入图片描述
(工具JSON-handle)

三.效果展示

1.数据库

1.1数据表
数据表存储了IP、IP归属地、IP运营商信息。
在这里插入图片描述

1.2数据量
一共存储了58677个IP地址信息(去重后)。
在这里插入图片描述

2.IP地址分类分析-饼图

在这里插入图片描述
这里将所有IP地址进行划分,分出来ABCDE类地址,划分规则如下图。可以看到A类地址占很大比例。
在这里插入图片描述


3.IP地址分布可视化-地图

在这里插入图片描述
将IP地址归属地信息可视化地标注在地图上(对归属地在国外的IP不做分析展示),山东的IP地址较多,共有12805个,其次是广东、香港、北京。

4.IP地址分布分析-饼图

什么,刚才的地图你没看出来广东、香港和北京占比也很大?那么请看下图。
在这里插入图片描述

5.IP地址分布可视化-条形图

在这里插入图片描述
条形统计图可以清楚地表明各种数量的多少。


6.IP地址运营商占比分析-饼图

在这里插入图片描述
统计所有IP地址归属的运营商。通过上面的饼图,能看到教育网占比最大,其次是电信阿里云

7.IP地址运营商占比可视化-条形图

在这里插入图片描述
条形统计图是用条形的长短来代表数量的大小,本文用于分析、展示IP运营商数量。

四.源代码

1.get_ip_infos.py(IP地址信息爬虫)

#coding:utf-8
import requests
import json
import time
import re

import xlwt

"""
resource_id 参数很重要
"""

class IP_ana:

    def read_txt(self,txt_file)->list:
        """
        读取文件中的IP地址,去掉末尾的换行符
        :param txt_file:
        :return:
        """
        with open(txt_file,'r',encoding="utf-8")as f:
            data=[ip.strip() for ip in f.readlines()]
            return data

    def fmt_ip(self,ip)->str:
        """
        对IP地址进行格式化,去掉其中的端口号
        :param ip: 待处理的IP地址
        :return: IPv4格式的IP地址
        """
        regx="(([01]{0,1}d{0,1}d|2[0-4]d|25[0-5]).){3}([01]{0,1}d{0,1}d|2[0-4]d|25[0-5])"
        if ":" in ip:
            aim_ip=ip.split(":")[0]
        else:
            aim_ip=ip
        if re.match(regx,aim_ip):
            return aim_ip
        else:
            return False

    def do_request(self,ip)->str:
        """
        对接口进行访问
        :param ip: url必须参数
        :return: 网页源代码
        """
        try:
                full_url=f"https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query={ip}&co=&resource_id=5809"
                headers={
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"
                }
                r=requests.get(full_url,headers=headers)
                if r.status_code==200:
                    html=r.text.encode('utf-8').decode('unicode_escape')
                    # print(html)
                    return html
        except:
            return False

    def get_ip_attribute(self,html)->dict:
        """
        获取IP归属地
        :param html:
        :return:
        """
        try:
            item={}
            _json=json.loads(html)
            data=_json.get("data")
            item['location'],item['ISP']=data[0].get("location").split(" ")
            return item
        except:
            return False

    def save_result(self,data)->None:
        """
        存储爬取结果
        :param data:
        :return:
        """
        with open(aim_txt,'a',encoding='utf-8')as f:
            f.write(data+'n')

    def write_to_excel(self,all_data)->None:
        """
        写入excel
        :param all_data:
        :return:
        """
        workbook = xlwt.Workbook()
        worksheet = workbook.add_sheet('sheet', cell_overwrite_ok=True)
        headers = ['IP', '归属地', 'ISP', ]
        worksheet.write(0, 0, headers[0])
        worksheet.write(0, 1, headers[1])
        worksheet.write(0, 2, headers[2])
        for index, data in enumerate(all_data):
            worksheet.write(index + 1, 0, data[0])
            worksheet.write(index + 1, 1, data[1])
            worksheet.write(index + 1, 2, data[2])
        workbook.save(excel_path)


if __name__ == '__main__':
    a=IP_ana()
    all_ips=a.read_txt("test_ip.txt")
    aim_txt="./2021-11-2_test_result.txt"
    excel_path = "./combine_result.xls"
    for ip in all_ips:
        ip_fmt=a.fmt_ip(ip)
        if ip_fmt:
            ip_infos=a.do_request(ip_fmt)
            if ip_infos:
                item=a.get_ip_attribute(ip_infos)
                if item:
                    data=ip_fmt+":t"+item['location']+"t"+item["ISP"]
                    print(data)
                    a.save_result(data)
                else:
                    print(ip_fmt + ":t" + "解析失败!")
            else:
                print((ip_fmt + ":t" + "获取信息失败!"))
        else:
            print((ip + ":t" + "不是标准IPv4格式!"))
    #读取txt,写入excel
    ip_data=a.read_txt(aim_txt)
    excel_data_list=[]
    for ip_data_ in ip_data:
        ip_data_list=[ip_data_.strip().split("t")]
        excel_data_list.append(ip_data_list)
    a.write_to_excel(excel_data_list)

2.analysis_ip_infos.py(IP地址数据可视化分析)

import pymysql
from pyecharts.charts import Map, Bar
from pyecharts.charts import Pie
from pyecharts import options as opts
from pyecharts.globals import ThemeType

"""
地图:
饼图:
地图:
"""

def get_data_from_mysql():
    """
    从数据库获取数据
    :return:
    """
    try:
        conn=pymysql.connect(
            host='127.0.0.1',
            port=3306,
            user='root',
            password='root',
            db='ip_count',
            charset='utf8'
        )
        cursor=conn.cursor()
        sql="select * from ip_count"
        cursor.execute(sql)
        data=cursor.fetchall()
        return data
    except pymysql.Error:
        print("数据库操作出现错误!")
    finally:
        cursor.close()
        conn.close()


def sort_ip(data):
    sort_result={}
    sort_result["A类"]=0
    sort_result["B类"]=0
    sort_result["C类"]=0
    sort_result["D类"]=0
    sort_result["E类"]=0
    for ip in data:
        fisrt_num=ip.split('.')[0]
        if 0<=int(fisrt_num)<=127:
            sort_result["A类"]+=1
        elif  128<=int(fisrt_num)<=191:
            sort_result["B类"]+=1
        elif  192<=int(fisrt_num)<=223:
            sort_result["C类"]+=1
        elif  224<=int(fisrt_num)<=239:
            sort_result["D类"]+=1
        elif  240<=int(fisrt_num)<=247:
            sort_result["E类"]+=1
        else:
            print(ip)
    return sort_result

def sort_provinces(data):
    """
    对省份信息进行分类排序
    :param data:
    :return:
    """
    sort_result_item={}
    Province_34 = ['北京', '上海', '海南', '贵州', '湖北', '重庆', '江苏', '安徽', '澳门特别行政区', '四川', '江西', '浙江', '青海', '河南',
                   '天津', '台湾', '湖南', '陕西', '黑龙江', '广东', '香港', '河北', '辽宁', '福建', '广西', '西藏', '内蒙古', '新疆', '云南',
                   '甘肃', '宁夏', '山西', '山东', '吉林']
    provinces=[line[1] for line in data]
    for line in provinces:
        if line !=None:
            for p in Province_34:
                if p in line:
                    if p in sort_result_item.keys():
                        sort_result_item[p]+=1
                    else:
                        sort_result_item[p]=0
    sort_result=sorted(sort_result_item.items(),key=lambda x:x[1],reverse=True)
    return sort_result

def sort_ISP_data(data):
    """
    对ISP信息进行分类统计排序
    :param data:
    :return:
    """
    data=[line[2] for line in data if line[2]!=None]
    item={}
    for isp in data:
        if isp in item.keys():
            item[isp]+=1
        else:
            item[isp] = 0
    item_sorted=sorted(item.items(),key=lambda x:x[1],reverse=True)
    return item_sorted

def draw_map(data):
    """
    将分类好的省份信息绘制成地图
    :param data:
    :return:
    """
    area1=[d[0] for d in data]
    area2=[d[1] for d in data]
    aim_num=max(area2)
    num_max_pos = len(str(aim_num)) - 2
    mid_num = divmod(aim_num, int("1" + "0" * num_max_pos))
    res_num = str(mid_num[0] + 1) + "0" * (num_max_pos)
    map =
        Map(init_opts=opts.InitOpts(width="1000px", height="600px"))
            .add("",[list(z) for z in zip(area1, area2)], 'china')
            .set_global_opts(title_opts=opts.TitleOpts(title="IP地址分布可视化-地图")
    ,visualmap_opts = opts.VisualMapOpts(max_=res_num, split_number=8, is_piecewise=True,precision=0))
    map.render("./IP地址分布可视化-地图.html")

def draw_line(data,type_):
    """
    将ISP和省份信息绘制成条形图
    :param data:
    :param type_:
    :return:
    """
    bar = (
        Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT)) #使用主题
            .add_xaxis([data[0] for data in data])
            .add_yaxis("占比",[data[1] for data in data])
            .set_global_opts(
            title_opts=opts.TitleOpts(title=f"IP地址{type_}可视化-条形图.html"),
            datazoom_opts=opts.DataZoomOpts(),
        )
    )
    bar.render(f"IP地址{type_}可视化-条形图.html")

def draw_pie(data,type_):
    """
    将ISP和省份信息绘制成饼图
    :param data:
    :return:
    """
    c = (
        Pie()
            .add(f"IP地址{type_}可视化", data,color = "green",rosetype = "radius")
            .set_colors(["lightblue", "orange", "yellow", "blue", "pink", "green", "purple", "black"])
            .set_global_opts(title_opts=opts.TitleOpts(title=f"IP地址{type_}可视化-饼图"),legend_opts=opts.LegendOpts(
            orient="vertical", #图例垂直放置
            pos_top="15%",# 图例位置调整
            pos_left="2%"),
    )
            .set_series_opts(label_opts=opts.LabelOpts(formatter="{b} : {c} ({d}%)"))
            .render(f"IP地址{type_}分析-饼图.html")
    )

if __name__ == '__main__':
    data=get_data_from_mysql()
    ip_data=[line[0] for line in  data]
    sort_ip_type_data=[(k,v) for k,v in sort_ip(ip_data).items()]
    draw_pie(sort_ip_type_data,"分类")

    ip_sort_res=sort_provinces(data)
    draw_map(ip_sort_res)
    draw_pie(ip_sort_res[:8],"分布")
    draw_line(ip_sort_res[:8],"分布")

    isp_sort_res=sort_ISP_data(data)
    draw_pie(isp_sort_res[:8],"运营商占比")
    draw_line(isp_sort_res[:8],"运营商占比")

五.总结

本次使用Python的pyecharts绘制了饼图、条形图、地图,可视化地将IP地址归属地分布、运营商分布展示出来,代码量不大,pyecharts中都给封装好了,调用其中的接口函数就能够轻易实现,希望大家也动手做起来。如果喜欢这些Echarts图可以到蓝奏云下载。思路、代码方面有什么不足欢迎各位大佬指正、批评!觉得还可以的能点个赞嘛。
在这里插入图片描述

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>