将以下代码放入到油猴,新建脚本,保存。也可以F12,放入控制台执行,但是可能会没有效果,除非是一直在变的dom。
//将需要监听的dom selector 替换掉 span.title-follownum new MutationObserver((mutations, observer) => { const el = document.querySelector("span.Title-followNum") if (el != null) { observer.disconnect() new MutationObserver((mutations, observer) => { debugger }).observe(el, {childList: true, subtree: true}) } }).observe(document, {childList: true, subtree: true})
二、前端可能使用的加密算法
base64、rot13
三、sdl渲染字体到图片
#include <stdio.h> #include <SDL2/SDL.h> #include <SDL2/SDL_ttf.h> #include <SDL2/SDL_image.h> int main(void) { if(TTF_Init() == -1) { printf("error: %s\n", TTF_GetError()); return 1; } TTF_Font *font = TTF_OpenFont("test.woff", 50); if(font == NULL) { printf("error: %s\n", TTF_GetError()); return 1; } SDL_Color black = { 0x00, 0x00, 0x00 }; SDL_Surface *surface = TTF_RenderText_Solid(font, "0123456789", black); if(surface == NULL) { printf("error: %s\n", TTF_GetError()); return 1; } IMG_SavePNG(surface, "test.png"); return 0; }
四、将数字加密为字体
常见的字体格式有 ttf
, otf
, woff
。其中 woff
是一个包装格式,里面的字体不是 ttf
就是 otf
的,所以真正的存储格式只有两种,ttf
和 otf
。
这两种格式很明显都是二进制格式,没法直接打开看。但是,幸运的是,字体有一个格式叫做 ttx
,是一个 XML 的可读格式。
我们的基本思路是:
裁剪字体:根据一个基础字体裁剪掉我们不需要的字符,比如斗鱼这种情况,我们只需要数字即可
将字体转换成 ttx
格式打开
找到字符和图形的映射
修改这个映射
再导出字体为 ttf
这里我们使用 fonttools 这个强大的 Python 库来进行后续的操作。
4.1裁剪字体
$ pyftsubset hack.ttf --text="0123456789" WARNING: TTFA NOT subset; don't know how to subset; dropped
上面的 warning 不用介意,运行完毕之后我们得到了 hack.subset.ttf
,这个便是裁剪后的字体,只支持渲染 0 ~ 9。
4.2转换字体为可读的 ttx 格式
$ ttx hack.subset.ttf Dumping "hack.subset.ttf" to "hack.subset.ttx"... Dumping 'GlyphOrder' table... Dumping 'head' table... Dumping 'hhea' table... Dumping 'maxp' table... Dumping 'OS/2' table... Dumping 'hmtx' table... Dumping 'cmap' table... Dumping 'fpgm' table... Dumping 'prep' table... Dumping 'cvt ' table... Dumping 'loca' table... Dumping 'glyf' table... Dumping 'name' table... Dumping 'post' table... Dumping 'gasp' table... Dumping 'GSUB' table...
4.3
我们会发现目录下多了一个 hack.subset.ttx
文件,打开观察一下。
很容易就可以发现,cmap
标签中定义了字符和图形的映射。
<cmap> <tableVersion version="0"/> <cmap_format_4 platformID="0" platEncID="3" language="0"> <map code="0x30" name="zero"/><!-- DIGIT ZERO --> <map code="0x31" name="one"/><!-- DIGIT ONE --> <map code="0x32" name="two"/><!-- DIGIT TWO --> <map code="0x33" name="three"/><!-- DIGIT THREE --> <map code="0x34" name="four"/><!-- DIGIT FOUR --> <map code="0x35" name="five"/><!-- DIGIT FIVE --> <map code="0x36" name="six"/><!-- DIGIT SIX --> <map code="0x37" name="seven"/><!-- DIGIT SEVEN --> <map code="0x38" name="eight"/><!-- DIGIT EIGHT --> <map code="0x39" name="nine"/><!-- DIGIT NINE --> </cmap_format_4> ... </cmap>
0x30
也就是字符 0 对应 name="zero"
的 TTGlyph
,TTGlyph 中定义了渲染要用的数据,也就是一些坐标。
<TTGlyph name="zero" xMin="123" yMin="-29" xMax="1110" yMax="1520"> <contour> <pt x="617" y="-29" on="1"/> <pt x="369" y="-29" on="0"/> <pt x="246" y="165" on="1"/> <pt x="123" y="358" on="0"/> <pt x="123" y="745" on="1"/> <pt x="123" y="1134" on="0"/> <pt x="246" y="1327" on="1"/> <pt x="369" y="1520" on="0"/> <pt x="616" y="1520" on="1"/> <pt x="864" y="1520" on="0"/> <pt x="987" y="1327" on="1"/> <pt x="1110" y="1134" on="0"/> <pt x="1110" y="745" on="1"/> <pt x="1110" y="-29" on="0"/> </contour> ... </TTGlyph>
那么怎么制作混淆字体的方法就不言而喻了,我们修改一下这个 XML,把 TTGlyph(name="zero")
标签的 zero
换成 eight
然后把 TTGlyph(name="eight")
标签的 eight
换成 zero
,保存文件为 fake.ttx
。
导出 ttx 到 ttf 依然是使用 ttx
工具。
$ ttx -o fake.ttf fake.ttx Compiling "fake.ttx" to "fake.ttf"... Parsing 'GlyphOrder' table... Parsing 'head' table... Parsing 'hhea' table... Parsing 'maxp' table... Parsing 'OS/2' table... Parsing 'hmtx' table... Parsing 'cmap' table... Parsing 'fpgm' table... Parsing 'prep' table... Parsing 'cvt ' table... Parsing 'loca' table... Parsing 'glyf' table... Parsing 'name' table... Parsing 'post' table... Parsing 'gasp' table... Parsing 'GSUB' table...
使用上文提到的 HTML 使用 fake.ttf
渲染 0 ~ 9,可以看到,我们成功地制作了一个混淆字体。
附脚本
./genfont.py hack.subset.ttf 20
#!/usr/bin/env python # 生成用于数字混淆的字体文件用于反爬 # 即字体对于数字的渲染是错误的,例如数字 1 会渲染成 5 # ./genfont.py <font-file> <count> # 生成字体在 result/generated 目录中 import sys import os import subprocess from pathlib import Path import random from bs4 import BeautifulSoup import copy import hashlib names = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] # must contain glyphs with name "zero" "one" .. "nine" def check_font(ttx): for name in names: if ttx.find("TTGlyph", attrs={"name": name}) is None: return False return True def gen(ttx): mapping = names[:] random.shuffle(mapping) target = copy.copy(ttx) for name in names: target.find("TTGlyph", {"name": name})["id"] = name for idx, name in enumerate(names): tmp = target.find("TTGlyph", attrs={"id": mapping[idx]}) tmp.attrs = {} for k, v in ttx.find("TTGlyph", attrs={"name": name}).attrs.items(): tmp[k] = v content = target.prettify() name = hashlib.md5(content.encode("utf8")).hexdigest()[:10] + "." + "".join([str(names.index(x)) for x in mapping]) print(f"Generate temporary ttx: {name}.ttx") target_ttx_path = os.path.join("result", "tmp", f"{name}.ttx") with open(target_ttx_path, "w") as f: f.write(content) target_ttf_path = os.path.join("result", "generated", f"{name}.ttf") print(f"Generate target ttf: {target_ttf_path}") subprocess.run(f"ttx -o {target_ttf_path} {target_ttx_path}", shell=True, check=True) def run(font_file, count): ttx_name = os.path.splitext(font_file)[0] + ".ttx" ttx_path = os.path.join("result", "tmp", ttx_name) if not Path(ttx_path).exists(): print("Convert ttf to ttx..") subprocess.run(f"ttx -o {ttx_path} {font_file}", shell=True, check=True) with open(ttx_path) as f: ttx = BeautifulSoup(f, "xml") if not check_font(ttx): print("font must contain glyphs with name 'zero', 'one', 'two' .. 'nine'") exit(1) for _ in range(count): gen(ttx) if __name__ == "__main__": if len(sys.argv) < 3: print(f"usage: ./genfont.py <font-file> <count>") exit(1) # create necessary dirs os.makedirs(os.path.join("result", "generated"), exist_ok=True) os.makedirs(os.path.join("result", "tmp"), exist_ok=True) run(sys.argv[1], int(sys.argv[2]))
原文
https://cjting.me/2020/07/01/douyu-crawler-and-font-anti-crawling/
本文为看恩吧原创文章,转载无需和我联系,但请注明来自knsay.com