学了python不知干啥?爬爬虫!(6)爱词霸翻译(内容详尽,从打开网页手把手完成JS逆向并写出代码)
合理爬取,不恶意扩大站点压力
本文章仅作示例,请勿用作非法用途
该系列的其他篇目:
系列文章合集目录
本篇目录
- 前言
- 分析
- 网络加载过程
- JS源码分析
- 寻找具体算法
- 代码编写
- 完整代码
前言
前几篇教程的爬取,我们一直局限于静态网站,且请求仅限于get。但在实际的开发过程中,动态内容才往往是爬取的核心。在本节内容中,我将带你一步步分析爱词霸的翻译结果获取过程,并伪装请求实现单词翻译。
本篇内容为本人原创,转载请注明!
请注意,本篇内容仅限于学习交流,切勿用于商业用途!

分析
网络加载过程
首先打开爱词霸,并使用 F12 打开开发者工具 【此处使用浏览器为Edge,如使用其他浏览器请参照对应教程打开此界面】 。
为了动态分析翻译过程,我们需要切换到 网络(Network) 标签页

输入任意单词,点击翻译按钮,你将会看到如下请求结果:

这就是动态翻译的请求。在此条请求上右键-复制链接地址

得到如下结果
https://ifanyi.iciba.com/index.php?c=trans&m=fy&client=6&auth_user=key_ciba&sign=0020c1fc11e96d3a
目前的链接有许多参数尚未明确,我们先留着,之后再做分析。
切换到预览标签页并展开结果,我们惊喜地发现,翻译结果就藏在对应的json中。


目标就是模拟这个请求,那么下一步我们需要弄清楚这里面的参数究竟各代表什么。
切换到标头页,在这里我们可以查看详细的请求头

可以看到,除了明显的get请求外,该请求还携带有表单参数。分析字段不难发现,post的表单带有此次翻译的单词信息。其中,from和to分别是源语言和目标语言,q即为翻译的单词。
我们需要弄清楚其他参数是什么意思。此处采用对比的方法。
再次翻译另一个单词,得到第二组请求地址和请求头
https://ifanyi.iciba.com/index.php?c=trans&m=fy&client=6&auth_user=key_ciba&sign=09467500f66fb4a7

上下对比可知,两次请求的url中,仅有sign参数发生改变。可以猜测正是此参数起到加密作用。下一步我们就需要搞清楚,这个参数是怎么来的。
我们进入重头戏:源码分析!
JS源码分析
现在我们的目标是找sign,那么最直白的思路就很显然了:看看源代码那些地方出现了"sign",那些就是比较可疑的地方。
切换到源代码标签,使用快捷键Ctrl+Shift+F或按下图找到全局搜索面板
先尝试一下直接搜索sign

得到了9个结果,先进入第一个排查。
使用“优质打印”格式化代码以便于后面的分析。

使用Ctrl+F打开页内搜索,尝试查找“sign”
但是结果很不理想,有相当大的一部分是关于assign的,不利于结果的分析。
我们大概推测,sign在代码执行过程中应该是某个对象的参数。因此改用"\.sign"重新搜索

逐个排查,发现这几个signature有较大概率是我们的目标。

是不是呢?验证一下就知道了。
在源代码左侧的行号旁单击鼠标打上断点,程序就会在运行到此处时暂停。

重新点击翻译按钮,得到运行结果

观察上图,从变量的值我们可以看到,在这行代码执行前就已经产生了sign

不难看出,变量r可能就是封装好的请求内容,而e则是对应链接。
那么我们的关注点就在变量e上,而变量e已经带有了对应的sign。于是我们从旁边的调用堆栈标签找到在断点执行位置的上一处,观察此处的代码和变量值



变量r的值引起了我们的注意,这一串字符非常符合刚刚看到的sign的特征。究竟是不是,我们让代码恢复执行,看看结果。

最终的请求url中的sign参数正是此处的r!
于是为r赋值的这一行代码就非常重要了,摘录如下:
takeResult: function(e) {var t = p.a.parse(e), r = c()("6key_cibaifanyicjbysdlove1".concat(t.q.replace(/(^\s*)|(\s*$)/g, ""))).toString().substring(0, 16);return g("/index.php?c=trans&m=fy&client=6&auth_user=key_ciba&sign=".concat(r), {baseURL: "//ifanyi.iciba.com",method: "post",headers: {"Content-Type": "application/x-www-form-urlencoded"},data: e})},
从这里的js代码可得知,r是调用c()函数得到的结果,传入的参数是一个固定字符串 “6key_cibaifanyicjbysdlove1” 再拼接上翻译的单词,之后调用 replace(/(^\s*)|(\s*$)/g, “”)) 的结果。下一步就是看看,这个c()究竟是个什么东西?
切换标签为控制台页,输入c()

发现它返回的是一个函数,点击上面的输出结果,可以跳转到对应的源代码位置

这是一个陌生的函数。进行到这一步,让我们回顾一下,我们输入的参数是明文,出来的却是英文字符组成的字符串。我们可以猜测,此处的函数作用就是对参数进行某种加密或哈希算法。
翻阅这个js文件,可以发现大量的位运算操作,这正是加密中常用的操作。因此我们可以就此猜测,这个文件就是一个库文件,功能就是实现常见的字符串加密算法。

到这里思路换了个方向。我们不妨暂时跳出分析代码的过程,看看JS中有没有这样的库存在。

搜索的结果几乎全部指向了CryptoJS这个第三方库,那我们去Github看看它的源代码

尝试搜索刚刚代码中出现的 _createHelper 字段。

在 core.js 下,我们发现了这一段:

与刚才的

一模一样!!!
寻找具体算法
简单搜索得知,CryptoJS提供了种类丰富的加密算法。要找到此处用的是哪一种就有很多办法。这里我们选择:黑箱理论。
黑箱理论,是指对特定的系统开展研究时,人们把系统作为一个看不透的黑色箱子,研究中不涉及系统内部的结构和相互关系,仅从其输入输出的特点了解该系统规律,用黑箱方法得到的对一个系统规律的认识。
寻找一个特定的输入:

在调试模式下选中生成参数的部分代码,就可以看到此次运行的结果

上面这一次的运行结果如下:

随便打开一个用于加密的网站,经过不断尝试,在尝试到MD5的时候,得到了一模一样的结果:

18dc7242de5f73cae3b299a6c8eba326
如此就确定了加密方式:MD5!
一切就绪,下一步自然就是编写代码啦!开干!
代码编写
上面分析得很到位了,每一个参数都给了详尽的解释,此处就不再赘述。参见代码和注释即可。
完整代码
"""爱词霸 单词翻译。仅用于学习与交流@copyright : FunnySaltyFish@date : 2021/04/17 20:38:45
"""
import requests
from hashlib import md5
import jsondef get_result(word,source="zh",to="en"):# 伪装请求头# 不知道为什么要有这一行的可以看 https://blog.csdn.net/qq_43596067/article/details/105889267headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.57"}# 计算sign参数sign = get_signature(word)[0:16]# 组装urlurl = f"https://ifanyi.iciba.com/index.php?c=trans&m=fy&client=6&auth_user=key_ciba&sign={sign}"# post表单 数据data = {"from":source,"to":to,"q":word}# 获得的json文本result = requests.post(url=url,headers=headers,data=data)return result.textdef get_signature(word:str):# 按js代码写出来的拼接字符串raw = "6key_cibaifanyicjbysdlove1"+word.replace(r"(^\s*)|(\s*$)","")return md5(raw.encode("utf-8")).hexdigest()def parse_json(text):# 简单解析获得翻译结果# 针对其他一些翻译结果 , 您可以自行修改result = json.loads(text)return result["content"]["out"]if __name__ == "__main__":word = "你好"raw = get_result(word)translation = parse_json(raw)print(f"【{word}】的翻译结果是【{translation}】")
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
