【爬虫实战】使用Python和JS逆向获取易车网汽车参数详情

前言

有的网站请求参数或者URL是明文的,但是有些是加密后的。接下来以易车网为例,巩固一下逆向思路。

一、目标分析

使用接口获取汽车的参数配置,但是请求参数无法直接阅读,点击解码即可。

请求头里还有一下加密参数

估计重点就是X-Sign,其他的可能用处不大。

其实可以试试直接请求接口,看是否有返回值:

 // https://mapi.yiche.com/web_api/car_model_api/api/v1/car/config_new_param?cid=508&param=%7B%22cityId%22%3A%22201%22%2C%22carId%22%3A%22166210%22%7D  {   "message": "公共参数缺失",   "status": "11036" }
所以重点来了,可能就是这个X-Sign起决定性因素。

二、逻辑分析

1. 定位函数获取X-Sign

只有两条记录,全部打上断点,进去哪个哪个就是。

看输出的值,有点像。

继续分析这句代码:

 "headers" == e.encryptType && (n["x-sign"] = u(e, t));
执行规则:
  1. 只要“&&”前面是false,无论“&&”后面是true还是false,结果都将返“&&”前面的值;

  2. 只要“&&”前面是true,无论“&&”后面是true还是false,结果都将返“&&”后面的值;

总结:假前真后

所以有效的的值一定是u(e, t)。

接下来分别看一下e和t这两个参数:

 // t {     "cid": "508",     "ver": "v10.80.0",     "timestamp": 1700192890484,     "gradeParam": {},     "uid": "",     "headerEncryptKeys": [         {             "name": "pc",             "value": "19DDD1FBDFF065D3A4DA777D2D7A81EC",             "cid": "508"         },         {             "name": "phone",             "value": "DB2560A6EBC65F37A0484295CD4EDD25",             "cid": "601"         },         {             "name": "h5",             "value": "745DFB2027E8418384A1F2EF1B54C9F5",             "cid": "601"         },         {             "name": "business_applet",             "value": "64A1071F6C3C3CC68DABBF5A90669C0A",             "cid": "601"         },         {             "name": "wechat",             "value": "AF23B0A6EBC65F37A0484395CE4EDD2K",             "cid": "601"         },         {             "name": "tencent",             "value": "1615A9BDB0374D16AE9EBB3BBEE5353C",             "cid": "750"         }     ],     "paramsKey": "f48aa2d0-31e0-42a6-a7a0-64ba148262f0" }
然后是e:

这不太对啊,接口地址不是这个,那就继续执行,当URL是config_new_param再追踪。

果然有执行了几次才拿到了我们想要的结果。

也就是说如果开始找出了URL,那么后面的一切都是南辕北辙。

接下来就跳转到了s函数:

至此,我们找到了X-Sign的位置,之后使用JS来补环境就可以了吗?不要着急,执行一下没准能得到意想不到的结果。

好像恰巧获取的cid和param的值,而X-Sign就是加密后的密文。

2. 使用代码实现X-Sign

刚才我们获取了

cid=508&param={"cityId":"201","carId":"166210"}19DDD1FBDFF065D3A4DA777D2D7A81EC1700201316661

这个值,把这个值传给h函数,并把相同作用域的函数全部复制过来就可以了。很顺利,一次就跑通了。

接下来思考一下如何获取这个参数变量。我们回到刚才的s函数,可以看到这个参数是由n提供的。

可以看到就是把n参数进行一个MD5的处理,这时候我们可以猜测一下是不是普通的MD5,可以用python或者js代码试试:

 import hashlib content = 'cid=508&param={"cityId":"201","carId":"166210"}19DDD1FBDFF065D3A4DA777D2D7A81EC1700211170720' md5_hash = hashlib.md5(content.encode()).hexdigest() print(md5_hash) # 输出 166cc7dd317a737794ecaa2bd795f37d
可以看到和结果中是一致的。这就简单很多了,大部分逻辑就分析完了。

3. 编写Python代码,进行调用

根据前面的分析,可以确定以下参数是需要传值的:

  • 查询参数 {“cityId”:“201”,“carId”:“166210”}

  • 时间戳,可以从Python传过去,也可以直接使用JS来生成

其他值可以是固定或者处理过的。

三、代码实现

全部代码如下,使用python将字符串进行MD5处理,然后发出请求。需要注意以下几个问题:

  1. 有些字段是必须存在的,比如X开头几个,因为前面的JS代码中可以看到有使用到,否则就会报错;

  2. python的JSON数据和JS的JSON是有区别的,需要单独处理一下,否则校验失败。

以下代码是请求一种车型的方式,如果是批量请求,提前准备好多个ID就可以了。

import json
import time
import hashlib
import requests


def start(city_data):
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/119.0",
        "Content-Type": "application/json;charset=UTF-8",
        "X-Platform": "pc",
    }
    ts = str(int(time.time() * 1000))
    new_data = json.dumps(city_data, separators=(',', ':'))
    s1 = f'cid=508&param={new_data}19DDD1FBDFF065D3A4DA777D2D7A81EC' + ts
    print(s1)
    md5_hash = hashlib.md5(s1.encode()).hexdigest()
    print(md5_hash)
    headers['Referer'] = 'https://car.yiche.com/biyadifsuv/m166210/peizhi/'
    headers['x-sign'] = md5_hash
    headers['x-timestamp'] = ts
    headers['x-city-id'] = "201"
    headers['cid'] = "508"
    params = {
        "cid": "508",
        "param": json.dumps(city_data, separators=(',', ':'))
    }
    print(params)
    # 注意要使用separators,否则会出现签名验证失败的问题,使用后间隔更小,符合网站中JS的要求
    # 'cid=508&param={"cityId":"201","carId":"166210"}19DDD1FBDFF065D3A4DA777D2D7A81EC1700211170720',
    # 否则会出现后面的两种情况
    # {'cid': '508', 'param': '{"cityId": "201", "carId": "166210"}'}
    # {'cid': '508', 'param': '{"cityId":"201","carId":"166210"}'}

    res = requests.get(
        'https://mapi.yiche.com/web_api/car_model_api/api/v1/car/config_new_param', params=params,
        headers=headers,
    )
    print(res.json())


if __name__ == '__main__':
    city_data = {
        "cityId": "201", "carId": "166210"
    }
    start(city_data)

返回结果:

四、总结回顾

今天分析JS逻辑的时候在md5这一块卡了很久,主要原因如下;

  1. 起初看到进行MD5处理的时候怀疑了一下是不是基础的MD5,有没有进行特殊处理。为了图省事,直接问的GPT。结果这家伙输出了和控制台不一致的值,所以就当成JS特殊处理了。因此就把相关的函数单独拉出来进行处理,生成Sign值后传给python。但最后一直校验失败,百思不得其解。后来又多次反复查看使用Python生成MD5值后才发现了GPT的问题。但还是很奇怪为啥JS生成的MD5值不对呢,难到少复制什么函数或者变量了?

  2. 拼接参数的时候出现了Python和JS两个版本JSON不一致的问题,就是空格。

  3. 还发现个问题,在不同的浏览器上请求头存在大小写不一致的问题,但是并不影响结果。后来问了一下GPT,回答放在后面。

在HTTP协议中,请求头是不区分大小写的。这意味着,根据HTTP规范,请求头的字段名不应该被视为大小写敏感的。例如,Content-Type和content-type在HTTP的角度来看是相同的。

然而,实际上在某些情况下,服务器和浏览器的处理可能会导致大小写的差异。一些服务器可能会将请求头字段名视为大小写敏感,而另一些则可能不敏感。

此外,浏览器和其他HTTP客户端有时会以不同的方式处理请求头。有些浏览器可能会按照规范的方式处理,而另一些可能会将请求头字段名规范化为特定的形式。

为了确保在不同浏览器和服务器之间具有一致的行为,最好遵循HTTP规范,将请求头字段名视为不区分大小写。在编写代码时,最好使用规范的大小写形式。

因此,还是需要经验,多动手多动脑。

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述
若有侵权,请联系删除
  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值