├── img ├── __init__.py └── demo01.png ├── python使用命令.md ├── readme.md ├── .gitignore └── jd.py /img/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/demo01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeeeeeeek/jd_qianggou/HEAD/img/demo01.png -------------------------------------------------------------------------------- /python使用命令.md: -------------------------------------------------------------------------------- 1 | ### 导出所有依赖 2 | pip freeze > requirements.txt 3 | 4 | ### 安装 5 | pip install -r requirements.txt 6 | 7 | 8 | ### pyinstaller使用 9 | 10 | -F 表示生成单个可执行文件; 11 | 12 | -D  –onedir 创建一个目录,包含exe文件,但会依赖很多文件(默认选项)。 13 | 14 | -w 表示去掉控制台窗口,这在GUI界面时非常有用。不过如果是命令行程序的话那就把这个选项删除吧!; 15 | 16 | -c  –console, –nowindowed 使用控制台,无界面(默认); 17 | 18 | -p 表示你自己自定义需要加载的类路径,一般情况下用不到; 19 | 20 | -i 表示可执行文件的图标。 21 | 22 | 23 | pyinstaller -F -w xx.py 24 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### 程序入口 2 | ``` 3 | python jd.py 4 | ``` 5 | 6 | 7 | ### 前置步骤:清空购物车,减少其他商品干扰 8 | ### 总体步骤如下:获取cookie->获取商品ID->设置抢购时间和数量->开始抢单 9 | 10 | ### 第一步:获取cookie 11 | 登录pc版JD商城,进入购物车,打开控制台,查看接口 12 | https://api.m.jd.com/api?functionId=pcCart_jc_getCurrentCart 13 | 的cookie,填入cookie的输入框 14 | 15 | ### 第二步:获取商品id 16 | 找到需要抢购的商品,查看详情,可以看到浏览器的URL为 17 | https://item.jd.com/xxxxxxxx.html, 18 | 复制xxxxxxxx填入商品ID框, 19 | 20 | ### 第三步:设置抢购时间和数量 21 | 抢购时间需要格式设置,格式为yyyy-MM-dd HH:mm:ss,如2021-11-02 12:00:00。数量可随意设置,但需要考虑到店铺的库存以及是否设置为限购1件。 22 | 23 | ### 第四步:开始抢单 24 | 这个时候可以泡一杯茶,静待好事发生。 25 | 26 | 即可 27 | 28 | **效果预览** 29 | 30 | ![](https://github.com/geeeeeeeek/jd_qianggou/blob/master/img/demo01.png) 31 | 32 | **问题咨询** 33 | 34 | 微信:lengqin1024 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | .idea 9 | .vscode 10 | .DS_Store 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # SageMath parsed files 85 | *.sage.py 86 | 87 | # Environments 88 | .env 89 | .venv 90 | env/ 91 | venv/ 92 | ENV/ 93 | env.bak/ 94 | venv.bak/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | /site 105 | 106 | # mypy 107 | .mypy_cache/ 108 | 109 | .vscode 110 | -------------------------------------------------------------------------------- /jd.py: -------------------------------------------------------------------------------- 1 | # 京东购物车下单 2 | # 程序流程:全选购物车->确认订单->下单 3 | # -*- coding=UTF-8 -*- 4 | import datetime 5 | import json 6 | import threading 7 | import time 8 | import tkinter.messagebox 9 | from tkinter import * 10 | 11 | import requests 12 | 13 | ApiUrls = { 14 | 'getProductInfos': 15 | 'https://api.m.jd.com/api?functionId=pcCart_jc_getCurrentCart&appid=JDC_mall_cart&loginType=3&body={"serInfo":{"area":"13_1000_40488_54435","user-key":"78f2fbb3-610e-4057-bee3-eafe10da0f8f"},"cartExt":{"specialId":1}}', 16 | 'addToCart': 17 | 'https://cart.jd.com/gate.action?pid={pId}&pcount={count}&ptype=1', 18 | 'getOrderInfo': 19 | 'https://trade.jd.com/shopping/order/getOrderInfo.action?overseaMerge=1', 20 | 'submitOrder': 21 | 'https://trade.jd.com/shopping/order/submitOrder.action?overseaMerge=1&presaleStockSign=1&overseaPurchaseCookies=&vendorRemarks={vendorRemarks}&submitOrderParam.sopNotPutInvoice=true&submitOrderParam.trackID=TestTrackId&overseaMerge=1&submitOrderParam.ignorePriceChange=0&submitOrderParam.btSupport=0&submitOrderParam.eid=CH2AWTZNVCRPTFNPJQT2SZTJ5PSK7EV4TOQB7V5CCSVCUIOQ3K5ZT5S62PYV4V4YI4Z7EUXLNLKETTZBKFJ5J6WSO4&submitOrderParam.fp=33eaf773494fe391925ae6df450d557a&submitOrderParam.jxj=1', 22 | 'checkAllOfCart': 'https://api.m.jd.com/api?functionId=pcCart_jc_cartCheckAll&appid=JDC_mall_cart&loginType=3' 23 | } 24 | 25 | 26 | cookies = { 27 | 'shshshfpa': 'b73fcd0a-e748-3867-db73-a1a3173b9878-1532745038', 28 | 'shshshfpb': '297f476594eaa487d83c7b4cb635aa1bd54b360e225a786a73afd6e3b0', 29 | '__jdu': '14990763208811196468013', 30 | 'jcap_dvzw_fp': 'Dk6rZeuLxPLoJJsdAHum8XOefzbLrR1LCBMKdTBoazGSG4Tq6NcVM2LWQpG2Jty7j5Bg5g==', 31 | 'whwswswws': '', 32 | 'TARGET_UNIT': 'bjcenter', 33 | 'pinId': '1DFZEXqqCEKuTTSad7TecA', 34 | 'ceshi3.com': '000', 35 | '__jdv': '76161171|direct|-|none|-|1646962875936', 36 | 'areaId': '13', 37 | 'ipLoc-djd': '13-1000-0-0', 38 | 'PCSYCityID': 'CN_370000_370100_0', 39 | 'TrackID': '14EkOK78VlS4TsusWJ3KrUWZ5OJ-s0Vzf0-S2ynF6AKUSeZ1ZA21NLV4QEY2kqUvGJ_oR-tWghu7Xzy98fZBGeakKkE_NQjkOVDXT6VO81v0DXe2UHyPqtAIpYIrBMRdv', 40 | 'thor': 'CFBCF332498D096CCB7570306153F76A936F3D67B40701C976DACC6D49E33E495695E6FC9F10459DBEBA26242D51A9D0CB921BA5601279DAE729BFC2ABD8796E991AE686706AC7EF4307C17FE2A5E919983942BBDDF82DD54DA0023C094A5AD10464F352B5FE797A6478B9C52A8D88743C5B46B3A9B694EB2EB3454197E02C687984A382E3CFCD712C727B9AC53F894A084038B2ED7CE34ACE21E60C64540765', 41 | 'pin': 'jd_sJovPmbhzNqJ', 42 | 'unick': 'jd_sJovPmbhzNqJ', 43 | '_tp': '40NjtDUMie4wthDN5S8bNg%3D%3D', 44 | '_pst': 'jd_sJovPmbhzNqJ', 45 | 'shshshfp': 'e12c803179a9671ef026e413574459a2', 46 | '__jda': '122270672.14990763208811196468013.1499076321.1644753203.1646962876.436', 47 | '__jdb': '122270672.5.14990763208811196468013|436.1646962876', 48 | '__jdc': '122270672', 49 | '3AB9D23F7A4B3C9B': 'AYGPYJLVRTLAQIUTZ7I4JLRTE42V4DIV7BW6KSA5GYZCY3CL2ISYTJS3GHMR7VJZYMISFYV7NXSBSAIK3EN4CZR66Y', 50 | 'shshshsID': 'fb8e36f4785ba40025bff0ee82250ebb_3_1646962953668', 51 | 'cn': '0', 52 | 'user-key': '78f2fbb3-610e-4057-bee3-eafe10da0f8f', 53 | } 54 | 55 | 56 | 57 | 58 | def getStrFromCookie(cookies): 59 | ck = '' 60 | for key in cookies: 61 | ck = ck + key + '=' + cookies[key] + ';' 62 | return ck 63 | 64 | 65 | def make_app(): 66 | app = Tk() 67 | app.geometry('600x450') 68 | app.title('京东加车抢') 69 | Label(app, text='cookie').place(relx=0.1, rely=0.05) 70 | cookieStr = StringVar() 71 | ck = getStrFromCookie(cookies) 72 | cookieStr.set(ck) 73 | Entry(app, textvariable=cookieStr, name='cookie').place(relx=0.1, 74 | rely=0.1, 75 | relwidth=0.8, 76 | relheight=0.15) 77 | 78 | Label(app, text='输入抢单时间').place(relx=0.1, rely=0.3) 79 | timeStr = StringVar() 80 | timeStr.set(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')) 81 | Entry(app, textvariable=timeStr, name='ipt').place(relx=0.1, 82 | rely=0.35, 83 | relwidth=0.3, 84 | relheight=0.1) 85 | 86 | Label(app, text='商品ID').place(relx=0.45, rely=0.3) 87 | pId = StringVar() 88 | # pId.set('2148924,10026899941091,100013490678') # 89 | pId.set('10043811965012,100034240884') # sku 90 | Entry(app, textvariable=pId, name='ipt1').place(relx=0.45, 91 | rely=0.35, 92 | relwidth=0.2, 93 | relheight=0.1) 94 | 95 | Label(app, text='数量').place(relx=0.7, rely=0.3) 96 | count = StringVar() 97 | count.set('1') 98 | Entry(app, textvariable=count, name='ipt2').place(relx=0.7, 99 | rely=0.35, 100 | relwidth=0.2, 101 | relheight=0.1) 102 | 103 | Button(app, text='点击开始抢单', fg="black", bg="white", 104 | command=orderThread).place(relx=0.1, 105 | rely=0.5, 106 | relwidth=0.8, 107 | relheight=0.1) 108 | 109 | Text(app, name="runningText").place(relx=0.1, 110 | rely=0.65, 111 | relwidth=0.8, 112 | relheight=0.3) 113 | runningText = app.children['runningText'] 114 | runningText.insert(0.0, '\n抢单步骤:') 115 | runningText.insert( 116 | END, 117 | '\n第一步:网页登录京东,查看购物车下https://api.m.jd.com/api?functionId=pcCart_jc_getCurrentCart的cookie,填入cookie的输入框' 118 | ) 119 | runningText.insert(END, '\n第二步:手动添加商品进购物车(注意限购数量),并将商品ID,填入商品ID输入框') 120 | 121 | return app 122 | 123 | 124 | def orderThread(): 125 | th = threading.Thread(target=checkCartAndSubmit) 126 | th.start() 127 | 128 | 129 | def checkCartAndSubmit(): 130 | cookie = app.children['cookie'].get() 131 | s = requests.Session() 132 | if cookie == '': 133 | tkinter.messagebox.showinfo( 134 | '错误', 135 | '请网页登录京东,查看购物车下https://api.m.jd.com/api?functionId=pcCart_jc_getCurrentCart的cookie,填入下面' 136 | ) 137 | return 138 | 139 | # 全选购物车请求头 140 | checkCartHeaders = { 141 | 'Cookie': cookie, 142 | 'Accept': 'application/json, text/javascript, */*; q=0.01', 143 | 'origin': 'https://cart.jd.com', 144 | 'referer': 'https://cart.jd.com/', 145 | 'user-agent': 146 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36', 147 | } 148 | 149 | # 请求头 150 | tradeHeaders = { 151 | 'Cookie': cookie, 152 | 'Accept': 'application/json, text/javascript, */*; q=0.01', 153 | 'path': '/shopping/order/submitOrder.action?', 154 | 'origin': 'https://trade.jd.com', 155 | 'referer': 'https://trade.jd.com/shopping/order/getOrderInfo.action', 156 | 'x-requested-with': 'XMLHttpRequest', 157 | 'user-agent': 158 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36', 159 | 'authority': 'trade.jd.com', 160 | 'method': 'post', 161 | 'scheme': 'https', 162 | } 163 | cartInfoheaders = { 164 | 'Cookie': cookie, 165 | 'Accept': 'application/json, text/javascript, */*; q=0.01', 166 | 'origin': 'https://cart.jd.com', 167 | 'referer': 'https://cart.jd.com', 168 | 'x-requested-with': 'XMLHttpRequest', 169 | 'user-agent': 170 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36', 171 | 'authority': 'api.m.jd.com', 172 | 'method': 'post', 173 | 'scheme': 'https', 174 | } 175 | 176 | now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') 177 | setTime = app.children['ipt'].get() 178 | # if now > setTime: 179 | # tkinter.messagebox.showinfo('错误', '设置时间要超过当前时间') 180 | # return 181 | 182 | # 轮询时间 183 | timeCut = 0.5 184 | 185 | pIds = app.children['ipt1'].get() 186 | # count = app.children['ipt2'].get() 187 | # 查询商品信息 188 | pInfoRes = s.get(ApiUrls['getProductInfos'], 189 | headers=cartInfoheaders, verify=False).json() 190 | vendors = [] 191 | if pInfoRes['success'] and pInfoRes['resultData']['cartInfo'] is not None: 192 | vendors = pInfoRes['resultData']['cartInfo']['vendors'] 193 | else: 194 | tkinter.messagebox.showinfo('提示', '请把商品pIds加入购物车') 195 | 196 | vendorRemarks = [] 197 | 198 | # 查找购买的商品的vendorId 199 | for vendor in vendors: 200 | for item in vendor['sorted']: 201 | if len(item['item']['items']) > 0: 202 | for iitem in item['item']['items']: 203 | if pIds.find(str(iitem['item']['Id'])) > -1: 204 | vendorRemarks.append({ 205 | "vendorId": 206 | str(vendor['vendorId']), 207 | "remark": 208 | "" 209 | }) 210 | break 211 | else: 212 | if pIds.find(str(item['item']['Id'])) > -1: 213 | vendorRemarks.append({ 214 | "vendorId": 215 | str(vendor['vendorId']), 216 | "remark": 217 | "" 218 | }) 219 | break 220 | 221 | for vendor in vendorRemarks: 222 | if vendor['vendorId'] == '8888': 223 | del vendorRemarks[vendorRemarks.index(vendor)] 224 | 225 | while True: 226 | runningText = app.children['runningText'] 227 | 228 | currentTime = datetime.datetime.now() 229 | 230 | if currentTime.strftime('%Y-%m-%d %H:%M:%S.%f') >= setTime: 231 | 232 | for i in range(3): 233 | submitResult = reSubmitOrder(s, checkCartHeaders, tradeHeaders, vendorRemarks, runningText) 234 | if submitResult: 235 | break 236 | runningText.insert(0.0, '\n第' + str(i) + '次抢单结束----' + datetime.datetime.now().strftime( 237 | '%Y-%m-%d %H:%M:%S.%f')) 238 | time.sleep(0.1) 239 | 240 | break 241 | 242 | leftSec = (datetime.datetime.strptime(setTime, "%Y-%m-%d %H:%M:%S.%f") - currentTime).seconds 243 | runningText.insert(0.0, '\n倒计时:' + str(leftSec) + '秒------' + currentTime.strftime('%Y-%m-%d %H:%M:%S.%f')) 244 | if leftSec <= 1: 245 | time.sleep(0.05) 246 | else: 247 | time.sleep(timeCut) 248 | 249 | 250 | def reSubmitOrder(s, checkCartHeaders, tradeHeaders, vendorRemarks, runningText): 251 | try: 252 | return _doSubmitOrder(s, checkCartHeaders, tradeHeaders, vendorRemarks, runningText) 253 | except Exception as e: 254 | runningText.insert(0.0, '\n程序异常:' + repr(e)) 255 | 256 | # 默认false 257 | return False 258 | 259 | 260 | def _doSubmitOrder(s, checkCartHeaders, tradeHeaders, vendorRemarks, runningText): 261 | start_time = int(datetime.datetime.now().timestamp() * 1000) 262 | 263 | # 全选购物车 264 | checkAllOfCartUrl = ApiUrls['checkAllOfCart'] 265 | checkAllofCartRes = s.get(checkAllOfCartUrl, headers=checkCartHeaders, verify=False) 266 | if checkAllofCartRes.json().get('resultData').get('cartInfo').get('checkedWareNum', 0) <= 0: 267 | return False 268 | 269 | # 确认订单 270 | getOrderInfoUrl = ApiUrls['getOrderInfo'] 271 | getOrderInfoRes = s.get(getOrderInfoUrl, headers=tradeHeaders, allow_redirects=False, verify=False) 272 | # runningText.insert(0.0, '\n确认订单时间:' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')) 273 | 274 | # 下单 275 | if getOrderInfoRes.status_code == 200: 276 | submitOrderUrl = ApiUrls['submitOrder'].format(vendorRemarks=json.dumps(vendorRemarks, separators=(',', ':'))) 277 | submitOrderRes = s.get(submitOrderUrl, headers=tradeHeaders, allow_redirects=False, verify=False) 278 | runningText.insert(0.0, '\n下单完成时间:' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')) 279 | message = '' 280 | if submitOrderRes.json()['orderId'] != 0: 281 | message = '抢单成功' 282 | runningText.insert(0.0, '\n下单结果:' + message) 283 | return True 284 | else: 285 | message = submitOrderRes.json()['message'] 286 | runningText.insert(0.0, '\n下单接口message:' + message) 287 | 288 | end_time = int(datetime.datetime.now().timestamp() * 1000) 289 | 290 | print('diff=' + str(end_time - start_time)) 291 | # 默认false 292 | return False 293 | 294 | 295 | app = make_app() 296 | app.mainloop() 297 | --------------------------------------------------------------------------------