定位
打开百度翻译网站, 随便输入单词, 通过观察接口调用及返回, 很容易就能定位到目标接口.
查看该接口调用参数, from, to, query
三个参数的含义比较明显, 分别是 翻译源语言, 翻译目标语言, 待翻译文本
, 破解的关键应该在于 sign, token
两个参数.
查看Initator, 可以看到调用集中在 index, public
两个文件. 比较安心的是函数名未作混淆, 直接跳转至 doTrans
函数. 一番调试后发现该函数离接口调用还是有点远的. 既然接口参数名比较长且有含义, 接口调用也被限制在两个文件内, 那就直接 ctrl+f
大法吧.
搜索关键字 token
, 很快就能在 index
文件中定位至参数构造处.
token参数破解
token参数从全局变量 window
中取出, 想用正常方法定位其被设置的位置可谓困难之至, 不过有了 Chrome Devtools Protocol
, 想调试全局变量就简单了.
通过运行下面的代码便能获取到一个能调试 window.common
的浏览器, 在 window.common
被赋值时会进入断点.
from selenium import webdriver
# 初始化浏览器
options = webdriver.ChromeOptions()
options.add_experimental_option(
"excludeSwitches",
["enable-automation"]
)
driver = webdriver.Chrome(
executable_path="your driver path",
options=options
)
# 更改window.common
token_locator = """
Object.defineProperty(window, "common", {
set: (value) => {console.log(value);debugger;}
})
"""
driver.execute_cdp_cmd(
"Page.addScriptToEvaluateOnNewDocument",
{
"source": token_locator
}
)
# 让浏览器不因为python进程退出而消失
input("don't go")
打开上述浏览器的控制台, 打开百度翻译网站, 顺利进入断点. 回溯调用栈可以轻松定位出 window.common
被设置的地方, 百度很阴地把它放在了html文件中, 这要是按照常理在一堆js文件中寻找是不可能找到的.
不过等等, 这里 token
是个空字符串, 明显不对劲. 从断点处继续运行, 发现控制台报错, 获取 window.common
出错, 这是当然的, 因为之前cdp代码中只定义了其 get
方法.
当然, 可以在cdp代码中补全 window.common
的 get
方法, 并用同样的方法魔改 window.common.token
, 定位至其被修改的位置. 不过稍微细心点, 在html文件之后不远处就能发现其中玄机.
从这段贴心的注释可进行合理推测: 第一次进入页面没有cookie, 导致 token
为空字符串, 这时会刷新页面, 有了第一次请求的cookie, 就能够正常获取 token
了.
用一段代码验证下, 发现不带 User-Agent
请求会被拒, 看来百度还是会检测这玩意的.
import httpx
UA= (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
+ "(KHTML, like Gecko) Chrome/90.0.4430.95 Safari/537.36"
)
client = httpx.Client(headers={"User-Agent": UA})
url = "https://fanyi.baidu.com"
with open("first.html", "wb") as f:
f.write(client.get(url).content)
with open("second.html", "wb") as f:
f.write(client.get(url).content)
果不其然, 带上cookie的第二次请求就能正常获取到 token
了, 至此 token
参数破解完成.
sign参数破解
从之前找到的接口请求参数构造处可定位至 sign
参数构造处. sign
参数构造比较简单, 只涉及两个函数, 也没有什么技巧, 可以读懂js的逻辑后用python仿写, 也可也复制js代码后用相关python库运行. 后者可能还得配置node, 更为麻烦, 实用性也要打折扣.
sign
参数构造过程中用到了 window.gtk
这一变量, 当然可以通过cdp的方式调试, 但其实之前的html代码中该变量已经出现过了.
完整代码
待补充…