目录
总结
前言
buff平台的商品价格有较大的波动范围,在特定的时间段(寒暑假等)还会有大幅的增长,如何选择低价商品进行购入,在高价时卖出,博取相当收益。buff提供的历史价格数据可以作为重要的参考依据。
这篇文章主要介绍了如何使用python获取buff商品历史价格,通过计算,对比当前价格和历史价格的大小,预警低价商品。
一、如何获取低价标准
在buff中,每个商品都存在价格趋势,普通用户可以获取一个月内商品的60个价格点(每天2个价格点),充值plus会员后,甚至可以获取6个月的历史价格数据。虽然价格不是连续线性的,但是也具有一定的参考价值。题主没有充会员,就用1个月作为参考哈哈。
buff价格趋势曲线图
二、代码实现
1.引入库
import re
import requests as req
import time,random,winsound,sendmessage,numpy as np
from datetime import datetime
class CS():
def __init__(self):
self.nowtime = datetime.now()
self.goodsurl = 'https://buff.163.com/api/market/goods'
self.pricehistoryurl = 'https://buff.163.com/api/market/goods/price_history/buff'
self.my_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36",
"Cookie": "yourcookie",#你自己的cookie
"Host": "buff.163.com",
"Referer": "https://buff.163.com/market/?game=csgo",
"X-Requested-With": "XMLHttpRequest"
}
这里用到的两个api
self.goodsurl = 'https://buff.163.com/api/market/goods'
self.pricehistoryurl = 'https://buff.163.com/api/market/goods/price_history/buff'
分别是查找符合商品的api和获取历史价格的api
2.获取商品数据
def SearchGoods(self, weaponame): # 搜索符合价格标准的weaponlist商品,返回商品id等数据
nowtime = self.nowtime.timestamp()
##############参数调整################
param = {
'game': 'csgo',
'page_size': 80,
'page_num': 100,
'quality':'normal',
'min_price': 20, # 最小价格
'max_price': 221.93, # 最大价格
'category': weaponame,
}
datalist = []
res = req.get(url=self.goodsurl, headers=self.my_headers, params=param)
#print(res.text)
data = res.json()['data']
datalist.extend(data['items'])
#print(data)
print('商品页数'+str(data['total_page']))
#buff页码显示有问题
if int(data['total_page']) > 1:
for i in range(1, int(data['total_page'])):
time.sleep(random.randint(10, 20))
param.update({
'page_num': i
})
res = req.get(url=self.goodsurl, headers=self.my_headers, params=param)
data = res.json()['data']
datalist.extend(data['items'])
print(weaponame + '数据获取成功') # 打印数据列表
return datalist
参数调整再param中,商品价格按照自己的需求填写,quality指的是普通,StatTrak带击杀记录等等,这个api可以选择的参数还有其他,在返回的数据中有一个:
"goods_info": { "info": { "tags": {
在这个tags中的category都是可以加进去的参数。
这个地方需要特别解释的是:buff的页码显示有bug,如果你用商品第1页请求数据,会给你返回一个不是实际数量的商品,往往比实际符合要求的商品大很多,页码也会大很多,所以这里用一个page_num很大的值100,首先将最后一页请求出来,就能正确显示实际商品数量,不知道有没有大佬知道这个问题出自哪里,是不是buff自身的bug还是说请求方式和代码的问题。
总的来说,就是先请求最后一页,获取总页码数量total_page后再请求1至倒数第二页。
3.检索商品历史价格
def GethistoryPrice(self,goodsdata):
param = {
'game': 'csgo',
'goods_id':'',
'currency': 'CNY',
'days':'30',
'buff_price_type': '1',
'with_sell_num': 'false',
'_':''
}
nowtime = self.nowtime.timestamp()
param.update({'goods_id':goodsdata['id'],
'_':nowtime}) # 参数加入id和时间戳
res = req.get(url=self.pricehistoryurl, headers=self.my_headers, params=param)
#print(res.content)
price_history = res.json()['data']['price_history']
print('历史价格获取成功')
return price_history
根据goods_id检索每个商品的历史价格,会返回一个price_history的列表,包括了60个价格和日期,但是不是每一个商品都有60个价格,毕竟有的商品销量很低。
这里需要说的是:param中的days我这里用的30天,如果你买了plus会员或者用积分兑换的历史价格,你的cookie是可以改成60的。
4.计算低价商品
def OperaData(self,pricelist,nowprice):
data=[]
for item in pricelist:
data.append(item[1])
# 计算四分位数
#print(data)
Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75)
IQR = Q3 - Q1
# 剔除超出Q1-1.5*IQR和Q3+1.5*IQR的值
filtered_data = [x for x in data if Q1 - 1.5 * IQR <= x <= Q3 + 1.5 * IQR]
#print(filtered_data)
npdata = np.array(filtered_data)
# 价格线设置——————————————————————历史价格的8%
priceline = np.percentile(npdata, 8)
print('价格线:' + str(priceline))
print('当前价位:' + str(nowprice))
weight=1/(float(nowprice)-float(priceline))
print('权重',str(weight))
if weight <= 0:
return 1, float(priceline),100
else:
return 2, float(priceline),weight
这里的总体思路是,将获取的历史数据列表计算分位数(即第百分之几的数,比如有100个数,这100个数的第10小的数就是这组数的10分位数) ,这个分位数可以自己填,我填的8,就是低于历史价格的8%,如果填的越小,说明这个商品的价格越接近最低价。
这里需要特别说明的是:上面使用了一个方法来筛选异常值,因为在buff中,经常会有一些比正常价格高出3-4倍非常离谱的价格,可能是用来倒余额等其他的价格,需要剔除异常值。
异常价格
使用代码filtered_data = [x for x in data if Q1 - 1.5 * IQR <= x <= Q3 + 1.5 * IQR]将异常值剔除。
权重weight是用于以后的其他计算方式,暂时可以不管。
5.通知及显示
def judgepirce(self,item):
print('★★★★★★★★★★')
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
print(str(item['name']))
try:
historypirce = self.GethistoryPrice(item)
if len(historypirce) > 55:
buyok, Q1, weight = self.OperaData(historypirce, item['sell_min_price'])
if buyok==1:
print('———————————价格较低,可以入手————————————')
print('https://buff.163.com/goods/'+str(item['id']))
winsound.PlaySound("output.wav", winsound.SND_FILENAME)
item.update({
'buyok':buyok,
'priceline':Q1,
'weight':weight
})
return item
else:
print('交易量过少')
item.update({
'buyok': 3,
'weight': 0.01
})
return item
except Exception as e:
print('获取历史价格失败', str(e))
item.update({
'buyok': 4,
'weight': 10
})
return item
这段代码主要是判断和通知(声音提示),加入了一个筛选功能,如果一个月的价格没有大于55个即认为该商品交易量过低,不再判断价格,因为交易不活跃的商品不好出手。
6.执行主程序
if __name__ == '__main__':
# weaponlist = [ 'weapon_awp','weapon_usp_silencer', 'weapon_ak47', 'weapon_glock','weapon_deagle','weapon_m4a1', 'weapon_m4a1_silencer', ]
weaponlist = ['weapon_revolver','weapon_bayonet', 'weapon_bizon', 'weapon_sg556', 'weapon_scar20', 'weapon_p250',
'weapon_m4a1', 'weapon_cz75a', 'weapon_aug','weapon_usp_silencer',
'weapon_xm1014', 'weapon_elite','weapon_sawedoff','weapon_nova','weapon_famas', 'weapon_fiveseven', 'weapon_g3sg1', 'weapon_galilar',
'weapon_glock', 'weapon_hkp2000', 'weapon_m249', 'weapon_mac10', 'weapon_mag7',
'weapon_mp5sd', 'weapon_mp7', 'weapon_mp9', 'weapon_negev', 'weapon_ssg08', 'weapon_zeus',
'weapon_tec9', 'weapon_ump45', 'weapon_ak47', 'weapon_m4a1_silencer', 'weapon_awp', 'weapon_deagle','weapon_p90']
test = CS()
list=[]
for i in weaponlist:#获取商品信息
data=test.SearchGoods(i)
list.extend(data)
time.sleep(random.randint(10, 60))
while True:#判断商品价格
for i in list:
test.judgepirce(i)
time.sleep(random.randint(10, 60))
首先请求符合价格要求的商品,再判断价格线并通知,使用一个无限循环,随机变动重复的时间time.sleep(random.randint(10, 60)),太短会被buff屏蔽。
后期可以在判断价格增加自动购买的代码,目前思路是使用selenium自动化浏览器控制,有没有大佬有更好的方法,欢迎交流提建议。
总结
以上就是今天的内容,本文仅仅使用了简单的方法自动爬取商品的价格并判断低价商品,需要特别说明的是:虽然系统自动找出低价商品,但还是要人为判断是否值得购买,因为有些商品价格波动特别小,整体可能就只有1块钱以内的波动,那怕它现在是1毛钱低价,也最多只能赚9毛钱,是不值得购买的。