Last active
December 21, 2024 18:46
-
-
Save btc100k/b622a5ad8e97559f3563ad6d0308f0c3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import requests | |
import datetime | |
from bs4 import BeautifulSoup | |
import sys | |
# This python script scrapes Ocean.xyz and creates a csv of the payouts to a given BTC address. | |
# | |
# This will be useful for anyone mining on Ocean.xyz who wants to keep track of their earnings in BTC or USD | |
# | |
# Example output: | |
# Please enter the address you're using to mine on Ocean.xyz: <3QomtEj5nfzEkxPXoVD3hvxgJDzA6M6evt> | |
# Row, Block_Height, Date, BTCUSD, BTC_Earned, USD_Earned, BTC_Fee, USD_Fee | |
# 1, 829513, 02-08-2024, $44335.00, 0.43437323, $19257.94, 0.00000000, 0.0 | |
# 2, 829267, 02-06-2024, $42658.30, 0.35614859, $15192.69, 0.00000000, 0.0 | |
# 3, 828232, 01-31-2024, $42937.72, 0.13227494, $5679.58, 0.00000000, 0.0 | |
# 4, 827750, 01-28-2024, $42126.55, 0.03297256, $1389.02, 0.00000000, 0.0 | |
# | |
# How it does it: | |
# 1. it visits Ocean.xyz and grabs your "Latest Earnings" | |
# 2. it takes the hash from the earning to mempool.space to get the date of the earning | |
# 3. it takes the date to coinbase's api to get the BTCUSD | |
# 4. it does the math to calculate $ earned & $ fee paid | |
# 5. it formats this into a nice CSV for you to import into Excel/Quickbooks/etc. | |
# | |
# Is there value here for you? | |
# Value 4 Value: [email protected] | |
# | |
def fetch_block_details(block_hash: str): | |
url = f"https://mempool.space/api/block/{block_hash}" | |
response = requests.get(url) | |
if response.status_code == 200: | |
data = response.json() | |
height = data.get('height') | |
timestamp = data.get('timestamp') | |
datetime_obj = datetime.datetime.utcfromtimestamp(timestamp) | |
formatted_date = datetime_obj.strftime("%m-%d-%Y") | |
return height, formatted_date | |
else: | |
return None, None | |
def get_bitcoin_price_on_date(mmddyyyy_str: str): | |
month, day, year = mmddyyyy_str.split("-") | |
date_formatted = f"{year}-{month}-{day}" | |
url = f"https://api.coinbase.com/v2/prices/BTC-USD/spot?date={date_formatted}" | |
try: | |
response = requests.get(url) | |
if response.status_code == 200: | |
data = response.json() | |
# The price is in the 'data' field, under 'amount' | |
btc_price = data['data']['amount'] | |
return float(btc_price) | |
else: | |
return "Error: Unable to fetch Bitcoin price data" | |
except Exception as e: | |
return f"Error: {str(e)}" | |
def output_for_address(ocean_addr: str, maximum_page_number: int = 20, oldest_to_newest: bool = True): | |
page_index = 0 | |
output_data = [] | |
while page_index < maximum_page_number: | |
url = f"https://ocean.xyz/template/workers/earnings/rows?user={ocean_addr}&epage={page_index+1}&page={page_index}&sortParam=" | |
page_index = page_index + 1 | |
print(f"Working on page {page_index}") | |
# Fetch the webpage | |
response = requests.get(url) | |
html = response.text | |
# Parse the HTML | |
soup = BeautifulSoup(html, 'html.parser') | |
rows = soup.find_all("tr", class_="table-row") | |
if not rows: | |
break | |
for row in soup.find_all("tr", class_="table-row"): | |
cells = row.find_all("td", class_="table-cell") | |
block_hash = cells[0].get_text(strip=True).split()[0] | |
btc_earned = cells[2].get_text(strip=True).split()[0] | |
btc_fee = cells[3].get_text(strip=True).split()[0] | |
height, mmdddyyyy_str = fetch_block_details(block_hash) | |
btcusd = get_bitcoin_price_on_date(mmdddyyyy_str) | |
usd_earned = float(btc_earned) * float(btcusd) | |
usd_fee_paid = float(btc_fee) * float(btcusd) | |
output = (f"{height}, " | |
f"{mmdddyyyy_str}, " | |
f"${btcusd:.2f}, " | |
f"{btc_earned}, " | |
f"${usd_earned:0.2f}, " | |
f"{btc_fee}, " | |
f"{usd_fee_paid}") | |
output_data.append(output) | |
if oldest_to_newest: | |
output_data.reverse() | |
output_row_number = 1 | |
print("Row", "Block_Height", "Date", "BTCUSD", "BTC_Earned", "USD_Earned", "BTC_Fee", "USD_Fee", sep=", ") | |
for one_row in output_data: | |
print(output_row_number, one_row, sep=", ") | |
output_row_number += 1 | |
if __name__ == "__main__": | |
old_first = True | |
pages = 10 | |
if len(sys.argv) > 1: | |
btc_address = sys.argv[1] | |
else: | |
btc_address = input("Please enter the address you're using to mine on Ocean.xyz: ") | |
# | |
# I'm not really documenting it, but you can pass in the # of pages to fetch in the 2nd argument | |
# | |
if len(sys.argv) > 2: | |
pages = int(sys.argv[2]) | |
# | |
# I'm not really documenting it, but you can pass "false" as arg #3 if you want it sorted newest-to-oldest" | |
# | |
if len(sys.argv) > 3: | |
old_first = False if sys.argv[3].lower() == "false" else True | |
if len(btc_address): | |
print("") | |
output_for_address(ocean_addr=btc_address, maximum_page_number=pages, oldest_to_newest=old_first) | |
print("") | |
else: | |
print("\nNext time, enter a BTC address on the command line, or at the prompt. \nHappy Hashing!") |
Also found it helpful to modify 83-102 to limit iterations for the latest ~3 blocks. (It'd be cool to take this in as an argument actually):
output_data = []
counter = 0
for row in earnings_table.find_all('tr')::#iterating through only three rows
# Extract data from each cell
cells = row.find_all('td')
data = [cell.get_text(strip=True) for cell in cells]
if len(data):
block_hash = data[0]
btc_earned = data[2].split()[0]
btc_fee = data[3].split()[0]
height, mmdddyyyy_str = fetch_block_details(block_hash)
btcusd = get_bitcoin_price_on_date(mmdddyyyy_str)
usd_earned = float(btc_earned) * float(btcusd)
usd_fee_paid = float(btc_fee) * float(btcusd)
output = (f"{height}, "
f"{mmdddyyyy_str}, "
f"${btcusd:.2f}, "
f"{btc_earned}, "
f"${usd_earned:0.2f}, "
f"{btc_fee}, "
f"{usd_fee_paid}")
output_data.append(output)
counter += 1 # incrementing the counter after each loop iteration
if counter == 3: # exit the loop when counter reaches three
break
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Had to change L38 from
utcfromtimestamp
to justdatetime_obj = datetime.datetime.fromtimestamp(timestamp)
because some newer version of python kept throwing horrible messages at me.