嘿,兄die,我是小右。交个朋友吧~
CoinMarketCap第二弹
需求背景
接上一篇文章
上一次咱们已经把货币的历史数据给搞定了
这次的客户又新增了一个获取数据需求
需求描述
网址: https://coinmarketcap.com/
- 进入首页
- 点击
Filters
- 点击
Add Filter
, 弹出More Filters
弹出框 - 点击
All Cryptocurrencies
展开下拉框 - 分别选择
Coins
和Tokens
页面会展示不同的查询结果 - 点击查询结果中的货币名称 跳转到货币详情页, 点击页面tabs中的
Historical Data
查看货币历史数据 划到页面底部 还有一个Load More
按钮 - 将每个货币的历史数据保存到各自Excel中
需求分析
第一步 获取列表页全部货币信息
- 打开浏览器开发中工具 在结果列表页点击其他页数 得到查询列表页接口
- 看查询参数应该和上一次的写的逻辑是一样的 没有page参数 是依靠start和limit进行范围获取的
- 分析查询参数 目前有三个参数是咱们明确知道是干嘛的
start
、limit
、cryptoTyle
- 在响应信息中分析
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 |
多点几个翻页 分析一下时间戳的规律
timeStart | date | timeEnd | date |
---|---|---|---|
1665360000 | 2022-10-10 | 1670630400 | 2022-12-10 |
1662768000 | 2022-09-10 | 1665273600 | 2022-10-09 |
1660089600 | 2022-08-10 | 1662681600 | 2022-09-09 |
1657411200 | 2022-07-10 | 1660003200 | 2022-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)
公众号: 小右说