package jd_seckill import ( "errors" "fmt" "github.com/Albert-Zhan/httpc" "github.com/PuerkitoBio/goquery" "github.com/tidwall/gjson" "github.com/unknwon/goconfig" "github.com/ztino/jd_seckill/common" "github.com/ztino/jd_seckill/service" "log" "net/http" "strconv" "strings" "time" ) type Seckill struct { client *httpc.HttpClient conf *goconfig.ConfigFile } func NewSeckill(client *httpc.HttpClient, conf *goconfig.ConfigFile) *Seckill { return &Seckill{client: client, conf: conf} } func (this *Seckill) getUserAgent() string { return this.conf.MustValue("config", "default_user_agent", "") } func (this *Seckill) SkuTitle() (string, error) { skuId := this.conf.MustValue("config", "sku_id", "") req := httpc.NewRequest(this.client) resp, body, err := req.SetUrl(fmt.Sprintf("https://item.jd.com/%s.html", skuId)).SetMethod("get").Send().End() if err != nil || resp.StatusCode != http.StatusOK { log.Println("访问商品详情失败") return "", errors.New("访问商品详情失败") } html := strings.NewReader(body) doc, _ := goquery.NewDocumentFromReader(html) return strings.TrimSpace(doc.Find(".sku-name").Text()), nil } func (this *Seckill) MakeReserve() { user := NewUser(this.client, this.conf) userInfo, _ := user.GetUserInfo() log.Println("用户:" + userInfo) shopTitle, err := this.SkuTitle() if err != nil { log.Println("获取商品信息失败") } else { log.Println("商品名称:" + shopTitle) } skuId := this.conf.MustValue("config", "sku_id", "") req := httpc.NewRequest(this.client) req.SetHeader("User-Agent", this.getUserAgent()) req.SetHeader("Referer", fmt.Sprintf("https://item.jd.com/%s.html", skuId)) resp, body, err := req.SetUrl("https://yushou.jd.com/youshouinfo.action?callback=fetchJSON&sku=" + skuId + "&_=" + strconv.Itoa(int(time.Now().Unix()*1000))).SetMethod("get").Send().End() if err != nil || resp.StatusCode != http.StatusOK { log.Println("预约商品失败") } else { reserveUrl := gjson.Get(body, "url").String() req = httpc.NewRequest(this.client) _, _, _ = req.SetUrl("https:" + reserveUrl).SetMethod("get").Send().End() log.Println("预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约") } } func (this *Seckill) getSeckillUrl() (string, error) { skuId := this.conf.MustValue("config", "sku_id", "") req := httpc.NewRequest(this.client) req.SetHeader("User-Agent", this.getUserAgent()) req.SetHeader("Host", "itemko.jd.com") req.SetHeader("Referer", fmt.Sprintf("https://item.jd.com/%s.html", skuId)) req.SetUrl("https://itemko.jd.com/itemShowBtn?callback=jQuery" + strconv.Itoa(common.Rand(1000000, 9999999)) + "&skuId=" + skuId + "&from=pc&_=" + strconv.Itoa(int(time.Now().Unix()*1000))).SetMethod("get") url := "" for { _, body, _ := req.Send().End() //临时打印数据 log.Println("返回信息:"+body) var cbBody string cbBody = body spBody := strings.Split(body, "(") if len(spBody) >= 2 { cbBody = strings.Trim(spBody[1], ")") } if gjson.Get(cbBody, "url").Exists() && gjson.Get(cbBody, "url").String() != "" { url = gjson.Get(cbBody, "url").String() break } log.Println("抢购链接获取失败,稍后自动重试") time.Sleep(300 * time.Millisecond) } url = "https:"+url //https://divide.jd.com/user_routing?skuId=8654289&sn=c3f4ececd8461f0e4d7267e96a91e0e0&from=pc url = strings.ReplaceAll(url, "divide", "marathon") //https://marathon.jd.com/captcha.html?skuId=8654289&sn=c3f4ececd8461f0e4d7267e96a91e0e0&from=pc url = strings.ReplaceAll(url, "user_routing", "captcha.html") log.Println("抢购链接获取成功:" + url) return url, nil } func (this *Seckill) RequestSeckillUrl() { user := NewUser(this.client, this.conf) userInfo, _ := user.GetUserInfo() log.Println("用户:" + userInfo) shopTitle, err := this.SkuTitle() if err != nil { log.Println("获取商品信息失败") } else { log.Println("商品名称:" + shopTitle) } url, _ := this.getSeckillUrl() skuId := this.conf.MustValue("config", "sku_id", "") log.Println("访问商品的抢购连接...") req := httpc.NewRequest(this.client) req.SetHeader("User-Agent", this.getUserAgent()) req.SetHeader("Host", "marathon.jd.com") req.SetHeader("Referer", fmt.Sprintf("https://item.jd.com/%s.html", skuId)) _, _, _ = req.SetUrl(url).SetMethod("get").Send().End() } func (this *Seckill) SeckillPage() { log.Println("访问抢购订单结算页面...") skuId := this.conf.MustValue("config", "sku_id", "") seckillNum := this.conf.MustValue("config", "seckill_num", "") req := httpc.NewRequest(this.client) req.SetHeader("User-Agent", this.getUserAgent()) req.SetHeader("Host", "marathon.jd.com") req.SetHeader("Referer", fmt.Sprintf("https://item.jd.com/%s.html", skuId)) _, _, _ = req.SetUrl("https://marathon.jd.com/seckill/seckill.action?skuId=" + skuId + "&num=" + seckillNum + "&rid=" + strconv.Itoa(int(time.Now().Unix()))).SetMethod("get").Send().End() } func (this *Seckill) SeckillInitInfo() (string, error) { log.Println("获取秒杀初始化信息...") skuId := this.conf.MustValue("config", "sku_id", "") seckillNum := this.conf.MustValue("config", "seckill_num", "") req := httpc.NewRequest(this.client) req.SetHeader("User-Agent", this.getUserAgent()) req.SetHeader("Host", "marathon.jd.com") req.SetData("sku", skuId) req.SetData("num", seckillNum) req.SetData("isModifyAddress", "false") resp, body, err := req.SetUrl("https://marathon.jd.com/seckillnew/orderService/pc/init.action").SetMethod("post").Send().End() if err != nil || resp.StatusCode != http.StatusOK { log.Println("初始化秒杀信息失败") return "", errors.New("初始化秒杀信息失败") } if !gjson.Valid(body) { log.Println("抢购失败,返回信息:" + body) return "", errors.New("抢购失败,返回信息:" + body) } return body, nil } func (this *Seckill) SubmitSeckillOrder() bool { eid := this.conf.MustValue("config", "eid", "") fp := this.conf.MustValue("config", "fp", "") skuId := this.conf.MustValue("config", "sku_id", "") seckillNum := this.conf.MustValue("config", "seckill_num", "") paymentPwd := this.conf.MustValue("account", "payment_pwd", "") initInfo, err := this.SeckillInitInfo() if err != nil { log.Println(fmt.Sprintf("抢购失败,无法获取生成订单的基本信息,接口返回:【%s】", err.Error())) return false } address := gjson.Get(initInfo, "addressList").Array() defaultAddress := address[0] isinvoiceInfo := gjson.Get(initInfo, "invoiceInfo").Exists() invoiceTitle := "-1" invoiceContentType := "-1" invoicePhone := "" invoicePhoneKey := "" invoiceInfo := "false" if isinvoiceInfo { invoiceTitle = gjson.Get(initInfo, "invoiceInfo.invoiceTitle").String() invoiceContentType = gjson.Get(initInfo, "invoiceInfo.invoiceContentType").String() invoicePhone = gjson.Get(initInfo, "invoiceInfo.invoicePhone").String() invoicePhoneKey = gjson.Get(initInfo, "invoiceInfo.invoicePhoneKey").String() invoiceInfo = "true" } token := gjson.Get(initInfo, "token").String() log.Println("提交抢购订单...") req := httpc.NewRequest(this.client) req.SetHeader("User-Agent", this.getUserAgent()) req.SetHeader("Host", "marathon.jd.com") req.SetHeader("Referer", fmt.Sprintf("https://marathon.jd.com/seckill/seckill.action?skuId=%s&num=%s&rid=%d", skuId, seckillNum, int(time.Now().Unix()))) req.SetData("skuId", skuId) req.SetData("num", seckillNum) req.SetData("addressId", defaultAddress.Get("id").String()) req.SetData("yuShou", "true") req.SetData("isModifyAddress", "false") req.SetData("name", defaultAddress.Get("name").String()) req.SetData("provinceId", defaultAddress.Get("provinceId").String()) req.SetData("cityId", defaultAddress.Get("cityId").String()) req.SetData("countyId", defaultAddress.Get("countyId").String()) req.SetData("townId", defaultAddress.Get("townId").String()) req.SetData("addressDetail", defaultAddress.Get("addressDetail").String()) req.SetData("mobile", defaultAddress.Get("mobile").String()) req.SetData("mobileKey", defaultAddress.Get("mobileKey").String()) req.SetData("email", defaultAddress.Get("email").String()) req.SetData("postCode", "") req.SetData("invoiceTitle", invoiceTitle) req.SetData("invoiceCompanyName", "") req.SetData("invoiceContent", invoiceContentType) req.SetData("invoiceTaxpayerNO", "") req.SetData("invoiceEmail", "") req.SetData("invoicePhone", invoicePhone) req.SetData("invoicePhoneKey", invoicePhoneKey) req.SetData("invoice", invoiceInfo) req.SetData("password", paymentPwd) req.SetData("codTimeType", "3") req.SetData("paymentType", "4") req.SetData("areaCode", "") req.SetData("overseas", "0") req.SetData("phone", "") req.SetData("eid", eid) req.SetData("fp", fp) req.SetData("token", token) req.SetData("pru", "") resp, body, err := req.SetUrl("https://marathon.jd.com/seckillnew/orderService/pc/submitOrder.action?skuId=" + skuId).SetMethod("post").Send().End() if err != nil || resp.StatusCode != http.StatusOK { log.Println("抢购失败,网络错误") _ = service.SendMessage(this.conf, "茅台抢购通知", "抢购失败,网络错误") return false } if !gjson.Valid(body) { log.Println("抢购失败,返回信息:" + body) _ = service.SendMessage(this.conf, "茅台抢购通知", "抢购失败,返回信息:"+body) return false } if gjson.Get(body, "success").Bool() { orderId := gjson.Get(body, "orderId").String() totalMoney := gjson.Get(body, "totalMoney").String() payUrl := "https:" + gjson.Get(body, "pcUrl").String() log.Println(fmt.Sprintf("抢购成功,订单号:%s, 总价:%s, 电脑端付款链接:%s", orderId, totalMoney, payUrl)) _ = service.SendMessage(this.conf, "茅台抢购通知", fmt.Sprintf("抢购成功,订单号:%s, 总价:%s, 电脑端付款链接:%s", orderId, totalMoney, payUrl)) return true } else { log.Println("抢购失败,返回信息:" + body) _ = service.SendMessage(this.conf, "茅台抢购通知", "抢购失败,返回信息:"+body) return false } }