【Humo】小趴菜系列-CoinMarketCap第二弹-获取货币历史数据

381 阅读5分钟

嘿,兄die,我是小右。交个朋友吧~

CoinMarketCap第二弹

需求背景

接上一篇文章

上一次咱们已经把货币的历史数据给搞定了

这次的客户又新增了一个获取数据需求

需求描述

网址: https://coinmarketcap.com/

  1. 进入首页
  2. 点击Filters
  3. 点击Add Filter, 弹出More Filters弹出框
  4. 点击All Cryptocurrencies展开下拉框
  5. 分别选择CoinsTokens 页面会展示不同的查询结果
  6. 点击查询结果中的货币名称 跳转到货币详情页, 点击页面tabs中的Historical Data 查看货币历史数据 划到页面底部 还有一个Load More按钮
  7. 将每个货币的历史数据保存到各自Excel中

需求分析

第一步 获取列表页全部货币信息

  • 打开浏览器开发中工具 在结果列表页点击其他页数 得到查询列表页接口
  • 看查询参数应该和上一次的写的逻辑是一样的 没有page参数 是依靠start和limit进行范围获取的
  • 分析查询参数 目前有三个参数是咱们明确知道是干嘛的 startlimitcryptoTyle

  • 在响应信息中分析 cryptoCurrencyList是结果页数组 这个item需要哪些字段 需要点进货币进入到详情页再做分析

  • 点击详情页的Historical Data 得到了一个查询历史数据接口 一共四个查询参数

/data-api/v3/cryptocurrency/historical?id=3951&convertId=2781&timeStart=1665360000&timeEnd=1670630400

字段分析
id货币ID 通过列表页结果分析 对应为cryptoCurrencyList中的对象id字段
convertId暂时不知道干啥的 但是 你多打开几个货币 发现这个值都不变 所以应该是一个死的 不管他
timeStart顾名思义 开始时间 是一个秒级时间戳 2022-10-10
timeEnd结束时间 秒级时间戳 2022-12-10

多点几个翻页 分析一下时间戳的规律

timeStartdatetimeEnddate
16653600002022-10-1016706304002022-12-10
16627680002022-09-1016652736002022-10-09
16600896002022-08-1016626816002022-09-09
16574112002022-07-1016600032002022-08-09

第一条的结束时间是当天日期 开始时间往前推了两个月 第二条的结束时间是上一条的开始时间再往前推一天 开始时间往前推了一个月 第三条同第二条...

需求实现

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time        : 2022/12/10 01:05
# @Author      : Mini-Right
# @Email       : www@anyu.wang
# @File        : coin_market_cap_coins.py
# @Software    : PyCharm
# @Description :

import os
import time

import arrow
import requests
import xlsxwriter
from requests.adapters import HTTPAdapter


def handle_datetime(date: str):
    """
    日期时间处理
    :param date:    2022-10-09T23:59:59.999Z
    :return:        2022-10-09 23:59:59
    """
    return date.replace('T', ' ').split('.')[0]


def handle_date(date: str):
    """
    日期处理
    :param date:    2022-10-09T23:59:59.999Z
    :return:        2022-10-09
    """
    return date.split('T')[0]


def get_current_date() -> str:
    """
    description:  获取当前日期
    :return:      2021-02-19 -> str
    """
    return arrow.now().format('YYYY-MM-DD')


def get_any_date(date: str = get_current_date(), month: int = 0, day: int = 0, _format: str = "YYYY-MM-DD") -> str:
    """
    description:  获取距离传入日期的任意偏移时间的日期时间
    :param date :       时间
    :param month:       月 1代表传入时间+1月   -1代表当前时间-1月     默认=0
    :param day:         日 1代表传入时间+1日   -1代表当前时间-1日     默认=0
    :param _format:      格式
    :return:            2020-09-11 14:21:36 -> str
    """
    return arrow.get(date, _format).shift(months=month, days=day).format(_format)


def get_date_timestamp(date: str):
    """
    获取日期时间戳
    """
    return int(time.mktime(time.strptime(date, '%Y-%m-%d')))


class CoinMarketCapHistoricalData(object):

    def __init__(self):
        self.headers = {
            "accept": "application/json, text/plain, */*",
            "accept-encoding": "gzip, deflate, br",
            "accept-language": "zh-CN,zh;q\u003d0.9,en;q\u003d0.8",
            "cache-control": "no-cache",
            "origin": "https://coinmarketcap.com",
            "platform": "web",
            "referer": "https://coinmarketcap.com/",
            "sec-ch-ua": "\"Google Chrome\";v\u003d\"107\", \"Chromium\";v\u003d\"107\", \"Not\u003dA?Brand\";v\u003d\"24\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"macOS\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-site",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
            "x-request-id": "9f5c086e1a844c29bdf665c050a4bebe",
        }
        self.session = requests.session()
        self.session.mount('https://', HTTPAdapter(max_retries=3))

    def get_crypto_map(self, crypto_type: str):
        """
        获取类型下全部货币信息
        :param crypto_type: 货币类型  tokens  coins
        :return:
        """
        coin_map = {}
        url = 'https://api.coinmarketcap.com/data-api/v3/cryptocurrency/listing'
        start, limit = 1, 100
        count = 0
        while True:
            params = {
                'start': str(start),
                'limit': str(limit),
                'sortBy': 'market_cap',
                'sortType': 'desc',
                'convert': 'USD,BTC,ETH',
                'cryptoType': crypto_type,
                'tagType': 'all',
                'audited': 'false',
                'aux': 'ath,atl,high24h,low24h,num_market_pairs,cmc_rank,date_added,max_supply,circulating_supply,total_supply,volume_7d,volume_30d,self_reported_circulating_supply,self_reported_market_cap',
            }

            response = self.session.get(url=url, params=params, headers=self.headers)
            crypto_currency_list = response.json().get('data').get('cryptoCurrencyList')
            if not crypto_currency_list:
                break
            for _ in crypto_currency_list:
                count += 1
                item_info = {_.get('id'): _.get('slug')}
                print(f"{count} {item_info}")
                coin_map.update(item_info)
            start += limit
        return coin_map

    def historical_api(self, slug_id: int, slug: str, date: str):
        excel = ToExcel(f'./coins/{slug}.xlsx', 'sheet')
        excel.write_title(['type', 'id', 'quote_name', 'quote_symbol', 'quote_timestamp', 'quote_time_open', 'quote_open', 'quote_time_high', 'quote_high', 'quote_time_low', 'quote_low', 'quote_time_close', 'quote_close', 'quote_volume', 'quote_market_cap'])
        url = 'https://api.coinmarketcap.com/data-api/v3/cryptocurrency/historical'
        while True:
            _time_start, _time_end = get_any_date(date=date, month=-1), date
            params = {
                'id': str(slug_id),
                'convertId': '2781',
                # 起止时间 秒级时间戳
                'timeStart': str(get_date_timestamp(_time_start)),
                'timeEnd': str(get_date_timestamp(_time_end)),
            }
            response = self.session.get(url=url, params=params, headers=self.headers)
            # print(response.json())
            response_data = response.json().get('data')
            name = response_data.get('name')
            symbol = response_data.get('symbol')
            quotes: list = response_data.get('quotes')
            if not quotes:
                break
            # 因为获取时间选是从早到晚 为了避免出现时间错位 进行翻转
            quotes.reverse()
            for quote in quotes:
                quote_name = name
                quote_symbol = symbol
                quote_time_open: str = quote.get('timeOpen')
                quote_time_close: str = quote.get('timeClose')
                quote_time_high: str = quote.get('timeHigh')
                quote_time_low: str = quote.get('timeLow')
                quote_open = quote.get('quote').get('open')
                quote_high = quote.get('quote').get('high')
                quote_low = quote.get('quote').get('low')
                quote_close = quote.get('quote').get('close')
                quote_volume = quote.get('quote').get('volume')
                quote_market_cap = quote.get('quote').get('marketCap')
                quote_timestamp: str = quote.get('quote').get('timestamp')

                quote_info = {
                    'type': 'coins',
                    'id': '6841',
                    'quote_name': quote_name,
                    'quote_symbol': quote_symbol,
                    'quote_timestamp': handle_date(quote_timestamp),
                    'quote_time_open': handle_datetime(quote_time_open),
                    'quote_open': quote_open,
                    'quote_time_high': handle_datetime(quote_time_high),
                    'quote_high': quote_high,
                    'quote_time_low': handle_datetime(quote_time_low),
                    'quote_low': quote_low,
                    'quote_time_close': handle_datetime(quote_time_close),
                    'quote_close': quote_close,
                    'quote_volume': quote_volume,
                    'quote_market_cap': quote_market_cap,
                }
                print(quote_info)
                excel.write(list(quote_info.values()))

            date = get_any_date(date=_time_start, month=0, day=-1)

        excel.close()

    def main(self, crypto_type: str, date: str = get_current_date()):
        if not os.path.exists(f"./{crypto_type}/"):
            os.mkdir(f"./{crypto_type}/")
        crypto_map = c.get_crypto_map(crypto_type)

        for slug_id, slug in crypto_map.items():
            print(f"开始处理: {slug_id}: {slug}")
            self.historical_api(slug_id=slug_id, slug=slug, date=date)


class ToExcel(object):
    def __init__(self, excel_path: str, sheet_name: str):
        self.f = xlsxwriter.Workbook(
            excel_path,
            options={
                'strings_to_urls': False,
                'default_date_format': '%Y-%m-%d %H:%M:%S'
            })
        self.ws = self.f.add_worksheet(sheet_name)
        self.count = 0

    def write_title(self, data: list):
        for index, value in enumerate(data):
            self.ws.write_string(0, index, str(value))

    def write(self, data: list):
        self.count += 1
        for index, value in enumerate(data):
            self.ws.write_string(self.count, index, str(value))

    def close(self):
        self.f.close()


if __name__ == '__main__':
    crypto_type = 'coins'
    # crypto_type = 'tokens'
    c = CoinMarketCapHistoricalData()
    c.main(crypto_type)

公众号: 小右说