第一次发帖子,可能有做得不够好的地方,请各位大佬指点
主页:aHR0cHM6Ly9waWFvZmFuZy5tYW95YW4uY29tL2Rhc2hib2FyZA==
难点:
* Js参数逆向
* 字体反爬
#############################参数逆向###################################
首先通过浏览器F12抓包看到**接口**是:aHR0cHM6Ly9waWFvZmFuZy5tYW95YW4uY29tL2Rhc2hib2FyZC1hamF4
多次请求后发现,params里面的`timestamp`, `index`, `signKey`三个参数不断发生变化,即需要对这三个参数进行逆向,接下来分别观察这三个参数的值
1. timestamp:根据英文释义,是时间戳,而且数据是13位的
2. index和signKey:首先打下XHR断点,然后进行调试,,然后找堆栈,可以看到有个setTimeout用于刷新,所以从之后的eval函数里开始寻找,打上断点,开始单步调试
进入到这个函数之后可以看到这里有对uuid的赋值,打上断点,看看复制之后得到了什么
可以看到 i 通过了` _veri.getQueryKey`函数进行赋值,而且其中也有我们所需要的index和signKey元素,所以进一步跟进这个函数里
在这里我们终于看到我们想要的东西的,timestamp和index还有signKey都在这里有构造说明,只需要一一cv出来就好了,在nodejs环境下调试,缺啥补啥即可,最终代码是
[JavaScript] 纯文本查看 复制代码window = this
window.btoa = function (ms){return Buffer.from(ms).toString('base64')}
CryptoJS = require('crypto-js')
_0x590248 = _0x4302
var o = _0x590248
var i = {
method: "GET",
timeStamp: +new Date,
"User-Agent": window.btoa("" + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'),
index: Math[o(250)](1e3 * Math.random() + 1),
channelId: 40009,
sVersion: 2,
key: o(260)
}
console.log(i)
function _0x2517() {
var e = ["keys", "log", "GET", "171WtxWGj", "floor", "6586595KrzIYt", "=''", "4INeORR", "reduce", "303256TgflDf", "1818384COzaid", "2216650lRBaPR", "error", "signKey", "A013F70DB97834C0A5492378BD76C53A", "userAgent", "replace", "11nRKPzJ","7tyVrdd", "method", "380474DWSxHC", "4045278HuYQWq", "5921330tHaNcD"];
return (_0x2517 = function() {
return e
}
)()
}
function _0x4302(e, t) {
var r = _0x2517();
return (_0x4302 = function(e, t) {
return e -= 246,
r[e]
}
)(e, t)
}
var d = Object[o(246)](i)[o(254)](function(e, t) {
var r = o;
return e = 0 === i[t] || i[t] ? e + "&" + t + "=" + i[t] : e + "&" + t + r(252)
}, "").slice(1)
function params() {
result = [i.index, CryptoJS.MD5(d.replace(/\s+/g, " ")).toString(), i.timeStamp]
return result
}
注意:
1. _0x2715的里的var e不知道为什么在浏览器环境执行的时候会自动偏移了4位,但在我的nodejs里面会不变,导致来了o(xxx)的结果会有所不同,所以顺序我进行了手动调整
2. result里面有三个参数,分别对应着index,signKey和timestamp,因为请求的时候这三个参数是一起构造的,所以我们也要在js文件里一起构造三个参数,而不是一个在python文件一个在js文件,这样子python文件在调用js文件的时候会有时间差,请求就会失败
3. 在构造signKey的时候可以看到使用了MD5算法,所以我就直接导入了一个crypto-js包来替代了,让代码可以简洁一点
三个参数全都已经被我们逆向构造出来了,接下来就可以去请求数据了
#############################字体反爬###################################
先上代码:
[Python] 纯文本查看 复制代码import io
import re
import jsonpath
import requests
import execjs
from faker import Faker
from fontTools.ttLib import TTFont
from PIL import ImageFont, ImageDraw, Image
import ddddocr
class MaoyanSpider(object):
def __init__(self):
faker = Faker()
self.url = 'aHR0cHM6Ly9waWFvZmFuZy5tYW95YW4uY29tL2Rhc2hib2FyZC1hamF4'
self.headers = {
"Accept": "application/json, text/plain, */*",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Pragma": "no-cache",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": faker.chrome(),
}
self.ocr = ddddocr.DdddOcr()
def get_pages(self):
with open('./maoyan(1).js', 'r', encoding='utf8') as f:
parse_js = f.read()
result = execjs.compile(parse_js).call('params')
index = result[0]
signKey = result[1]
_time = result[2]
params = {
"orderType": "0",
"uuid": "185df28337fc8-01871e640a76dc-26021151-144000-185df28337fc8",
"timeStamp": _time,
"User-Agent": "TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzExMS4wLjAuMCBTYWZhcmkvNTM3LjM2",
"index": index,
"channelId": "40009",
"sVersion": "2",
"signKey": signKey,
}
response = requests.get(self.url, headers=self.headers, params=params)
return response
def parse_font(self,response):
# 找到字体文件
txt = response.json()['fontStyle']
fontfile = 'https:' + re.search('opentype"\),url\("(//.*?\.woff)"', txt).group(1)
# 获取字体文件
resp = requests.get(fontfile)
with open('temp.woff', 'wb') as f:
f.write((resp.content))
ttfont = TTFont('temp.woff')
# 返回字形名称的列表,以其在文件中的顺序排序
fontlist = ttfont.getGlyphOrder()[2:]
# 实现字体的对应关系
charlist = [] # 定义了实际字体的列表
# 加载字体文件
font = ImageFont.truetype('temp.woff', 40)
# 将fontlist里的字体通过画图画出来,然后用ddddoc来识别
for uchar in fontlist:
unknown_char = f"\\u{uchar[3:]}".encode().decode("unicode_escape")
# 对字体坐标进行读取,并且转换为黑白,图像为RGB
im = Image.new(mode="RGB", size=(42, 40), color="white")
# 创建一个可以再给定图像上绘制的图像对象
draw = ImageDraw.Draw(im=im)
# text要绘制的文本,fill用于文本的颜色,font是一个ImageFont实例
draw.text(xy=(0, 0), text=unknown_char, fill=0, font=font)
# 二进制的文件,在内存中读写的bytes
img_byte = io.BytesIO()
im.save(img_byte, format="JPEG")
# dddddor进行图像的识别
charlist.append(self.ocr.classification(img_byte.getvalue()))
return charlist, fontlist
def show(self, response, charlist, fontlist):
moviename_list = jsonpath.jsonpath(response.json(), "$.movieList.data.list..movieInfo.movieName")
boxSplitUnit = jsonpath.jsonpath(response.json(), "$.movieList.data.list..boxSplitUnit.num")
# 映射完以后就可以进行字体的替换了
for j in range(len(moviename_list)):
rstr = ""
number = boxSplitUnit[j].split(';')
for i in number:
if i == "":
continue
tmp = "uni" + i.replace("&#x", "", 1).replace('.', '').upper()
for k in range(len(fontlist)):
if tmp == fontlist[k]:
if '.' in i:
rstr = rstr + '.' + charlist[k]
else:
rstr = rstr + charlist[k]
print(moviename_list[j], rstr)
if __name__ == "__main__":
spider = MaoyanSpider()
response = spider.get_pages()
charlist, fontlist = spider.parse_font(response)
spider.show(response, charlist, fontlist)
这里我分别构造了3个函数,get_pages()获得页面,parse_font()解析字体, show()展示
get_pages不用多说直接构造好headers,url,再引用js文件获取我们需要的参数即可
当我们获取到页面以后,我们可以看到有一些数据是乱码
这就涉及到了字体反爬,在parse_font部分,我们可以通过浏览器F12的font模块里看到字体文件,看到有很多,而且是随时间不断刷新的,那么很有可能是在我们原本的接口里会有说明用哪个字体文件
果然,我们通过全局搜索可以看到在接口的fontStyle包含了字体文件的url,我们就可以通过请求字体文件获得字形的名称以及顺序,我们需要一一将其与真实的字体对照起来,我用的方法是先读取字体文件,将里面的字体通过PIL库画图画出来然后用ddddocr进行图像识别。识别完以后就可以按顺序返回真实的字体列表了
最后,通过show()把乱码替换成真实字体并且打印出来,这样就成功展示我们需要的数据啦
成果:
请大佬们指点一二:lol, 你们的宝贵意见都是我进步的阶梯