前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >2019年末逆向复习系列之从猫眼字体反爬分析谈谈字体反爬的前世今生

2019年末逆向复习系列之从猫眼字体反爬分析谈谈字体反爬的前世今生

作者头像
云爬虫技术研究笔记
发布于 2019-12-17 09:11:46
发布于 2019-12-17 09:11:46
93700
代码可运行
举报
运行总次数:0
代码可运行

最有深度的研究笔记由程序员界最会排版的追星族运营

文章信息

作者: Lateautumn4lin 来源:云爬虫技术研究笔记

AKA 逆向小学生

郑重声明:本项目的所有代码和相关文章, 仅用于经验技术交流分享,禁止将相关技术应用到不正当途径,因为滥用技术产生的风险与本人无关。

这篇文章是公众号《云爬虫技术研究笔记》的《2019年末逆向复习系列》的第六篇:《从猫眼字体反爬分析谈谈字体反爬的前世今生》

本次案例的代码都已上传到Review_Reverse上面,后面会持续更新,大家可以Fork一波。

背景分析

“字体反爬” 我相信大多数从事爬虫工作的工程师都接触过,这其实不是一种常规的反爬手段,它其实是页面和前端字体文件想配合完成的一个反爬策略。像最早使用字体反爬的58同城、汽车之家到现在很多App的web页面也开始使用,例如美团、猫眼、快手抖音等等。随着爬虫工程师和反爬工程师的不断对抗,字体反爬从一开始的单纯依靠一个写死的字体文件升级成现在最新动态的字体文件,而字体反爬的攻克也有一个开始的解析字体文件做数据映射到现在依靠KNN来做动态映射,算是经历了一个又一个光辉的“升级阶段”,这篇文章就是简单讲述一下字体反爬的演变史以及最新依靠KNN来做动态映射的破解思路。

历史分析

PS: 关于历史分析的项目都可以在Review_Reverse项目下的fonts目录看到

使用方法,输入以下代码即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
python -m fonts.xxxx

首先我们先理解字体反爬的原理,就是前端工程师通过自定义的字体来替换页面中某些关键的数据,那在HTML中如何使用自定义字体呢?答案就是使用@font-face,我们举个例子看看@font-face

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@font-face {
 font-family: <identifier>;
 src: <fontsrc> [, <fontsrc>]*; <font>;
  }

里面的font-family也就是一个特定的名字,src就表示你需要引用的具体的文件,而这个文件就是字体文件,一般是ttf类型eot类型,当然,现在因为ttf文件过大,在移动端使用的时候会导致加载速度过慢,woff类型的文件最近也广泛会用,所以一般大家现在碰到的都是woff类型的文件。那woff文件中的内容是什么呢?它是怎样把数据进行替换的呢?下面我们先简单的看个例子。 我们先把woff文件打开,需要使用两种工具打开:

  • FontCreator工具:https://www.high-logic.com/font-editor/fontcreator
  • 在线FontEditor工具:http://fontstore.baidu.com/static/editor/index.html

这里我们使用FontCreator,我们把FontCreator下载下来,传来一个我们之前准备好的woff文件看看效果

我们可以看到woff文件中每个字符都有一个编码对应,woff实际上就是编码和字符的映射表。我们再来看看页面中的被替换的词是什么形式

我们对比下可以发现,页面源码中的被替换字的就是woff文件中字符的编码加上$#x,所以大家可以发现字体替换的原理就是这样,我们使用一个简单的等式来表现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
“替换数据”=“$#x{woff文件中被替换数据的编码}

现在我们懂得了原理,下面开始回顾下字体反爬的演变历程

1. 阶段一:通过固定的字体文件进行数据替换

反爬方:一开始的时候,字体反爬还没有发展的很成熟,所以大部分网站使用字体反爬的方式是使用固定的字体文件来做数据替换,固定的字体文件就表明每个数据的编码是写死的,不变的,那么每次网站引用这个woff文件之后,都可以用相同的编码来替换想要替换的数据,这就是最初的时候的字体反爬。

应对方:既然他们的字体文件不变,那我们就直接解析他们的固定的woff文件就行,我们使用PythonfontTool库的ttLib包,代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from pathlib import Path
from fontTools.ttLib import TTFont
woff_path = Path(__file__).absolute().parent/"base64 (1).woff"
font = TTFont(woff_path)
font_names = font.getGlyphOrder()
font_str = [
    "8", "验", "杨", "女", "3", "届", "7", "男", "高", "赵", "6", "2", "下", "以", "技", "黄", "周",
    "4", "经", "专", "硕", "刘", "吴", "陈", "士", "E", "5", "中", "博", "1", "科", "大", "9", "本",
     "王", "B", "无", "李", "应", "生", "校", "A", "0", "张","M"
]
print(dict(zip(font_names[2:],font_str)))

我们解析woff文件得到一定顺序的编码集再结合在FontCreator中的字符集得到字符编码字典,在我们解析HTML源码的时候替换就行了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{'uniE032': '8', 'uniE200': '验', 'uniE267': '杨', 'uniE2DF': '女', 'uniE34E': '3', 'uniE39C': '届',
'uniE42A': '7', 'uniE481': '男', 'uniE51F': '高', 'uniE555': '赵
', 'uniE595': '6', 'uniE608': '2', 'uniE6CD': '', 'uniE72D': '', 'uniE7C1': '', 'uniE7C6': '黄',
'uniE7D3': '周', 'uniE841': '4', 'uniE84B': '经', 'uniE8A4': '专', 'uniE8E6': '硕', 'uniE8F4': '刘',
'uniE906': '吴', 'uniE9CF': '陈', 'uniEA8F': '士', 'uniEB2C': 'E', 'uniEBBA': '5', 'uniEBE2': '中', 'uniED0E': '博',
 'uniEF3E': '1', 'uniF003': '科', 'uniF012': '大', 'uniF01A': '9', 'uniF02F': '本',
'uniF0D7': '王', 'uniF160': 'B', 'uniF180': '无', 'uniF205': '李', 'uniF2A0': '应', 'uniF3B5': '生', 'uniF501': '校',
 'uniF6E9': 'A', 'uniF71C': '0', 'uniF76F': '张', 'uniF877': 'M'}

2. 阶段二:字体信息不换,动态更换字符编码

反爬方:既然写死的woff文件太容易让人解析,那就每次都更换新的woff文件,woff文件不更换字体信息,只更换字符编码,这样,每次的字符编码都不一样,解析的时候就不能使用同一套字符编码字典去解析了。

应对方:每次同一字符的编码都不一样的情况是什么样呢?可以看看下面两个图所示

我们连续两次请求的同一个字符却有不同的编码,换个思路,同一个的字符它们的字体的关键点的坐标是不变的,就像我们在FontCreator点开某个字符看的的一样

为了得到每个字的坐标点参数,我们需要把woff文件转换成xml文件

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from pathlib import Path
from fontTools.ttLib import TTFont
font1_path = Path(__file__).absolute().parent/"font_1.xml"
font2_path = Path(__file__).absolute().parent/"font_2.xml"
woff1_path = Path(__file__).absolute().parent/"base64 (1).woff"
woff2_path = Path(__file__).absolute().parent/"base64 (2).woff"
font_1 = TTFont(woff1_path)
font_2 = TTFont(woff2_path)
font_1.saveXML(font1_path)
font_2.saveXML(font2_path)

得到文件是这样的

我们根据刚才生字的两个不同编码寻找,得到下面这两个结构

我们可以看到,虽然这两个字符的坐标不一样,但是从旧字符根据一定的偏移量可以得到新字符,所以我们破解这一代字体反爬的手段可以是把最先的字符和字符的坐标保留下来,之后请求得到的字符和字符坐标,根据一定量的偏移去匹配是否是同一个字,类似这样

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from pathlib import Path
from fontTools.ttLib import TTFont
woff1_path = Path(__file__).absolute().parent/"base64 (1).woff"
woff2_path = Path(__file__).absolute().parent/"base64 (2).woff"
font_1 = TTFont(woff1_path)
font_2 = TTFont(woff2_path)
font_old_order = font_1.getGlyphOrder()[2:]
font_new_order = font_2.getGlyphOrder()[2:]


def get_font_flags(font_glyphorder, font_ttf):
    f = {}
    for i in font_glyphorder:
        flags = font_ttf['glyf'][i]
        if "flags" in flags.__dict__:
            f[tuple(list(flags.flags))] = i
    return f


def comp(arr1, arr2):
    if len(arr1) != len(arr2):
        return 0
    for i in range(len(arr2)):
        if arr1[i] != arr2[i]:
            return 0
    return 1


def get_old_new_mapping():
    old, new = get_font_flags(font_glyphorder=font_old_order, font_ttf=font_1), get_font_flags(
        font_glyphorder=font_new_order, font_ttf=font_2)
    result_dict = {}
    for key1, value1 in old.items():
        for key2, value2 in new.items():
            if comp(key1, key2):
                result_dict[value1] = value2
    return result_dict


print(get_old_new_mapping())

我们会得到新旧两个字符的映射

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{'uniE032': 'uniF889', 'uniE595': 'uniEB52', 'uniF01A': 'uniF07A', 'uniF71C': 'uniEBDE'}
代码语言:javascript
代码运行次数:0
运行
复制

3. 阶段三:有了动态的编码,再搞个动态字体坐标?

代码语言:javascript
代码运行次数:0
运行
复制

反爬方:动态更换字符编码集也能根据字体坐标来破解,要是新旧两个字符的坐标不是按照一定的偏移量来做的呢?例如我们新的字符和旧的字符的字体不一样,新的字体做了一定量的变形,导致某些坐标的缺少以及坐标的偏移量不一致,所以可以做几百套不同字体坐标,不同字符编码的动态字体集(真的变态!)。

应对方:这一阶段的反爬看到过很多大佬的实现:

  • 有使用阈值来做的,不过阈值是写死的,也就是说明成功其实有点靠运气,有时候返回的两个字符坐标差值在阈值内,有时不在,所以这一个方案有点不太靠谱。
  • 有使用ocr来做的,哈哈,真的是秀,利用ocr来做的原理就是先利用坐标勾勒出汉字图样,接着识别出汉字,再把相同汉字的不同编码做对应,这样也能得出结果,效果没有具体去测算,不过使用ocr来识别汉字应该相对于tfpytorch等来说效果会差点。
  • 之前看到大壮哥使用KNN来做,是个好想法,而且也不用去识别图片成汉字,资源消耗和速率上相对来说会小点,原理就是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。放在字体这个例子中,就是新字体文件中哪个字符离旧字体文件中的某个字符距离较近,它就属于这个字符的类别,也就是和这个字符是一样的。

4. 阶段四:展望未来。。。

目前还没有新的字体反爬的手段出现,更多的@font-face的加密上面,比如对字体文件的地址做基本的Js加密等等什么的,其他的我就暂时没发现,有发现的大佬可以透露一下。

整个字体反爬的演变历程就是上面介绍的这样,下面我们开始做实战分析。

猫眼实战分析

猫眼国内票房榜地址

https://maoyan.com/board/1

猫眼字体反爬分析

首先我们进入页面,查看哪个部分的数据被替换

我们可以看到票房数据被替换了,是被stonefont这个@font-face的名称给替换了,我们去搜索这个stonefont

我们通过这个woff地址去下载woff文件,在利用FontCreator打开,看到这样

和我们之前看到的字体反爬方式是一样的,动态字体文件,以及动态的字体坐标,接下来,我们用KNN的思路去破解它。

猫眼字体KNN思路分析

KNN算法比较简单,我们划分测试集和训练集,先用测试集得到每个数字的距离,然后在测试的时候我们对不同的输入(也就是数字)就是距离计算,距离最近的即为相同值。

代码实战

PS: 关于历史分析的项目都可以在Review_Reverse项目下的maoyan目录看到

使用方法,输入以下代码即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
python -m maoyan.xxxx

我们这次使用Sklearn来做KNN

1. 收集猫眼的多套字体文件

这里我会获取10套字体文件(越多越好),然后将所有字符对应的字形坐标信息保存到一个列表当中(注意做好字符与字形坐标的对应关系)

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def get_font_content() -> str:
    response = requests.get(
        url=_brand_url,
        headers=_headers
    )
    woff_url = re.findall(r"url\('(.*?\.woff)'\)", response.text)[0]
    font_url = f"http:{woff_url}"
    return requests.get(font_url).content


def save_font() -> None:
    for i in range(5):
        font_content = get_font_content()
        with open(f'./fonts/{i+1}.woff', 'wb') as f:
            f.write(font_content)


def get_coor_info(font, cli):
    glyf_order = font.getGlyphOrder()[2:]
    info = list()
    for i, g in enumerate(glyf_order):
        coors = font['glyf'][g].coordinates
        coors = [_ for c in coors for _ in c]
        coors.insert(0, cli[i])
        info.append(coors)
    return info

FontCreator的字符补充上

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def get_font_data() -> List[List[List[int]]]:
    font_1 = TTFont('./fonts/1.woff')
    cli_1 = [6, 7, 4, 9, 1, 2, 5, 0, 3, 8]
    coor_info_1 = get_coor_info(font_1, cli_1)

    font_2 = TTFont('./fonts/2.woff')
    cli_2 = [1, 3, 2, 7, 6, 8, 9, 0, 4, 5]
    coor_info_2 = get_coor_info(font_2, cli_2)

    font_3 = TTFont('./fonts/3.woff')
    cli_3 = [5, 8, 3, 0, 6, 7, 9, 1, 2, 4]
    coor_info_3 = get_coor_info(font_3, cli_3)

    font_4 = TTFont('./fonts/4.woff')
    cli_4 = [9, 3, 4, 8, 7, 5, 2, 1, 6, 0]
    coor_info_4 = get_coor_info(font_4, cli_4)

    font_5 = TTFont('./fonts/5.woff')
    cli_5 = [1, 5, 8, 0, 7, 9, 6, 3, 2, 4]
    coor_info_5 = get_coor_info(font_5, cli_5)

    infos = coor_info_1 + coor_info_2 + coor_info_3 + coor_info_4 + coor_info_5
    return infos

2. 使用knn算法训练数据

通常情况下,拿到样本数据,先进行缺失值处理,然后取出特征值和目标值,再对样本数据进行分割,分为训练集和测试集,然后再对样本数据进行标准化处理,最后进行训练预测。由于采集的字体数据不多,如果按随机分割的方式,训练集容易缺失某些字符,导致预测测试集的结果误差率较大,所以在此固定前40个样本为训练集,最后10个样本为测试集合。另外,多次测试发现,此处进行标准化,会影响成功率,所以不采用,另外k值取1, 也就是说,我判定当前样本跟离它最近的那个样本属于同一类型,即同一个字符,这个值取多少合适经过调试才知道,最后预测10个样本,包含了0-9 10个字符,成功率为100%。

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import numpy as np
import pandas as pd
from maoyan.font import get_font_data
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler


def main() -> None:
    # 处理缺失值
    imputer = SimpleImputer(missing_values=np.nan, strategy='mean')
    data = pd.DataFrame(imputer.fit_transform(pd.DataFrame(get_font_data())))
    # 取出特征值\目标值
    x = data.drop([0], axis=1)
    y = data[0]
    # 分割数据集
    # x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=0)
    x_train = x.head(30)
    y_train = y.head(30)
    x_test = x.tail(10)
    y_test = y.tail(10)
    # 标准化
    # std = StandardScaler()
    # x_train = std.fit_transform(x_train)
    # x_test = std.transform(x_test)
    # 进行算法流程
    knn = KNeighborsClassifier(n_neighbors=1)
    # 开始训练
    knn.fit(x_train, y_train)
    # 预测结果
    y_predict = knn.predict(x_test)
    print(y)
    # 得出准确率
    print(knn.score(x_test, y_test))
代码语言:javascript
代码运行次数:0
运行
复制

3. 得到训练好的流程之后我们进行测试

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def get_board() -> None:
    map_dict = get_map(
        text=requests.get(
            url=_board_url,
            headers=_headers
        ).text
    )
    for uni in map_dict.keys():
        text = text.replace(uni, map_dict[uni])
    html = etree.HTML(text)
    dd_li = html.xpath('//dl[@class="board-wrapper"]/dd')
    for dd in dd_li:
        p_li = dd.xpath(
            './div[@class="board-item-main"]//div[@class="movie-item-info"]/p')
        title = p_li[0].xpath('./a/@title')[0]
        star = p_li[1].xpath('./text()')[0]
        releasetime = p_li[2].xpath('./text()')[0]
        p_li = dd.xpath(
            './div[@class="board-item-main"]//div[@class="movie-item-number boxoffice"]/p')
        realtime_stont = ''.join(
            list(map(lambda x: x.strip(), p_li[0].xpath('.//text()'))))
        total_stont = ''.join(
            list(map(lambda x: x.strip(), p_li[1].xpath('.//text()'))))
        print(title)
        print(star)
        print(releasetime)
        print(realtime_stont)
        print(total_stont)
        print('-' * 50)


get_board()

把训练好的结果和官网对比一下,是不是感觉美滋滋,连最新的字体反爬也被我们破解啦!

复习要点

  • 重新梳理下字体反爬的整个演变历程
  • 对于最新的字体反爬转化思路,从识别图片到分类算法,提高效率

号主介绍

多年反爬虫破解经验,AKA“逆向小学生”,沉迷数据分析和黑客增长不能自拔,虚名有CSDN博客专家和华为云享专家。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云爬虫技术研究笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
【漆学军】EA编程速成教程(3)设置EA属性
大家好,我是漆天编程团队的漆学军,也是MT4专家论坛的创办者,下面是我为大家准备的《EA编程速成教程》第三课。
漆学军
2019/12/27
1.8K0
【漆学军】EA编程速成教程(3)设置EA属性
【漆学军】EA编程速成教程(2)第一个程序:下单
大家好,我是漆天编程团队的漆学军,也是MT4专家论坛的创办者,下面是我为大家准备的《EA编程速成教程》第二课。
漆学军
2019/12/27
1.9K0
【漆学军】EA编程速成教程(2)第一个程序:下单
【漆学军】EA编程速成教程(4)修改止损止盈
大家好,我是漆天编程团队的漆学军,也是MT4专家论坛的创办者,下面是我为大家准备的《EA编程速成教程》第四课。
漆学军
2020/04/16
2.2K4
【漆学军】EA编程速成教程(4)修改止损止盈
MT4行情交易API接口开发手记
1、用C++编写一个动态库文件,在里面实现行情和交易数据调用接口,将报价数据和K线数据写入数据库中,并从数据库中获取外汇量化系统发出的交易指令。 2、在MT4中编写EA文件,在MT4上不间断运行,从MT4平台实时获取报价和K线数据,并调用动态库写入数据库中,于此同时,不断从数据库中获取交易指令,再调用MT4的交易指令完成交易。 采用此种方法的好处就是兼容性强,只要打开MT4软件运行EA,就可以完成行情和交易接口的获取,也不用管是哪个外汇平台,即使MT4软件升级了也能继续用。缺点就是必须打开一个MT4软件专门获取行情和报价数据,同时每个交易的账户也必须要运行一个MT4软件,比如有10个外汇账户,就必须运行10个MT4软件。交易账户不多的话,运行速度和各方面指标也尚可接受,周末都不用重启或关闭,基本上实现7X24小时不间断运行。 一晃自己的量化系统就运行了几年了,中间也不断进行各种优化,但随着交易账户的不断增加,对软硬件的考验就越来越高了,一台普通的服务器,同时运行10多个账户就感觉有点吃力了,毕竟MT4本身就是一个大型的行情和交易软件,要占用不少软硬件资源,还要加上数据库服务器,现在感觉3、4台服务器都不够用了,网络带宽也开始吃紧,已经到了非改不可的时候了。 对于MT4行情和交易的API接口,自己一直都有耳闻,据说这种API接口,可以直接连接MT4行情和交易服务器,而且可以不用管是哪家外汇平台,只要该平台支持MT4软件即可使用。现在市面上很多跟单系统和跟单平台,就是通过该API接口来实现跟单服务的,但感觉这种API接口应该不是MT4软件开发商推出的,属于第三方软件,甚至有可能就是通过对MT4软件进行逆向分析提取出来的东西,一旦MT4软件升级了,就有可能导致API接口失效。记得以前网上就有通达信的行情和交易接口,可以获取国内A股行情并实现交易,自己当时还付费买了一套回来并使用了一段时间,据说也是逆向分析通达信系统得来的,但用了一段时间后,随着通达信软件和券商后台系统的升级,就无法使用了。 去年初的时候,自己就获取了一套MT4行情和交易接口及相关调用资料,但一直未去深入研究,因为该接口就仅仅是一个DLL文件,需要在Window 的.Net 平台下用C#开发和调用,自己对C#并不熟悉,这种托管DLL用其它的开发语言也不好调用,最主要是当时的重心和精力都放在量化系统和缠论策略的开发和优化上,对这种可有可无非要不可的东西实在无暇兼顾。但想着以后随着账户的不断增加,这种API调用接口肯定要用到,毕竟同时打开几十个MT4软件来实现交易接口太费资源了!自己也曾想到花点钱请别人开发,但想着要和自己的量化系统深度融合在一起,沟通和开发起来也挺麻烦,再加上自己本身就是程序员出身,还是适当的时候自己开发吧!从那时起,闲暇时间自己翻看一下C#的编程书籍,了解一下C#的语言和用法,先为以后的使用打点基础。 上周,将自己几个要完成的开发工作按重要性和紧急性排列出来各种比较后,终于决定将MT4API接口的开发提上日程了,说干就干,在电脑上安装好VS2019后,这个星期就忙着搞开发了。整个接口的需求和流程其实自己已经非常清晰,唯一不足的地方是对VS2019和C#还不熟悉,但开发语言都是相通的,不懂不会的地方就查查书,或者百度及CSDN上搜索一下就好了。 花了两天时间,完成了大致的软件界面,并实现了行情和交易接口的简单调用,成功返回了想要的各项数据,开发工作挺顺利,各项功能正慢慢实现。自己是用真实的交易账户来测试的,想着这样频繁的测试,不断登录和退出,途中还会有不少出错和非法调用,会不会引起外汇平台的警觉,如果把自己的账户封禁掉,那可就麻烦大了,因此马上申请了个模拟账户来测试,结果悲剧了,接口竟然无法登录了,返回Old Version,看来平台的模拟账户后台服务器已经升级了,不再支持这个接口,而真实账户的后台服务器,可能考虑到兼容性的缘故,还没有进行更新,或者还兼容这个接口版本,因此还能使用。记得去年底有一段时间,听说很多跟单系统或跟单平台都无法使用了,就因为MT4软件商强制升级了一次,有的MT4后台服务器已经不再支持这个接口了。想着这样下去也不是办法,因此又开始想办法去找这个接口的最新版本,皇天不负有心人,仅半天时间就找到了一个新的API接口版本,不过这个接口有一点点限制。在这里不得不鄙视一下C#,像C#,JAVA这种开发语言开发出来的托管代码,真的很容易被反编译,简直就和真正的源码看起来没有什么差别,因此很快就被我把限制解除了。 正好这两个星期新冠疫情吃紧,有的小区还被封了,羽毛球馆也不让打球了,因此整个星期几乎没有出门过,就窝在家里辛辛苦苦搞开发了,老骥伏枥,像我这种老程序员了,想不到开发效率还挺高,到了今天周五,就把整套接口完成了。现在回想过来,难点上除了本身对C#进行各种熟悉外,怎么优化速度和算法也花了不少时间,这里就通过缓冲区来实
全栈程序员站长
2022/09/02
5.2K3
MT4行情交易API接口开发手记
python控制mt4自动交易软件下载_MT4 EA智能自动交易系统使用教程[通俗易懂]
1.首先,你要有一个EA,必须要有以ex4为扩展名的,如果只有mq4文件的话,就要用MetaTrader自带的编辑器MetaEditor打开,将mq4通过编译(compile)并且要不出现错误,才能在原存放mq4的文件夹下面得到一个同名的ex4文件。
全栈程序员站长
2022/09/02
1.4K0
妥妥的世界第一:为什么MT4软件的地位无法撼动?
我是漆学军,2004年接触MT3.83的时候,还没有太在意,直到2005年,MT4正式推出之后,我就再也没有真正爱上别的软件,即便是MT4的升级版本MT5,我也一直提不起太大兴趣。
全栈程序员站长
2022/09/02
1.2K0
【漆学军】分享我自用多年的马丁策略EA完整源码(winkey),曾半年将我账户翻3倍
马丁策略一直都是具有很大争议的,因为有太多的人使用这个策略爆仓了,但是依然有人使用这个赚钱了。那些使用这个策略爆仓了的人会认为马丁策略不行,是垃圾,我是不同意这种说法的。
漆学军
2021/06/07
4.3K4
【漆学军】分享我自用多年的马丁策略EA完整源码(winkey),曾半年将我账户翻3倍
mt4编程语言 c语言,MT4编程之MQL4编程语言介绍[通俗易懂]
MetaQuotes Language 4 (MQL4) 是一种新的内置型程序用来编写交易策略。 这种语言可以创建你自己的智能交易,使自己的交易策略能够完全自动地执行。而且,MQL4还能自定义客户指标,脚本和数据库。
全栈程序员站长
2022/08/28
6.3K0
【零基础】MT4量化入门一:跑一个简单的boll
  今天开始研究MT4了,MT4是大大有名的外汇交易和量化软件,使用一种叫做MQL的语言来开发量化程序(跟C比较像)。因为是外国人做的,用的也大部分是外国人,使用起来不是很顺手,跟极星各有优劣吧。这里我就先逐步讲一下MT4的使用,然后再简单跑一个boll指标,最后汇总下使用心得。
全栈程序员站长
2022/08/31
1.8K0
【零基础】MT4量化入门一:跑一个简单的boll
五步学会任何编程语言
有些人喜欢学习新的编程语言,也有一些人觉得学习一种都是可望不可及的事情。在本文中,我将向你展示如何像程序员一样思考,这样你就可以自信地学习任何一门你想要学习的编程语言。
用户8639654
2021/09/13
4580
各路大牛的C语言编程建议和技巧,看完感触颇深
我们鼓励在编程时应有清晰的哲学思维,而不是给予硬性规则。我并不希望你们能认可所有的东西,因为它们只是观点,观点会随着时间的变化而变化。可是,如果不是直到现在把它们写在纸上,长久以来这些基于许多经验的观点一直积累在我的头脑中。因此希望这些观点能帮助你们,了解如何规划一个程序的细节。(我还没有看到过一篇讲关于如何规划整个事情的好文章,不过这部分可以是课程的一部分)要是能发现它们的特质,那很好;要是不认同的话,那也很好。但如果能启发你们思考为什么不认同,那样就更好了。在任何情况下,都不应该照搬我所说的方式进行编程;要用你认为最好的编程方式来尝试完成程序。请一以贯之而且毫不留情的这么做。
C语言中文社区
2022/05/30
8450
各路大牛的C语言编程建议和技巧,看完感触颇深
要成为年薪百万的技术大牛必经历这5个阶段, 收好这份超实用的技术进阶指南 | 技术头条
2018年的那股区块链热潮也着实让区块链开发人员火了一把,他们(她们)拿着几十万,甚至是上百万的年薪,头顶着耀眼的光环,成了大家心目中的男神。
区块链大本营
2019/04/28
5160
要成为年薪百万的技术大牛必经历这5个阶段, 收好这份超实用的技术进阶指南 | 技术头条
悟透一个小窍门,你就能举一反三掌握所有编程语言
当你程序员当久了,你会慢慢了解到,许多真 . 大佬都是编程语言通。他们在彻底掌握了一门语言之后,就能够很自信的觉得自己能够轻松掌握别的语言。这种能力可把许多人馋哭了,到底是怎么回事?为什么别人能够轻松驾驭所有语言,而我不行?
用户6901603
2024/02/22
1470
悟透一个小窍门,你就能举一反三掌握所有编程语言
程序员到底如何成为编程高手或以此创业?
每个IT企业,尤其是初创企业,非常苦恼:找不到好的程序员。现在大学、软件学院及各种培训机构,每年培养几十万的程序员,毕业的每个人都有同样困恼:找不到好的工作。问题出在哪里呢?
Spark学习技巧
2018/08/01
3450
程序员到底如何成为编程高手或以此创业?
零基础转行学习python是否还在纠结?
Python编程语言由于自身具有的“清晰”、“简略”等特点而受到众多使用Python编程语言的IT从业者喜爱。而且,对于初学者来说,比起其他编程语言,Python 更容易上手。加上很多企业都使用Python编程语言,促进了Python程序员的市场需求量增加。
程序员鑫港
2022/01/06
3800
系统编程语言Rust特点介绍(1)
最近在学习Rust语言,感觉这门语言有点意思,因此写一篇文章分享。我不会去介绍Rust的基本语法,什么变量声明,if..else..,循环等等。这些东西,文档介绍的很清楚,何必多此一举。本文主要介绍Rust这门语言的特点,和其它语言的对比。
glinuxer
2019/09/28
1.6K0
系统编程语言Rust特点介绍(1)
【董飞】谷歌研究主管论人工智能以及程序员自我修养
【新智元导读】谷歌研究主管Peter Norvig在文中介绍了他对人工智能、个人计算和地图服务诸多领域的看法。文中提供了Peter写了大量AI笔记的个人网站和公开课视频地址。Peter Norvig 并不相信计算机能力的增强会把我们带到奇点。他还认为,至少要花上10年或10000小时才可能学好编程。 董飞:Peter Norvig任职于Google,其职位是研究主管(Director of Research). Peter Norvig是享誉世界的计算机科学家和人工智能专家。他是 AAAI 和 ACM
新智元
2018/03/22
6260
【董飞】谷歌研究主管论人工智能以及程序员自我修养
Android:知道类加载的过程面试还是卡壳?干货总结,一网打尽“类”的基础知识!
之前说了类加载的过程,但是有的读者表示还是有些面试题还是答不来,所以今天就来总结下类加载、对象实例化方面的知识点/面试题,帮助大家加深印象。
Android技术干货分享
2021/03/19
4160
Android:知道类加载的过程面试还是卡壳?干货总结,一网打尽“类”的基础知识!
Java编程基础知识点和技术点归纳
Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。
用户5814672
2019/07/12
5300
如何高效学习一门编程语言
首先这篇文章是建立在有一些编程基础之上来展开的,做为一种效率学习编程语言的自我总结输出。把编程语言当做一个工具,而这些不同种类的工具有很多的共通之处,抓住其中的关键之处可以大大提升学习效率,也是一篇自我总结的学习方法论,里面有的方法可能不适合我,但也会讲讲。如果要学习一门编程语言,先要问一下为什么要学?学会了能做什么?要达到什么样的目标?只有把这些问题想清楚了再去做,不然稀里糊涂不知所以,很可能半途而废。想明白了就要坚持去做,不要再东望望西看看,想、做、坚持三位一体是学到的基本三要素。
知一
2021/12/07
5470
如何高效学习一门编程语言
推荐阅读
相关推荐
【漆学军】EA编程速成教程(3)设置EA属性
更多 >
LV.0
上海君之维科技有限公司培训师
目录
  • “字体反爬” 我相信大多数从事爬虫工作的工程师都接触过,这其实不是一种常规的反爬手段,它其实是页面和前端字体文件想配合完成的一个反爬策略。像最早使用字体反爬的58同城、汽车之家到现在很多App的web页面也开始使用,例如美团、猫眼、快手抖音等等。随着爬虫工程师和反爬工程师的不断对抗,字体反爬从一开始的单纯依靠一个写死的字体文件升级成现在最新动态的字体文件,而字体反爬的攻克也有一个开始的解析字体文件做数据映射到现在依靠KNN来做动态映射,算是经历了一个又一个光辉的“升级阶段”,这篇文章就是简单讲述一下字体反爬的演变史以及最新依靠KNN来做动态映射的破解思路。
  • PS: 关于历史分析的项目都可以在Review_Reverse项目下的fonts目录看到
  • 使用方法,输入以下代码即可:
    • 反爬方:一开始的时候,字体反爬还没有发展的很成熟,所以大部分网站使用字体反爬的方式是使用固定的字体文件来做数据替换,固定的字体文件就表明每个数据的编码是写死的,不变的,那么每次网站引用这个woff文件之后,都可以用相同的编码来替换想要替换的数据,这就是最初的时候的字体反爬。
    • 应对方:既然他们的字体文件不变,那我们就直接解析他们的固定的woff文件就行,我们使用Python的fontTool库的ttLib包,代码如下:
    • 反爬方:既然写死的woff文件太容易让人解析,那就每次都更换新的woff文件,woff文件不更换字体信息,只更换字符编码,这样,每次的字符编码都不一样,解析的时候就不能使用同一套字符编码字典去解析了。
    • 应对方:每次同一字符的编码都不一样的情况是什么样呢?可以看看下面两个图所示
    • 我们连续两次请求的同一个字符却有不同的编码,换个思路,同一个的字符它们的字体的关键点的坐标是不变的,就像我们在FontCreator点开某个字符看的的一样
    • 为了得到每个字的坐标点参数,我们需要把woff文件转换成xml文件
    • 目前还没有新的字体反爬的手段出现,更多的@font-face的加密上面,比如对字体文件的地址做基本的Js加密等等什么的,其他的我就暂时没发现,有发现的大佬可以透露一下。
  • https://maoyan.com/board/1
    • 首先我们进入页面,查看哪个部分的数据被替换
    • 我们可以看到票房数据被替换了,是被stonefont这个@font-face的名称给替换了,我们去搜索这个stonefont
    • 我们通过这个woff地址去下载woff文件,在利用FontCreator打开,看到这样
    • 和我们之前看到的字体反爬方式是一样的,动态字体文件,以及动态的字体坐标,接下来,我们用KNN的思路去破解它。
    • KNN算法比较简单,我们划分测试集和训练集,先用测试集得到每个数字的距离,然后在测试的时候我们对不同的输入(也就是数字)就是距离计算,距离最近的即为相同值。
  • PS: 关于历史分析的项目都可以在Review_Reverse项目下的maoyan目录看到
  • 使用方法,输入以下代码即可:
    • 这里我会获取10套字体文件(越多越好),然后将所有字符对应的字形坐标信息保存到一个列表当中(注意做好字符与字形坐标的对应关系)
    • 通常情况下,拿到样本数据,先进行缺失值处理,然后取出特征值和目标值,再对样本数据进行分割,分为训练集和测试集,然后再对样本数据进行标准化处理,最后进行训练预测。由于采集的字体数据不多,如果按随机分割的方式,训练集容易缺失某些字符,导致预测测试集的结果误差率较大,所以在此固定前40个样本为训练集,最后10个样本为测试集合。另外,多次测试发现,此处进行标准化,会影响成功率,所以不采用,另外k值取1, 也就是说,我判定当前样本跟离它最近的那个样本属于同一类型,即同一个字符,这个值取多少合适经过调试才知道,最后预测10个样本,包含了0-9 10个字符,成功率为100%。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档