小白求教一些爬虫逆向的思路方法...

2025/04/10

感谢各位佬友提供的思路,在大家的帮助下,今天终于成功爬到了数据 :joy:

期间通过这个文章学习了破除无限debug,也分享给大家:

https://www.cnblogs.com/liyuanhong/articles/18210072

在经理本地替换js->搜索aes关键代码->控制台输出后终于找到了key和iv(确实挺有嘲讽意味的 :zany_face:

然后再和deepseek掰扯很久后终于有了以下代码(期间因为json无效的问题耽误好久):

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64
import json

def decrypt_jiucai_fear_greed(encrypted_text):
    
    '''
    函数用于解密韭圈儿的数据
    
    encrypted_text: 从api获取的加密字符串
    
    '''
    
    # 初始化解析字典
    results = {}
    
    try:
        # 处理密钥和IV
        key = ("bieyanjiulexixishuibatoufamei" + "ll1").encode('utf-8')[:32]  # AES-256
        iv = ("nengnongchulainb" + "ll1").encode('utf-8')[:16]  # 16字节
        
        # Base64解码
        ciphertext = base64.b64decode(encrypted_text)
        
        # AES解密
        cipher = AES.new(key, AES.MODE_CBC, iv)
        decrypted = cipher.decrypt(ciphertext)
        
        # 尝试自动去除PKCS7填充
        try:
            decrypted = unpad(decrypted, AES.block_size)
        except ValueError:
            # 如果自动去除失败,手动清理无效字符
            decrypted = decrypted.rstrip(b'\x00')  # 去除\x00
            
        # 提取有效JSON部分
        decrypted_str = decrypted.decode('utf-8', errors='ignore')
        json_start = decrypted_str.find('{')
        json_end = decrypted_str.rfind('}')
        if json_start != -1 and json_end != -1:
            results =  json.loads(decrypted_str[json_start:json_end+1])
            
    except:
        pass
    
    return results

再次感谢大家,此贴完结 :tada:


2025/04/09

最近市场因为众所周知的原因震荡,我想找一些可以反映市场情绪的指标,就找到了这个:

韭圈儿恐惧贪婪指数

看了一下内容挺详细的,于是想自己做一个爬虫每天拉一下数据。结果发现主要的数据api:
https://api.jiucaishuo.com/v2/kjtl/history-lines
返回的结果是加密的数据。

我于是看了下发起程序序列,发现这个js中有写相关加解密的函数:
https://app.jiucaishuo.com/static/js/index.ae29f209.js

然后,我就不会了… 有没有懂得大佬指点一下解密的思路呀,感谢!

19 Likes

与其琢磨加解密不如直接selenium模拟浏览器行为得了,反正你频率不高网页也简单不是吗

5 Likes

首先声明我略懂一点点,我觉得你既然已经定位到这个js里有解密方法了,是不是可以打个断点,一步步调试呢?最后将涉及到的方法函数都放到自己的js里走一遍,应该就可以解密了。
从请求的堆栈里,看下js的加载过程,里面应该可以定位到具体方法的。

2 Likes

靠经验啦,比如这个接口的响应看结尾是==,那99%是base64了,然后就经验猜测是AES了,去你发现的js里全局搜索下aes,很容易就定位到了


然后在用一些小手段,输出看到iv和key就行了

9 Likes

好奇佬友说的小手段 :joy:

1 Like

md
跟你拼了

以他的解密方法拿过用。思路清晰。

我也是小白,一般都是打断点跟调用栈,搜常见的关键加密词。

1 Like

最简单就打个断点直接看了,当然这个有反调试,我懒得研究就直接把js替换加两个console.log了

这个反调试,可以在跳进去前,index.js里的window[“eval”](“(function() { setInterval(() => { debugger; }, 1000); }())”);这个位置
打个断点,然后在console输入方法

// 重写 setInterval,使它不会执行 callback
window.setInterval = function(callback, interval) {
// 返回 false 或直接不调用 callback
return 0; // 返回一个假的 ID
};
这样子可以跳过这个调试

3 Likes

相对还蛮简单的

贪婪恐惧 币安有接口啊, 你看看跟币安的数据一样不

https://api.alternative.me/fng/?limit=30

兄弟,都是有现成接口的,之前问ai要的计算ahr999的脚本又改了一点,你看看是不是这个恐惧贪婪指数

代码块
import requests
import datetime
import math
import pandas as pd

from ta.momentum import RSIIndicator
import urllib3
import warnings
import logging

logging.basicConfig(level=logging.ERROR)

def get_btc_price():
    """
    从币安API获取比特币当前价格
    
    返回:
    float: 比特币当前价格(以USDT计)
    """
    try:
        url = "https://api.binance.com/api/v3/ticker/price"
        params = {"symbol": "BTCUSDT"}
        response = requests.get(url, params=params)
        data = response.json()
        return float(data["price"])
    except Exception as e:
        logging.error(f"获取比特币价格时发生错误: {str(e)}")
        return None

def get_btc_200ma():
    """
    从币安API获取过去200天的比特币价格并计算平均值
    
    这个函数获取过去200天的每日收盘价,并计算它们的平均值,
    也就是200日定投成本。

    返回:
    float: 比特币200日平均价格
    """
    try:
        url = "https://api.binance.com/api/v3/klines"
        params = {
            "symbol": "BTCUSDT",
            "interval": "1d",
            "limit": 200
        }
        response = requests.get(url, params=params)
        data = response.json()
        prices = [float(item[4]) for item in data]  # 收盘价是第5个元素(索引4)
        return sum(prices) / len(prices)
    except Exception as e:
        logging.error(f"获取比特币200日平均价格时发生错误: {str(e)}")
        return None

def calculate_bitcoin_age():
    """
    计算比特币的年龄(以天为单位)
    
    从比特币创世区块的日期(2009年1月3日)到今天的天数

    返回:
    int: 比特币年龄(天数)
    """
    genesis_date = datetime.date(2009, 1, 3)
    current_date = datetime.date.today()
    return (current_date - genesis_date).days

def calculate_exponential_growth_valuation(bitcoin_age):
    """
    计算比特币的指数增长估值
    
    使用公式: 10^(5.84 * log10(比特币年龄) - 17.01)

    参数:
    bitcoin_age (int): 比特币年龄(天数)

    返回:
    float: 指数增长估值
    """
    return 10 ** (5.84 * math.log10(bitcoin_age) - 17.01)

def calculate_ahr999(price, ma200, exp_growth_val):
    """
    计算AHR999指标
    
    使用公式: (比特币价格/200日定投成本) * (比特币价格/指数增长估值)

    参数:
    price (float): 当前比特币价格
    ma200 (float): 200日平均价格(200日定投成本)
    exp_growth_val (float): 指数增长估值

    返回:
    float: AHR999指标值
    """
    return (price / ma200) * (price / exp_growth_val)


# def get_altcoin_season_index():
#     """
#     获取代币季指数
    
#     代币季指数用于衡量山寨币相对比特币的表现。
#     指数范围从0到100:
#     0-25: 比特币季节
#     26-75: 中性市场
#     76-100: 山寨币季节
    
#     返回:
#     int: 代币季指数值
#     str: 市场状态描述
#     """
#     url = "https://api.blockchaincenter.net/v1/index/altcoin_season_index"
#     response = requests.get(url, proxies=proxies)
#     data = response.json()
#     index = data["altcoin_season_index"]
    
#     if index <= 25:
#         status = "比特币季节"
#     elif index <= 75:
#         status = "中性市场"
#     else:
#         status = "山寨币季节"
    
#     return index, status

def get_btc_price_on_date(date):
    """
    从币安API获取指定日期的比特币收盘价
    
    参数:
    date (datetime.date): 指定的日期

    返回:
    float: 指定日期的比特币收盘价(USDT计)
    """
    url = "https://api.binance.com/api/v3/klines"
    # date转换为datetime,然后获取timestamp
    datetime_obj = datetime.datetime.combine(date, datetime.time.min)
    start_time = int(datetime_obj.timestamp() * 1000)
    params = {
        "symbol": "BTCUSDT",
        "interval": "1d",
        "startTime": start_time,
        "limit": 1
    }
    response = requests.get(url, params=params)
    data = response.json()
    if data:
        return float(data[0][4])  # 收盘价是第5个元素(索引4)
    else:
        raise ValueError(f"无法获取 {date} 的比特币价格数据")

def get_btc_200ma_on_date(date):
    """
    从币安API获取指定日期之前200天的比特币价格并计算平均值
    
    参数:
    date (datetime.date): 指定的日期

    返回:
    float: 比特币200日平均价格
    """
    # 将date转换为datetime,然后获取timestamp
    datetime_obj = datetime.datetime.combine(date, datetime.time.min)
    end_time = int(datetime_obj.timestamp() * 1000)
    url = "https://api.binance.com/api/v3/klines"
    params = {
        "symbol": "BTCUSDT",
        "interval": "1d",
        "endTime": end_time,
        "limit": 200
    }
    response = requests.get(url, params=params)
    data = response.json()
    prices = [float(item[4]) for item in data]  # 收盘价是第5个元素(索引4)
    return sum(prices) / len(prices)

def get_binance_c2c_rate():
    """
    获取币安C2C的CNY对USDT汇率
    
    返回:
    float: CNY对USDT的汇率
    """
    try:
        url = "https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search"
        headers = {
            "Content-Type": "application/json"
        }
        payload = {
            "fiat": "CNY",
            "page": 1,
            "rows": 10,
            "tradeType": "BUY",
            "asset": "USDT",
            "countries": [],
            "proMerchantAds": False,
            "publisherType": None,
            "payTypes": []
        }
        
        response = requests.post(url, json=payload, headers=headers)
        data = response.json()
        
        if data["success"] and data["data"]:
            rates = [float(ad["adv"]["price"]) for ad in data["data"]]
            return sum(rates) / len(rates)
        else:
            raise ValueError("无法获取币安C2C的CNY对USDT汇率")
    except Exception as e:
        logging.error(f"获取币安C2C汇率时发生错误: {str(e)}")
        return None

def get_fear_and_greed_index():
    """
    获取恐惧与贪婪指数
    - 0-24 :极度恐惧(橙色)

    - 25-49: 恐惧(琥珀色/黄色)

    - 50-74: 贪婪(浅绿色)

    - 75 -100: 极度贪婪(绿色)
    """
    try:
        url = "https://api.alternative.me/fng/"
        response = requests.get(url)
        data = response.json()
        return int(data["data"][0]["value"])
    except Exception as e:
        logging.error(f"获取恐惧与贪婪指数时发生错误: {str(e)}")
        return None

# def get_google_trends():
#     """
#     获取谷歌趋势数据
#     """
#     pytrends = TrendReq(hl='en-US', tz=360)
#     kw_list = ["bitcoin"]
#     pytrends.build_payload(kw_list, timeframe='today 1-m')
#     data = pytrends.interest_over_time()
#     return data["bitcoin"].iloc[-1]

def get_week_200ma():
    """
    获取200周移动平均线
    """
    url = "https://api.binance.com/api/v3/klines"
    params = {
        "symbol": "BTCUSDT",
        "interval": "1w",
        "limit": 200
    }
    response = requests.get(url, params=params)
    data = response.json()
    prices = [float(item[4]) for item in data]  # 收盘价是第5个元素(索引4)
    return sum(prices) / len(prices)

def get_week_rsi():
    """
    获取周RSI
    """
    url = "https://api.binance.com/api/v3/klines"
    params = {
        "symbol": "BTCUSDT",
        "interval": "1w",
        "limit": 100
    }
    response = requests.get(url, params=params)
    data = response.json()
    prices = [float(item[4]) for item in data]  # 收盘价是第5个元素(索引4)
    df = pd.DataFrame(prices, columns=['close'])
    rsi_indicator = RSIIndicator(close=df['close'], window=14)
    return rsi_indicator.rsi().iloc[-1]

# def get_market_bias():
#     """
#     计算市场偏差(Market Bias)
#     这里我们使用一个简单的方法:比较当前价格与200周均线的关系
#     """
#     current_price = get_btc_price()
#     ma200w = get_week_200ma()
#     bias = (current_price - ma200w) / ma200w * 100
#     return bias

def get_binance_btc_data(symbol="BTCUSDT", interval="1h", limit=100):
    """
    从币安API获取比特币K线数据
    """
    url = "https://api.binance.com/api/v3/klines"
    params = {
        "symbol": symbol,
        "interval": interval,
        "limit": limit
    }
    response = requests.get(url, params=params)
    data = response.json()
    
    df = pd.DataFrame(data, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume', 'close_time', 'quote_asset_volume', 'number_of_trades', 'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume', 'ignore'])
    df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
    df['close'] = df['close'].astype(float)
    df['volume'] = df['volume'].astype(float)
    
    return df

def calculate_can_short(price_data, volume_data, ma_period=20, volume_threshold=1.5, price_change_threshold=0.02):
    """
    根据价格和交易量数据计算是否可以做空
    """
    # 计算价格移动平均线
    price_ma = price_data.rolling(window=ma_period).mean()
    
    # 计算交易量移动平均线
    volume_ma = volume_data.rolling(window=ma_period).mean()
    
    # 检查当前价格是否低于移动平均线
    price_below_ma = price_data.iloc[-1] < price_ma.iloc[-1]
    
    # 检查当前交易量是否高于平均水平
    volume_above_average = volume_data.iloc[-1] > volume_ma.iloc[-1] * volume_threshold
    
    # 计算价格变化
    price_change = (price_data.iloc[-1] - price_data.iloc[-2]) / price_data.iloc[-2]
    
    # 检查价格是否显著下跌
    significant_price_decrease = price_change < -price_change_threshold
    
    # 判断是否可以做空
    can_short = 1 if price_below_ma and volume_above_average and significant_price_decrease else 0
    
    return can_short

def can_short():
    """
    判断是否可以做空
    """
    try:
        # 从币安获取比特币数据
        df = get_binance_btc_data()
        
        # 计算can_short值
        can_short_value = calculate_can_short(df['close'], df['volume'])
        
        return can_short_value
    except Exception as e:
        logging.error(f"计算can_short时发生错误: {str(e)}")
        return None

def main(date=None):
    """
    主函数,协调整个计算过程并输出结果
    
    参数:
    date (datetime.date, 可选): 指定的日期,默认为None(使用当前日期)
    """
    try:
        if date is None:
            date = datetime.date.today()
        
        # 获取指定日期的比特币价格
        btc_price = get_btc_price_on_date(date)
        
        # 计算指定日期的200日平均价格
        btc_200ma = get_btc_200ma_on_date(date)
        
        # 计算比特币年龄
        bitcoin_age = (date - datetime.date(2009, 1, 3)).days
        
        # 计算指数增长估值
        exp_growth_val = calculate_exponential_growth_valuation(bitcoin_age)
        
        # 计算AHR999指标,处理可能的None值
        ahr999 = None
        if btc_price and btc_200ma and exp_growth_val:
            ahr999 = calculate_ahr999(btc_price, btc_200ma, exp_growth_val)
        
        # 获取币安C2C的CNY对USDT汇率
        cny_usdt_rate = get_binance_c2c_rate()
        
        # 获取新增的指标
        fgi = get_fear_and_greed_index()
        # google_trends = get_google_trends()
        week_200ma = get_week_200ma()
        week_rsi = get_week_rsi()
        # market_bias = get_market_bias()
        can_short_value = can_short()
        
        # 输出结果,处理可能的None值
        print(f"日期: {date}")
        print(f"比特币价格: ${btc_price:.2f}" if btc_price else "比特币价格: 无法获取")
        print(f"比特币200日平均价格: ${btc_200ma:.2f}" if btc_200ma else "比特币200日平均价格: 无法获取")
        print(f"比特币年龄(天): {bitcoin_age}")
        print(f"指数增长估值: ${exp_growth_val:.2f}" if exp_growth_val else "指数增长估值: 无法计算")
        print(f"AHR999 指标值: {ahr999:.4f}" if ahr999 else "AHR999 指标值: 无法计算")
        print(f"币安C2C CNY/USDT 汇率: ¥{cny_usdt_rate:.2f}" if cny_usdt_rate else "币安C2C CNY/USDT 汇率: 无法获取")
        if btc_price and cny_usdt_rate:
            print(f"比特币价格(CNY): ¥{btc_price * cny_usdt_rate:.2f}")
        else:
            print("比特币价格(CNY): 无法计算")
        print(f"恐惧与贪婪指数: {fgi}" if fgi is not None else "恐惧与贪婪指数: 无法获取")
        # print(f"谷歌趋势数据: {google_trends}" if google_trends is not None else "谷歌趋势数据: 无法获取")
        print(f"200周移动平均线: ${week_200ma:.2f}" if week_200ma else "200周移动平均线: 无法获取")
        print(f"周RSI: {week_rsi:.2f}" if week_rsi is not None else "周RSI: 无法获取")
        # print(f"市场偏差: {market_bias:.2f}%" if market_bias is not None else "市场偏差: 无法计算")
        print(f"是否可以做空: {'是' if can_short_value == 1 else '否'}" if can_short_value is not None else "是否可以做空: 无法判断")
    except Exception as e:
        logging.error(f"主函数执行时发生错误: {str(e)}")
        print("程序执行过程中发生错误,请查看日志获取详细信息。")

if __name__ == "__main__":
    main()

学习一下

学习学习

都是佬啊!

谢谢佬友的思路哈,这点我倒真没想到… 但因为我是要内嵌在wx机器人的,那个小机器我都没装chrome…

谢谢佬友!以前的我只会f12看api直接用,看来得进一步琢磨了噗

这是真的大佬哈哈哈,我就会一丢丢python,等我干完活摸鱼研究一下 :clap:

谢谢佬友,这个数据似乎不太一样哈,恐惧贪婪指数的计算方式有很多种,并且在不同市场的数值也不太一样的,还是很感谢啦