爬取观鸟网的信息
有sign值,timestamp和requestid,要看这些值是怎么生成的
载荷有加密的数据
返回值也经过加密
搜索requestid,找到了eval加密的代码,通过解密,就能找到生成这些值的代码段
代码格式化后,找到了这几个值的生成位置
requestid的生成是随机值,timestamp是时间戳
e是一些限制条件,通过url编码可知,pointname是地点名称,所以e就是一些限制条件,然后序列化成了字符串
sign值就是这个值的字符串拼接,再通过md5加密
JSEncrypt是一个第三方库,通过npm install jsencrypt
encryptUnicodeLong函数是自定义的方法修改的源代码,所以找到这个函数的位置
这就是自定义的函数,使其支持中文加密,复制到本地的源码内,就可以获取data值
python代码
import json import requests from functools import partial import time import hashlib import subprocess subprocess.Popen = partial(subprocess.Popen, encoding="utf-8") import execjs from urllib.parse import urlencode from Crypto.Cipher import AES import base64 from Crypto.Util.Padding import pad,unpadurl = "https://api.birdreport.cn/front/record/activity/search"e = {"limit":"20","page":"4","pointname":"梧桐沟" }# url编码 e = urlencode(e)# 读取并执行js代码 with open("bird.js", 'r') as f:jscode = f.read() js = execjs.compile(jscode) res = js.call("jiami",e)# 获取请求头和data headers = res.get('header') data = res.get("data")# 补充请求头 headers.update({"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36","Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","Referer":"https://www.birdreport.cn/","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", })res = requests.post(url=url, data=data, headers=headers) data = res.json().get("data")# base64解码 data = base64.b64decode(data)# aes解密 key = "3583ec0257e2f4c8195eec7410ff1619" iv = "d93c0d5ec6352f20" aes = AES.new(key.encode('utf-8'), AES.MODE_CBC,iv.encode()) ret = aes.decrypt(data) print(ret.decode())
js代码
var JSEncrypt = require('node-jsencrypt'); // 引入md5标准库 const CryptoJS = require('crypto-js');function MD5(password) {const encryptedPassword = CryptoJS.MD5(password).toString();return encryptedPassword; }function getUuid() {var s = [];var a = "0123456789abcdef";for (var i = 0; i < 32; i++) {s[i] = a.substr(Math.floor(Math.random() * 0x10), 1)}s[14] = "4";s[19] = a.substr((s[19] & 0x3) | 0x8, 1);s[8] = s[13] = s[18] = s[23];var b = s.join("");return b }function sort_ASCII(a) {var b = new Array();var c = 0;for (var i in a) {b[c] = i;c++}var d = b.sort();var e = {};for (var i in d) {e[d[i]] = a[d[i]]}return e }function url2json(a) {var b = /^[^\?]+\?([\w\W]+)$/, reg_para = /([^&=]+)=([\w\W]*?)(&|$|#)/g, arr_url = b.exec(a), ret = {};if (arr_url && arr_url[1]) {var c = arr_url[1], result;while ((result = reg_para.exec(c)) != null) {ret[result[1]] = result[2]}}return ret }function dataTojson(a) {var b = [];var c = {};b = a.split('&');for (var i = 0; i < b.length; i++) {if (b[i].indexOf('=') != -1) {var d = b[i].split('=');if (d.length == 2) {c[d[0]] = d[1]} else {c[d[0]] = ""}} else {c[b[i]] = ''}}return c }const serialize = function (a) {var b = [];for (var p in a) if (a.hasOwnProperty(p) && a[p]) {b.push(encodeURIComponent(p) + '=' + encodeURIComponent(a[p]))}return b.join('&') }; var paramPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvxXa98E1uWXnBzXkS2yHUfnBM6n3PCwLdfIox03T91joBvjtoDqiQ5x3tTOfpHs3LtiqMMEafls6b0YWtgB1dse1W5m+FpeusVkCOkQxB4SZDH6tuerIknnmB/Hsq5wgEkIvO5Pff9biig6AyoAkdWpSek/1/B7zYIepYY0lxKQIDAQAB"; var encrypt = new JSEncrypt(); encrypt.setPublicKey(paramPublicKey);function jiami(text) {var c = Date.parse(new Date());var d = getUuid();var e = JSON.stringify(sort_ASCII(dataTojson(text || '{}')));data = encrypt.encryptUnicodeLong(e);var f = MD5(e + d + c);// 返回需要的数据return {header:{timestamp:c.toString(),requestId:d,sign:f},data:data} }