百词斩数据之小析

       作为一位英语爱好者,百词斩是我每天都会用的一款APP,这款应用可以自测词汇量,并巩固你的单词量,确实是一款用心的产品。作为一名雅思7分的选手,个人觉得里面的发音和例句,对于口语还是有很大的帮助,可以边听边读,做到碎片化的学习。总言而之,推荐大家都体验一下。

       好吧,本人使用百词斩也是一年之久,时间久了总会发现一些不足之处,也会在它的官方渠道提出一些自己的需求和缺陷,而且该公司和我工作的地方都在一个园区,客服妹妹也很热情的邀请我去她们办公区坐坐,当然这都是客气话了。我个人最大的一个需求就是将收藏的单词能够导出来,这样可以打印,满足一些情景下的需要。

       好吧,恭维的话就到这里,画风开始急转,客气归客气,可是猴年都来了,提的需求还是没找落,千万不要轻视客户的需求啊,特别是一个程序员的需求。眼看马月就要来了,还是自己动手吧。

       本身百词斩提供离线数据包,而且是Android的应用,假如我能够获取单词的请求格式,同时能够解析每一个单词的音频,图片,例句,再能够解析各个数据库之间的结构关系。理论上讲,这样就能做出来一个PC版的百词斩,也就能满足我的个性化需求,而Android上也就只有sqlite数据库了,这意味着这些数据应该不难解析。初步分析,觉得应该是可行的吧,于是乎,我就开始了这趟邪恶之旅。

       首先就是要找到这些数据都存在什么位置了,我对Android系统不熟悉,或许是我眼拙,找了很久都没找到存放路径,就这几个文件夹,怎么就没有baicizhan这样让人眼前一亮的文件夹呢。仿佛当头一棒,看来敌人没这么蠢,靠人肉技术是不行滴。

       出师不利,只好另辟蹊径。百词斩提供离线数据包,如果可以监控手机的网络请求,那就能知道他下载的是什么内容了。抓取Android手机的HTTP请求,这就得靠Fiddler了。同时在Fiddler Options中开启代理,如下图。然后重启Fiddler。

       在手机端(保证是同一个网段),长摁WiFi信号源,修改网络,显示高级,代理设置为手动选择Fiddler所在电脑的无线IP,端口为8888,和Fiddler里面的端口号一致。

       一切就绪,点击下载单词报,yes,we get it!一条条的请求都在Fiddler中获取。请求消息如下图:

       如上图,不难猜测,zpk应该就是每一个单词的数据内容,原来单词是保存在文件里面而非数据库中,文件则按照一定的规则来命名。好吧,顺藤摸瓜,看看zpk里面到底是什么玩意。下载下来一个zpk,然后在beyond compare下面以16进制方式打开,上阕如下:

       好吧,你应该和我一样不想看下去了,唯一不同的是我能忍,继续看到中间,发现看到了里面的单词,音标,例句等ASCII码的内容,终于有点头绪了。再继续忍着看,下阕如下:

       可以看到,这算是里面的数据清单,包括zpk文件对应的数据和顺序,说明这个数据包括一个jpg,一个aac或者mp3的音频,其中的.是他们的分隔符,对应的是ASCII码的0X00。这样我们可以先解析这部分,就可以了解到该文件的详细内容,然后每一个文件都有自己的标示符,这样就可以把该二进制文件分解成对应的格式,一个zpk就这样迎刃而解。当然如上都只是猜测而已,还是需要验证,另外对比来看看是否有没有遗漏的字段。比如jpg文件的开头是FF D8的标识,结尾则是FF D9,我们手动把这部分的二进制字段截取出来,保存为jpg格式,果然不出所料。同样,里面还有png图片和aac的音频。都可以如此获取,最麻烦的是mp3,我对这个格式不熟悉,发现它没有固定的头尾标识,也算一个美中不足吧。

       至此,这个zpk基本上就被我们扒光,赤裸裸的呈现在我们的眼前,让我们肆意的享用了。不过,百词斩可是有六万多个单词(这是后话),如果每一个都得靠手(来截取二进制字段),手是会抽筋的。这时候,如果家里有一位程序员的话,那就写一个程序自动解析了。我写了一个zpkparser程序来实现对zpk的解析。对多个数据测试发现,一个zpk最多有四个文件,分别是jpg,png,aac和mp3,顺序不定,但和zpk结尾给出的顺序一致。分别是例句和单词的音频,单词示意图片和象形图片。只要记住每一个文件的头尾标识,然后按照zpk尾给出的顺序来解析单个二进制段,一切就大功告成。有图有真相。

       当然,我之前破解过Google Earth数据。相比而言,zpk文件并没有加密,也没有压缩,而且还是ASCII码,所以破解这种程度的数据并没有太多成就感。而且破解zpk文件并不能满足我的需要。我只有解析了zpk,才能知道这个zpk对应的是哪单词。而我需要根据我收藏的单词,获取对应的zpk文件。所以,还需要一个举一反三的过程。

继续努力,全局搜索下终于找到了百词斩在手机里面的存放路径。我认为在Android手机上,百词斩也对自己的数据存放位置做了一些隐藏,因为我用百词斩比较久了,早起的版本貌似就在存储卡下baicizhan文件夹,但发现后来他放到了很难找到的位置,在我的华为手机上对应在Android/data/com.jiongci.com这样一个目录下,也是比较隐藏的,里面的zpk文件夹肯定是所有单词的汇总了,把其他数据拷到电脑上,看看里面的逻辑关系。

       我的习惯,先按照大小排序,然后找到我想要分析的文件后,然后在按照格式排序,最后在看看里面是什么内容。首先最大的文件是baicizhantotal.db,这也太明显了。手机上只能是sqlite嘛。我们在sqliteman软件下打开这个数据库,果然不出所料,在tb_total_topic_resources表中,保存了所有单词的属性信息。音标,中英文示意,例句等,前面还有book_id,topic_id等索引信息。这和zpk里面对应的内容完全一样。我们在建立一个key value的映射上迈出了一小步。

       然后对所有db解析,建立各自关联,仿佛你在和百词斩的程序员在进行一次无声的交流,这是一个漫长,晦涩的过程,也是一个绞尽脑汁的过程。这里就不说过程了,直接说结果吧。

       bookID表,里面统计了不同科目下的id和单词数目等。可见,考研词汇有6k多个,其中bookid就是每一类的一个索引值。

       想看看雅思核心中的具体的单词统计,则打开对应的表,如下,topic就是该单词的唯一id,下面则是zpk的路径。

       我想要的只是收藏单词的导出功能,所以继续找,你会找到出错单词的统计,当然,还有收藏单词的数据表。如下,这下大家满意了吧。

       我也不清楚为什么这里的id怎么就不一样了呢?于是在Fiddler里面反复收藏单词,查看请求,该功能必须要求在联网环境下,估计是避免版本管理问题。然后找到两个已知id的单词,收藏后对比,这里面是是id+N这样一个固定格式,具体N是多少就不说了,于是乎,把id取出来,减掉N,你就可以获取到收藏单词的id,根据id就可以获取该单词的存放路径,在通过zpkparser就可以解析。这样就能满足我的要求了。

       当然,人的欲望是无止境的,现在,我已经不满足于收藏的单词了,何不把所有的单词都搞下来,这也算一份不错的英文单词数据了,毕竟数据到哪里都是最核心的。好吧,于是就有了下面的这个截图,百词斩大部分的单词都在其中,每个数字对应一个bookid,包括新概念,囧记,以及雅思托福考研等主要内容。

       前前后后用了两天,也算基本搞清楚了百词斩单词数据部分的相关细节。个人有两个感想,第一,看上去数据解析很简单明了,这是在知道的情况下,其实在破解过程中并不如是,就好比让你蒙眼走路,即使再熟悉的一条路也是有一定的挑战,你不得不凭借你的其他感官,综合判断方向,还要不断的尝试并忍受无劳的失败;另外一个,就是大多数公司对数据的重视程度不够,无论如何,数据都是应用的一个基石,没有任何的防范还是略显不足,尽管你以为二进制对人类而言是不可解析的。

       好了,写了这么多,无非是要显摆一下自己雅思7分而已,能懂我的人才是真爱~

时间: 2024-10-20 19:44:50

百词斩数据之小析的相关文章

动态加载页面数据的小工具 javascript + jQuery (持续更新)

使用该控件,可以根据url,参数,加载html记录模板(包含json参数对应,以及具体记录位置Index根据参数描述加载对应的属性,并可以根据简单的判断分支加载对应html或者控件)至列表容器内(JQuery选择器字符串)注: 该控件在使用前需引入JQuery框架支持,使用该控件,可极大的减少Ajax列表数据动态加载开发工作的实际工作量. 使用方式: 首先,添加控件引用,并加入Jquery支持 <script src="js/jquery.js"></script&g

ASP.NET 异步Web API + jQuery Ajax 文件上传代码小析

该示例中实际上应用了 jquery ajax(web client) + async web api 双异步. jquery ajax post 1 $.ajax({ 2 type: "POST", 3 url: "/api/FileUpload", 4 contentType: false, 5 processData: false, 6 data: data, 7 success: function (results) { 8 ShowUploadControl

【iOS】正则表达式抓取网页数据制作小词典

应用程序不一定要自己去提供数据,有现成的数据学会去用才好. 网络很大,各种搜索引擎每天到处爬.本文通过正则表达式抓取网站的数据来做一个小词典. 一.正则表达式的使用 1. 确定匹配方案,即pattern 2. 用pattern实例化NSRegularExpression 3. 用匹配方法开始匹配. 匹配一次:可以使用firstMatch方法 匹配多次:可以用matchs方法 正则表达式对照表:(在网上找到了一个很不错的表,正则表达式各个语言通用) http://www.jb51.net/shou

Python之美[从菜鸟到高手]--NotImplemented小析

今天写代码时无意碰到NotImplemented,我一愣,难道是NotImplementedError的胞弟,所以稍微研究了一下. NotImplemented故名思议,就是"未实现",一般是用在一些比较算法中的,如class的__eq__,__lt__等,注意NotImplemented并不是异常,所以不能 使用raise,当没有实现时应该是return NotImplemented. 我们可以看看django中的Field的实现, @total_ordering class Fie

C语言判断系统数据大/小端存储方式

小端存储:数据的低位部分,存储于存储器的低地址空间里. 大端存储:数据的低位部分,存储于存储器的高地址空间里. 首先,一般PC数据存储方式是小端存储. 基本实现思想是:将存储器中所存的数据按字节以地址顺序输出,与存入数据的高低位进行比较,即得出结论. 实现方法一: 1 #include <stdio.h> 2 int main(void) 3 { 4 short int x; 5 char *arr; 6 7 x = 0x1122; 8 arr = (char *)&x; 9 10 i

使用Listener准备application作用域数据的小问题

在程序中,有些数据我们希望在程序启动的时候就准备好,并且只准备一次,放在application作用域中,这时候,我们通常会用Listener来准备这些数据.但是,用Listener准备application作用域的数据,在获取容器的时候会有一些小问题. public class InitListener implements ServletContextListener { //该Listener配置在web.xml里,默认通过反射生成实例,来得到这个对象实例来执行 //并没有从Spring容器

为数据挖掘小组写的一个用于造数据的小程序

最近有个数据挖掘的项目,要求在文本里面写入随机字母并且要1000W个 于是就写了个程序用来造数据并记录一下 程序写的时候遇到的问题 1 未考虑内存溢出的情况,大批量的把数据写入导致内存溢出 以后需要谨慎对待 目前完整版 package test; import java.io.File; import java.io.FileWriter; import java.io.IOException; import org.apache.commons.io.FileUtils; import org

Hadoop mapreduce 数据去重 数据排序小例子

数据去重: 数据去重,只是让出现的数据仅一次,所以在reduce阶段key作为输入,而对于values-in没有要求,即输入的key直接作为输出的key,并将value置空.具体步骤类似于wordcount: Tip:输入输出路径配置. import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop

Poco logger 日志使用小析

Poco logger 日志使用小析 Poco logger 日志使用小析 日志 logger 库选择 Pocologger 架构简析 步骤一 生成消息 步骤二 写入logger 步骤三 导入channel 步骤四 写文件 使用 h file cpp file main 入口函数 备注 拓展 总结 转载请注明本文链接 日志 在软件开发过程中,为了定位软件运行过程中可能出现的错误,一种常用的做法是在潜在的错误位置,设置防御代码,并且将错误代码执行后的错误信息记录下来,以供后续改进代码提供支持. 在