如何从 Coinmarketcap 解析历史 BTC 数据?

Posted

技术标签:

【中文标题】如何从 Coinmarketcap 解析历史 BTC 数据?【英文标题】:How to parse Historical BTC Data from Coinmarketcap? 【发布时间】:2020-02-16 15:57:35 【问题描述】:

我正在尝试学习如何使用 Python、请求和 BeautifulSoup 从 Coinmarketcap.com 网络抓取 BTC 历史数据。

我想解析以下内容:

1)日期

2)关闭

3)音量

4)市值

到目前为止,这是我的代码:

import requests
from bs4 import BeautifulSoup
from fake_useragent import UserAgent

ua = UserAgent()
header = 'user-agent': ua.chrome
response = requests.get('https://coinmarketcap.com/currencies/bitcoin/historical-data/', headers=header)

# html.parser
soup = BeautifulSoup(response.content,'lxml')  

tags = soup.find_all('td')
print(tags)

我能够抓取我需要的数据,但我不确定如何正确解析它。我宁愿让日期尽可能回溯(“所有时间”)。任何建议将不胜感激。提前致谢!

【问题讨论】:

这是 link 您想利用它来获取所有时间的数据。 【参考方案1】:

编辑

CoinMarketCap 似乎改变了他们的 DOM,所以这里有一个更新:

import lxml.html
import requests
from typing import Dict, List


def coinmarketcap_get_btc(start_date: str, end_date: str) -> List[Dict]:
    # Build the url
    url = f'https://coinmarketcap.com/currencies/bitcoin/historical-data/?start=start_date&end=end_date'
    # Make the request and parse the tree
    response = requests.get(url, timeout=5)
    tree = lxml.html.fromstring(response.text)
    # Extract table and raw data
    table = tree.find_class('cmc-table')[0]
    xpath_0, xpath_1 = 'div[3]/div/table/thead/tr', 'div[3]/div/table/tbody/tr/td[%d]/div'
    cols = [_.text_content() for _ in table.xpath(xpath_0 + '/th')]
    dates = (_.text_content() for _ in table.xpath(xpath_1 % 1))
    m = map(lambda d: (float(_.text_content().replace(',', '')) for _ in table.xpath(xpath_1 % d)),
            range(2, 8))
    return [k: v for k, v in zip(cols, _) for _ in zip(dates, *m)]

获取 df 就像使用 pd.DataFrame.from_dict 一样简单。


原创

您可以为此requestslxml

这是一个函数coinmarketcap_get_btc,它将开始和结束日期作为参数并收集相关数据

import lxml.html
import pandas
import requests


def float_helper(string):
    try:
        return float(string)
    except ValueError:
        return None


def coinmarketcap_get_btc(start_date: str, end_date: str) -> pandas.DataFrame:
    # Build the url
    url = f'https://coinmarketcap.com/currencies/bitcoin/historical-data/?start=start_date&end=end_date'
    # Make the request and parse the tree
    response = requests.get(url, timeout=5)
    tree = lxml.html.fromstring(response.text)
    # Extract table and raw data
    table = tree.find_class('table-responsive')[0]
    raw_data = [_.text_content() for _ in table.find_class('text-right')]
    # Process the data
    col_names = ['Date'] + raw_data[:6]
    row_list = []
    for x in raw_data[6:]:
        _, date, _open, _high, _low, _close, _vol, _m_cap, _ = x.replace(',', '').split('\n')
        row_list.append([date, float_helper(_open), float_helper(_high), float_helper(_low),
                         float_helper(_close), float_helper(_vol), float_helper(_m_cap)])
    return pandas.DataFrame(data=row_list, columns=col_names)

您始终可以忽略不感兴趣的列并添加更多功能(例如,接受 datetime.datetime 对象作为日期)。

注意,用于构建 URL 的 f-string 至少需要 Python 3.x 版本(我相信是 3.6),所以如果您使用的是旧版本,只需使用其中一个'stringvar.format(var=var)''string%s' % var 符号。

示例

df = coinmarketcap_get_btc(start_date='20130428', end_date='20191020')
df
#              Date    Open*     High      Low  Close**        Volume    Market Cap
# 0     Oct 19 2019  7973.80  8082.63  7944.78  7988.56  1.379783e+10  1.438082e+11
# 1     Oct 18 2019  8100.93  8138.41  7902.16  7973.21  1.565159e+10  1.435176e+11
# 2     Oct 17 2019  8047.81  8134.83  8000.94  8103.91  1.431305e+10  1.458540e+11
# 3     Oct 16 2019  8204.67  8216.81  7985.09  8047.53  1.607165e+10  1.448240e+11
# 4     Oct 15 2019  8373.46  8410.71  8182.71  8205.37  1.522041e+10  1.476501e+11
# ...           ...      ...      ...      ...      ...           ...           ...
# 2361  May 02 2013   116.38   125.60    92.28   105.21           NaN  1.168517e+09
# 2362  May 01 2013   139.00   139.89   107.72   116.99           NaN  1.298955e+09
# 2363  Apr 30 2013   144.00   146.93   134.05   139.00           NaN  1.542813e+09
# 2364  Apr 29 2013   134.44   147.49   134.00   144.54           NaN  1.603769e+09
# 2365  Apr 28 2013   135.30   135.98   132.10   134.21           NaN  1.488567e+09
# 
# [2366 rows x 7 columns]

【讨论】:

感谢@niko 花时间彻底解释每个步骤。多亏了你,我会学到很多……为此,我真的很感激! =) 这似乎不再起作用了......也许 coinmarketcap 阻止了这种行为...... @CharlyEmpereur-mot 他们并没有真正阻止它,但他们改变了 DOM。检查编辑以获取更新的解决方案。【参考方案2】:

你可以有一个函数,它需要几个月才能返回(你可以改变这个,但几个月是一个很好的例子)然后使用 pandas read_html 来获取列的表和子集。目前设置为从今天开始工作。

import requests
import pandas as pd
from datetime import datetime
from dateutil.relativedelta import relativedelta

def get_date_range(number_of_months:int):
    now = datetime.now()
    dt_end = now.strftime("%Y%m%d")
    dt_start = (now - relativedelta(months=number_of_months)).strftime("%Y%m%d")
    return f'start=dt_start&end=dt_end'

number_of_months = 3

table = pd.read_html(f'https://coinmarketcap.com/currencies/bitcoin/historical-data/?get_date_range(number_of_months)')[0]
table = table[['Date', 'Close**', 'Volume','Market Cap']]
print(table)

【讨论】:

我做错了吗?我收到一个错误:Traceback(最近一次通话最后一次):文件“C:/Users/xboss/PycharmProjects/Practice_Makes_Perfect/venv/coinmarketcap.com.py”,第 105 行,在 table = pd.read_html(f' coinmarketcap.com/currencies/bitcoin/historical-data?get_date_range(number_of_months)')[0] 文件“C:/Users/xboss/PycharmProjects/Practice_Makes_Perfect/venv/coinmarketcap.com.py”,第 100 行,在 get_date_range dt_start = (now - relativedelta(months =n)).strftime("%Y%m%d") NameError: name 'n' is not defined 我做了替换,忘记了编辑。请立即尝试。 这是@QHarr 的精美作品。泰好先生! =) 我会研究这个并且会从中学到很多东西。 收到ValueError: No tables found 错误 见下面的答案。看起来页面在编写后发生了变化。【参考方案3】:

这是使用BeautifulSoup 库从该表中获取上述字段的方法之一。我使用.select() 而不是.find_all() 来定位所需的项目。

工作解决方案:

import pandas
import requests
from bs4 import BeautifulSoup

link = 'https://coinmarketcap.com/currencies/bitcoin/historical-data/?start=&end='

def get_coinmarketcap_info(url,s_date,e_date):
    response = requests.get(url.format(s_date,e_date))
    soup = BeautifulSoup(response.text,"lxml")

    for items in soup.select("table.table tr.text-right"):
        date = items.select_one("td.text-left").get_text(strip=True)
        close = items.select_one("td[data-format-market-cap]").find_previous_sibling().get_text(strip=True)
        volume = items.select_one("td[data-format-market-cap]").get_text(strip=True)
        marketcap = items.select_one("td[data-format-market-cap]").find_next_sibling().get_text(strip=True)
        yield date,close,volume,marketcap

if __name__ == '__main__':
    dataframe = (elem for elem in get_coinmarketcap_info(link,s_date='20130428',e_date='20191020'))
    df = pandas.DataFrame(dataframe)
    print(df)

【讨论】:

感谢@SIM 的精彩回复!这是非常有用和信息丰富的! =) 我也非常感谢您展示如何将数据转换为 pandas 数据框。

以上是关于如何从 Coinmarketcap 解析历史 BTC 数据?的主要内容,如果未能解决你的问题,请参考以下文章

从 Coinmarketcap.com 抓取

Python从Coinmarketcap网站API获取数据

如何在 Flutter 中使用 CoinMarketCap API

coinmarketcap.com爬虫

coinmarketcap api v2 jquery

btsnooz.py 不解析 android BT 日志