使用Python3与OpenSea API获取NFT集合完整持有者列表及对应ETH地址
Opensea Aethir Wars NFT持有者列表爬取问题修复
问题描述
尝试获取Opensea.io上Aethir Wars NFT集合的持有者列表,所需数据包含持有人信息、所持NFT详情及对应的ETH地址。目前已能获取到部分持有者及其地址,但输出列表存在缺失不全的问题。另外由于未经过购买直接转账的NFT的Last_Sale参数为null,已额外添加if判断处理该场景,需要解决列表不全的问题。
现有问题代码
import requests url = "https://api.opensea.io/api/v1/assets" api_key = "api key" col = 'reapers' def print_owners(race, offset): querystring = {'order_by': 'name', 'order_direction': 'asc', 'offset': offset, 'limit': '50', 'collection': col, 'X-API-KEY': api_key} response = requests.get(url, params=querystring) data = response.json() i = 0 for each_asset in data['assets']: try: if race in data['assets'][0]['name']: if data["assets"][i]["last_sale"] is None: print(data["assets"][i]["name"] + ': ' + data["assets"][i]["sell_orders"][0]["maker"]["address"]) else: print(data["assets"][i]["name"] + ': ' + data["assets"][i]["last_sale"]["transaction"]["from_account"]["user"]["username"]) except TypeError: pass i += 1 x = range(0, 600, 50) for stuff in x: try: print_owners(race='Reaper', offset=str(stuff)) except KeyError: pass try: print_owners(race='Android', offset=str(stuff)) except KeyError: pass try: print_owners(race='Goblin', offset=str(stuff)) except KeyError: pass
数据缺失的核心原因
- 逻辑判断错误:原代码用
if race in data['assets'][0]['name']判断整页数据是否符合种族要求,只要当前页第一个资产不属于对应种族,整页50条数据全部跳过,直接丢失大量符合要求的条目 - 取数逻辑错误:没有直接使用接口返回的
owner字段获取当前持有者地址,反而绕路去取sell_orders(只有挂单的资产才有该字段,没挂单的直接报错跳过)和last_sale的转出方(这是上次交易的卖家,不是当前持有者,数据完全不对) - 接口调用格式错误:Opensea v1 API要求
X-API-KEY放在请求头中,原代码把key放在查询参数里,会触发接口限流或者返回不完整数据 - 异常捕获过于宽泛:所有报错直接静默跳过,无法排查漏数原因
- 分页范围不足:仅遍历0-600的offset,如果集合NFT总量超过600,超出部分完全无法获取
修复方案
直接调用接口返回的标准owner字段获取当前持有者信息,修正判断逻辑和参数传递规则,优化异常捕获:
import requests url = "https://api.opensea.io/api/v1/assets" api_key = "替换为你自己的API_KEY" col = 'reapers' # 可根据集合实际总量调整最大offset MAX_OFFSET = 2000 def print_owners(race, offset): querystring = { 'order_by': 'name', 'order_direction': 'asc', 'offset': offset, 'limit': '50', 'collection': col } # API KEY放到请求头 headers = {"X-API-KEY": api_key} response = requests.get(url, params=querystring, headers=headers) # 先判断接口返回状态 if response.status_code != 200: print(f"接口请求失败,状态码{response.status_code},offset={offset}") return data = response.json() if 'assets' not in data: return for each_asset in data['assets']: try: # 对当前遍历的资产做种族判断,不是整页第一个 asset_name = each_asset.get('name', '') if race in asset_name: # 直接取当前持有者地址,不受last_sale、sell_orders影响 owner_addr = each_asset['owner']['address'] # 可以额外取owner的用户名,没有的话留空 owner_name = each_asset['owner'].get('user', {}).get('username', '无用户名') print(f"{asset_name}: 持有者地址{owner_addr}, 用户名{owner_name}") except Exception as e: # 打印错误方便排查,不要直接跳过 print(f"处理资产{each_asset.get('id', '未知ID')}出错: {str(e)}") if __name__ == '__main__': race_list = ['Reaper', 'Android', 'Goblin'] for offset in range(0, MAX_OFFSET, 50): for race in race_list: print_owners(race=race, offset=offset)
内容的提问来源于stack exchange,提问作者Luke Puller
相关产品推荐
相关产品推荐

