两周前,签名图(https://sig.dogcraft.top/img.jpg)的访问量自从有记录以来突破了一万次,为纪念访问量突破一万次,重写了大部分代码,并写了这篇搭建教程。
基本原理
原理并不复杂,浏览器向服务器请求图片时,浏览器会向服务器提供诸如IP地址、浏览器版本和访问时间等基本信息,服务器可以利用这些信息,生成相关的图片返回给浏览器,这样浏览器会收到一张写有用户端信息的独一无二的图片。如果这张图片的url被放在论坛的签名档里面,所有看到签名档的用户就会看到写有自己IP地址及相关信息的签名图。
签名图样式(IP地址是联通4G公网NAT出口地址,没什么意义)
实现过程
要实现这个过程并不复杂,我就采用最熟悉的flask框架来做这件事。
首先是获取浏览器的基本信息(IP地址、UA等),在flask框架下获取相关信息很容易:
from flask import (Flask,request)app = Flask(__name__)@app.route('/')def index(): dog_ip = request.remote_addr#获取IP地址 dog_ua = request.user_agent#获取浏览器UA return [dog_ip,dog_ua]
获取了这两个基本信息之后,需要分别解析出浏览器的版本,所在的操作系统与ip地址所对应的物理地址。获取ip地址相关信息有很多方法,这里采用,采用https://ip.zxinc.org/api.php 的 ip 地址 api。调用方法如下:
import requestsdef get_ip_info(dogip): canshu = {'ip': dogip, 'type': 'json'} r = requests.get('https://ip.shanshan-business.com/api.php', params=canshu) resd = (r.content.decode('utf8')) res = json.loads(resd[1:]) if res['code'] != 0: fu = [' ', ' '] return fu dog_location = res['data']['location'] dog_ip_fw = '{} - {}'.format(res['data']['ip']['start'], res['data']['ip']['end']) return [dog_location, dog_ip_fw]
这个api会返回两个信息,分别是对应地理位置以及IP段范围。
另个关键点是解析解析浏览器的UA,即通过UA字段解析出客户端所用的浏览器、操作系统以及设备,这里采用的是user_agents这个库:
from user_agents import parsedog_ua = request.user_agentdog_uap = parse(str(dog_ua))dog_os = dog_uap.get_os()#获取操作系统信息dog_browser = dog_uap.get_browser()#获取浏览器信息dog_device = dog_uap.get_device()#获取设备信息
获取这两个信息之后就可以开始画出图形了。
首先应该准备一个适当大小的背景图片,用来充当签名图的基底,打开这个文件应该在初始化阶段,将文件内容读入到内存中,当浏览器请求签名图的时候再把这个图片在内存中复制一遍,在副本的基础上进行画图,使得整个过程可以可持续进行。
为了配合flask输出文件,需要把这个文件以字符串的形式在python中保存与处理,这需要io这个库
basdog = Image.open('bd.png', 'r')def dog_pic(ip, refer, ua): [dog_location, dog_ip_fw] = get_ip_info(ip)#获取ip地址相关信息 dog_uap = parse(str(ua)) dog_os = dog_uap.get_os() dog_browser = dog_uap.get_browser() dog_device = dog_uap.get_device() dog_num = get_number() #生成相关文本 dog_text_1 = '只争朝夕,不负韶华。IP段 : {}'.format(dog_ip_fw) dog_text_2 = 'IP地址: {} {}'.format(ip, dog_location) dog_text_3 = '{}'.format(refer) dog_text_4 = '浏览器: {} OS: {} 设备: {}'.format( dog_browser, dog_os, dog_device) dog_text_5 = '已被访问{}次 北京时间:{}'.format(dog_num, time.ctime()) dog_text_6 = '为庆祝有记录以来访问量已经突破十万次,本签名图即将改版!' # 开始画图了 image = basdog.copy()#复制基底片 #选择字体 font1 = ImageFont.truetype('/home/yu/.local/share/fonts/仿宋_GB2312.ttf', 14) font2 = ImageFont.truetype('/home/yu/.local/share/fonts/仿宋_GB2312.ttf', 20) draw = ImageDraw.Draw(image) #获取画笔 #文字写入对应位置 draw.text((10, 30), dog_text_1, font=font2, fill=(255, 0, 0)) draw.text((10, 70), dog_text_2, font=font2, fill=(0, 0, 0)) draw.text((10, 110), dog_text_3, font=font1, fill=rndColor2()) draw.text((10, 150), dog_text_4, font=font2, fill=rndColor2()) draw.text((10, 180), dog_text_5, font=font2, fill=rndColor2()) draw.text((10, 210), dog_text_6, font=font1, fill=rndColor2()) buf = io.BytesIO() # 字符串文件 image.save(buf, 'jpeg') #将画好的图片保存 image.close() # 转化成flask的返回格式 buf_str = buf.getvalue() response = app.make_response(buf_str) response.headers['Content-Type'] = 'image/jpeg' return response
剩下的部分内容(统计访问数量、部署方式以及完整代码)将在《基于flask的论坛签名档图片(下)》中完成。
如果想看一下效果,可以点下面的原文链接,签名图的地址已经放上去了。
Q:为什么不一次性写完?
A:……之前提了好几周的封面图已经换好了