快手H5逆向,字体反爬,互动量获取

目标

获取快手视频点赞数、评论数、 播放数

分析视频接口

https://m.gifshow.com/fw/photo/5232901352181146092

请求头:#

sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
sec-ch-ua-mobile: ?1
sec-ch-ua-platform: "Android"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: didv=1644894188000; did=web_160a5eac2ed14dbda625edcf277953d1; sid=9995d540410efc97b28229c2; Hm_lvt_86a27b7db2c5c0ae37fee4a8a35033ee=1644894193; Hm_lpvt_86a27b7db2c5c0ae37fee4a8a35033ee=1645090238

响应(关键信息window.pageData=):略#

互动量相关JSON.obfuseData:#

{
    "id": 263,
    "fontCdnUrl": "https://tx2.a.kwimgs.com/kos/nlav10312/mdata/pkg/kwai-font/fontscn_263f9585.ttf",
    "fontCdnStyle": "<style>\n        @font-face {\n            font-family: kwaiFont;\n            src: url(https://tx2.a.kwimgs.com/kos/nlav10312/mdata/pkg/kwai-font/fontscn_263f9585.ttf) format(\"truetype\");\n        }\n        .font {\n            font-family: \"kwaiFont\";\n            font-style: normal;\n            font-weight: normal;\n            font-variant: normal;\n            text-transform: none;\n            line-height: 1;\n            -webkit-font-smoothing: antialiased;\n        }\n    </style>",
    "commentCount": "<SPAN STYLE='FONT-FAMILY: kwaiFont;'>&#xf7a6;&#xf7a6;&#xe43e;</SPAN>",
    "likeCount": "<SPAN STYLE='FONT-FAMILY: kwaiFont;'>&#xe7ca;&#xe7ca;&#xec7e;&#xf6c7;</SPAN>",
    "viewCount": "<SPAN STYLE='FONT-FAMILY: kwaiFont;'>&#xec7e;&#xe729;&#xf5d6;&#xec7e;&#xeac6;</SPAN>"
}

页面中显示:#

<p class="video-like">  <span style="FONT-FAMILY: kwaiFont;"></span>  </p>

可以明显看出是有字体反爬处理,需要下载字体文件进行解密

什么是字体反爬?

使用自定义的ttf文件来渲染网页中的文字,而网页中的文字不再是文字,而是相应的字体编码,通过复制或者简单的采集是无法采集到编码后的文字内容

字体反爬实现原理#

厂商会随机生成成千上万套字库,并且保存好编码、字库文件、字的映射关系。文章显示的时候会从库中随机查询一套字库,并把文章中的替换成unicode编码,以达到字体加密的效果。

字体反爬的前世今生

初级难度:一套字体做加密#

做好映射解密即可

中级难度:生成多套字体和编码#

比如:猫眼电影,58同城等等,汽车之家
虽然生成了多套,但是每个字对应的字体信息是不变
比如一个字的笔画数,和x,y 变化都很小,可以利用这个特征解析字体的xml文件,做特征映射

高级难度:字体变形,字体信息随机化#

比如:快手

静态字体解密

下载字体用FontCeator打开#

将字体文件转为xml#

先用FontCreator将字体转为woff文件,再用python转为xml

    <GlyphID id="0" name=".notdef"/>
    <GlyphID id="1" name="uniEAC6"/>
    <GlyphID id="2" name="uniEC96"/>
    <GlyphID id="3" name="uniF38C"/>
    <GlyphID id="4" name="uniF088"/>
    <GlyphID id="5" name="uniEC7E"/>
    <GlyphID id="6" name="uniF5D6"/>
    <GlyphID id="7" name="uniF20E"/>
    <GlyphID id="8" name="uniF7A6"/>
    <GlyphID id="9" name="uniF5B9"/>
    <GlyphID id="10" name="uniE9D5"/>
    <GlyphID id="11" name="uniE43E"/>
    <GlyphID id="12" name="uniE729"/>
    <GlyphID id="13" name="uniE47F"/>
    <GlyphID id="14" name="uniE7CA"/>
    <GlyphID id="15" name="uniF6C7"/>

注意:这个xml表示的是id和值的映射关系。比如
uniE43E字体对应真实的值:4 , 对应的glyph11

查看字体解密规则#

"commentCount": "<SPAN STYLE='FONT-FAMILY: kwaiFont;'>&#xf7a6;&#xf7a6;&#xe43e;</SPAN>",


规则:&#+16进制加上;号

解密#

逆推字体加密
将字体的key 转为16进制,&#+16进制与加密的对比,得到对应的值

[ #编写对应的列表
    'w', '5', 'm', 'k', '6', '.', '1', '3', '+', '0', '4', '8', '9',
    '7', '2'
]

fontCdnUrl = "https://tx2.a.kwimgs.com/kos/nlav10312/mdata/pkg/kwai-font/fontscn_263f9585.ttf"
key_map = {}

font_content = requests.get(fontCdnUrl).content
font = TTFont(BytesIO(font_content)) 
code = font.getGlyphOrder()[1:] 
nums = [ 
    'w', '5', 'm', 'k', '6', '.', '1', '3', '+', '0', '4', '8', '9',
    '7', '2'
]
temp = dict(zip(code, nums)) 
res = font.getBestCmap()  
for k, v in res.items(): #解析字体
    print("字体的原始:" +str(k))
    kk = str(hex(k))
    print("字体的转换成16进制:" + str(kk))
    kk = kk[1:]
    print("去除前缀:" + str(kk))
    kk ='&#' + kk
    print("拼接&#得到最后加密的:" + str(kk))
    print(str(v)+"字体对应真实的值:" + str(temp[v]))
    key_map[kk] = temp[v]
re_html_code = re.compile(r'&#x[\da-f]{4}')
text = "&#xe7ca;&#xe7ca;&#xec7e;&#xf6c7;"  #7751confusionLikeCount=&#xe179;&#xe343;&#xf878;&#xf1a9;
words = re_html_code.findall(text)
result = ''.join(key_map[i] for i in words)
print(result)

得到7762, 静态字体破解搞定~

动态字体解密

每次请求的字体都不一样

可以发现字体的形状不同,无法通过笔画来映射

 <pt x="-10158" y="550" on="1"/>
        <pt x="-10154" y="545" on="0"/>
        <pt x="-10161" y="534" on="1"/>
        <pt x="-10160" y="350" on="0"/>
        <pt x="-10490" y="54" on="1"/>
        <pt x="-10492" y="27" on="1"/>
        <pt x="-10492" y="5" on="1"/>
        <pt x="-10285" y="0" on="1"/>
        <pt x="-10156" y="4" on="1"/>
        <pt x="-10031" y="-5" on="1"/>
        <pt x="-10035" y="16" on="1"/>
        <pt x="-10029" y="58" on="1"/>
        <pt x="-10034" y="87" on="1"/>
        <pt x="-10232" y="75" on="1"/>
        <pt x="-10236" y="87" on="1"/>
        <pt x="-10256" y="83" on="0"/>
        <pt x="-10300" y="83" on="1"/>
        <pt x="-10323" y="77" on="0"/>
        <pt x="-10356" y="78" on="1"/>
        <pt x="-10116" y="306" on="0"/>
        <pt x="-10077" y="472" on="1"/>
        <pt x="-10071" y="499" on="0"/>
        <pt x="-10066" y="529" on="1"/>
        <pt x="-10070" y="610" on="0"/>
        <pt x="-10110" y="665" on="1"/>
        <pt x="-10119" y="688" on="0"/>
        <pt x="-10124" y="692" on="1"/>
        <pt x="-10156" y="716" on="0"/>
        <pt x="-10201" y="737" on="1"/>
        <pt x="-10237" y="742" on="0"/>
        <pt x="-10282" y="743" on="1"/>
        <pt x="-10394" y="740" on="0"/>
        <pt x="-10497" y="632" on="1"/>
        <pt x="-10442" y="594" on="1"/>
        <pt x="-10441" y="592" on="1"/>
        <pt x="-10372" y="671" on="0"/>
        <pt x="-10293" y="672" on="1"/>
        <pt x="-10226" y="677" on="0"/>
        <pt x="-10191" y="630" on="1"/>
        <pt x="-10163" y="603" on="0"/>
 <pt x="-9192" y="740" on="0"/>
        <pt x="-9129" y="740" on="1"/>
        <pt x="-9114" y="755" on="0"/>
        <pt x="-9099" y="746" on="1"/>
        <pt x="-9037" y="747" on="0"/>
        <pt x="-8991" y="706" on="1"/>
        <pt x="-8986" y="698" on="0"/>
        <pt x="-8973" y="696" on="1"/>
        <pt x="-8973" y="695" on="0"/>
        <pt x="-8975" y="695" on="1"/>
        <pt x="-8917" y="624" on="0"/>
        <pt x="-8920" y="525" on="1"/>
        <pt x="-8920" y="337" on="0"/>
        <pt x="-9203" y="69" on="1"/>
        <pt x="-9180" y="74" on="0"/>
        <pt x="-9159" y="75" on="1"/>
        <pt x="-9102" y="79" on="0"/>
        <pt x="-9086" y="83" on="1"/>
        <pt x="-9016" y="81" on="1"/>
        <pt x="-8878" y="77" on="1"/>
        <pt x="-8881" y="66" on="1"/>
        <pt x="-8882" y="13" on="1"/>
        <pt x="-8881" y="-3" on="1"/>
        <pt x="-8917" y="-1" on="1"/>
        <pt x="-9346" y="2" on="1"/>
        <pt x="-9340" y="50" on="1"/>
        <pt x="-9345" y="58" on="1"/>
        <pt x="-9008" y="348" on="0"/>
        <pt x="-9006" y="534" on="1"/>
        <pt x="-9003" y="552" on="0"/>
        <pt x="-9008" y="574" on="1"/>
        <pt x="-9016" y="607" on="0"/>
        <pt x="-9036" y="635" on="1"/>
        <pt x="-9049" y="639" on="0"/>
        <pt x="-9063" y="648" on="1"/>
        <pt x="-9072" y="660" on="0"/>
        <pt x="-9080" y="658" on="1"/>
        <pt x="-9077" y="662" on="0"/>
        <pt x="-9083" y="669" on="1"/>
        <pt x="-9107" y="671" on="0"/>
        <pt x="-9144" y="676" on="1"/>
        <pt x="-9163" y="678" on="0"/>
        <pt x="-9187" y="672" on="1"/>
        <pt x="-9206" y="651" on="0"/>
        <pt x="-9229" y="648" on="1"/>
        <pt x="-9263" y="626" on="0"/>
        <pt x="-9293" y="586" on="1"/>
        <pt x="-9314" y="600" on="1"/>
        <pt x="-9326" y="628" on="1"/>
        <pt x="-9349" y="639" on="1"/>
        <pt x="-9304" y="686" on="0"/>
        <pt x="-9259" y="716" on="1"/>
        <pt x="-9251" y="717" on="0"/>
        <pt x="-9242" y="719" on="1"/>

经过下载多个字体文件查看,发现如下:

  • 每次请求都是不同的字体,字体库不是简单的几套
  • 笔画数不同
  • x,y坐标不同,并且移动很大

所以无法通过解析字体的xml利用有规律的特征来做映射

思路一:#

将获取到的加密字段自己组装成html显示后,截图,ocr识别出来

<style>
   @font-face {
       font-family: kwaiFont;
       src: url(https://tx2.a.kwimgs.com/kos/nlav10312/mdata/pkg/kwai-font/fontscn_28789ff8.ttf) format("truetype");
   }
   .font {
       font-family: "kwaiFont";
       font-style: normal;
       font-weight: normal;
       font-variant: normal;
       text-transform: none;
       line-height: 1;
       -webkit-font-smoothing: antialiased;
   }
</style>
<div>
   <SPAN STYLE='FONT-FAMILY: kwaiFont;'>&#xe4bf;&#xe4bf;&#xf735;</SPAN>
</div>

保存为html#

截图#

OCR 识别#

思路二#

将字体文件字体分割为png,ocr识别成出来。放入集合按顺序排列

下载安装tesseract-ocr#

下载语言包#

https://tesseract-ocr.github.io/tessdoc/Data-Files
放入Tesseract-OCR\tessdata

配置环境变量#

将字体切割成图片代码#

def uni_2_png_stream(txt, font, img_size=512):
    """将字形转化为图片流

    Args:
        txt ([type]): [description]
        font ([type]): [description]
        img_size (int, optional): [description]. Defaults to 512.

    Returns:
        [type]: [description]
    """
    img = Image.new('1', (img_size, img_size), 255)
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype(font, int(img_size * 0.7))

    txt = chr(txt)
    x, y = draw.textsize(txt, font=font)
    draw.text(((img_size - x) // 2, (img_size - y) // 2), txt, font=font, fill=0)
    # draw.text((0,0), txt, font=font, fill=0)
    return img

对比ocr效果#

uniE43E字体对应真实的值:4
uniE47F字体对应真实的值:9
uniE729字体对应真实的值:8
uniE7CA字体对应真实的值:7
uniE9D5字体对应真实的值:0
uniEAC6字体对应真实的值:w
uniEC7E字体对应真实的值:6
uniEC96字体对应真实的值:D
uniF088字体对应真实的值:k
uniF20E字体对应真实的值:1
uniF38C字体对应真实的值:m
uniF5B9字体对应真实的值:+
uniF5D6字体对应真实的值:.
uniF6C7字体对应真实的值:2
uniF7A6字体对应真实的值:3

可以看出来识别率90%,数字5识别成D了。

优化数字识别率 和ocr执行时间#

      pytesseract.pytesseract.tesseract_cmd = filename
    # text = pytesseract.image_to_string(image, lang='chi_sim', config='--psm 10')
        # 注意 这里替换语言库,提升纯数字识别准确率 和 执行时间 11秒 提升到6秒
        text = pytesseract.image_to_string(image, config='--psm 10')

测试了几个font文件,数字识别率100%

继续优化

tesseract:#

nums:['Ww', '+', '4', '9', '2', '6', 'mM', '8', '', '7', '3', 'k', '5', '0', '1']
INFO: 127.0.0.1:2629 - "GET /api/ks_interactive_data/3xdyxk622whqx2q HTTP/1.1" 200 OK
2022-03-01 12:28:14.721 | WARNING | utils:ocr_processor:146 - 识别耗时:2227.240800857544
2022-03-01 12:28:14.722 | WARNING | main:request:123 - result:0_2_65
2022-03-01 12:28:14.723 | WARNING | main:request:124 - 耗时:3415.691375732422

更换ocr识别后:#

2022-03-01 12:31:36.279 | WARNING | utils:ocr_processor:146 - 识别耗时:131.30450248718262
nums:['', '7', 'w', '2', '0', '9', '6', '3', '十', '4', '8', '5', 'k', '1', 'm']
INFO: 127.0.0.1:2923 - "GET /api/ks_interactive_data/3xdyxk622whqx2q HTTP/1.1" 200 OK
2022-03-01 12:31:36.290 | WARNING | main:request:123 - result:0_2_65
2022-03-01 12:31:36.290 | WARNING | main:request:124 - 耗时:1669.9013710021973

DID 风控解决

待下篇分解

大规模测试:#

10个线程,单IP,5个小时一共跑了 62264次,失败次数3266,成功率94%

平均一个小时12452次。

单个请求2秒以内完成。1秒4和1秒8居多,通过redis预存did的话,可以进一步提升耗时到1秒以内。

警告:仅供学习和参考,不可用于非法用途!

作者:765

出处:https://www.cnblogs.com/linuxxx/p/18342768

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   拓森765  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示