)
簡介這是一份面向普通消費者的京東網頁端搶購輔助工具以搶購茅臺等熱門商品為典型場景采用JavaScript開發(fā)并封裝為Chrome及360等兼容Chromium內核瀏覽器的插件形式顯著降低使用門檻——無需安裝Python環(huán)境或編寫代碼適合對編程了解有限但希望提升搶購成功率的用戶。資源包共8個文件含核心邏輯腳本index.js、jQuery依賴庫、插件配置清單manifest.json、前端交互頁面index.html、圖標資源png及開源協(xié)議、說明文檔等結構完整、即裝即用整體僅60KB輕量易部署。已有6192人學習下載反映出其在實際搶購場景中的廣泛參考價值。讀者可直接復用該插件框架理解網頁自動點擊、倒計時監(jiān)聽與DOM動態(tài)操作等關鍵實現(xiàn)思路并基于README.md和項目結構快速掌握開發(fā)邏輯與適配方法。1. 為什么“ rush_to_purchase”不是玄學而是可復現(xiàn)的京東搶購確定性動作“rush_to_purchase可能是最簡單的京東搶購腳本了以搶茅臺為例”——這個標題里藏著三重真實訴求第一用戶要的不是“能跑就行”的玩具代碼而是在京東商品秒殺真實鏈路中能穩(wěn)定觸發(fā)下單動作的最小可行單元第二“最簡單”不是指刪減邏輯而是把登錄態(tài)維持、庫存輪詢、按鈕狀態(tài)識別、提交防抖這四個黑匣子壓縮成不到200行可讀、可調、可斷點的Python邏輯第三“以搶茅臺為例”是典型場景錨點它意味著高并發(fā)下頁面結構穩(wěn)定京東自營、SKU固定53度飛天500ml、限購嚴格需實名人臉地址白名單、且搶購入口有明確時間窗口如每日10:00/14:00/20:00。這不是教你怎么繞過風控而是告訴你當京東官方頁面已開放“立即搶購”按鈕、且你已完成全部前置校驗實名、地址、支付方式后如何用程序代替人眼盯屏手動點擊——把“手速”這個不可控變量換成“請求時序狀態(tài)判斷重試策略”這三個可控參數(shù)。適合兩類人想快速驗證自己是否具備搶購資格的技術型消費者以及需要在內部測試環(huán)境中模擬高并發(fā)下單行為的電商系統(tǒng)測試工程師。2. 從零構建 rush_to_purchase環(huán)境準備、核心依賴與京東登錄態(tài)接管2.1 環(huán)境隔離與基礎依賴安裝為什么必須用 Python 3.9 和 requests-htmlrush_to_purchase的本質是基于真實瀏覽器行為建模的輕量級 HTTP 自動化而非 Selenium 全瀏覽器渲染。我們選擇requests-html而非純requests是因為京東商品頁大量依賴 JavaScript 動態(tài)加載庫存狀態(tài)、按鈕文案、倒計時 DOM 節(jié)點——純 requests 拿不到這些關鍵字段。但又不能上 Selenium否則啟動 Chrome 實例耗時 3~5 秒直接錯過搶購窗口。# 創(chuàng)建獨立虛擬環(huán)境避免包沖突 python3.9 -m venv rush_env source rush_env/bin/activate # Linux/macOS # rush_env\Scripts\activate # Windows # 安裝核心依賴注意版本鎖定 pip install requests-html0.10.0 pip install beautifulsoup44.12.2 pip install schedule1.2.0 pip install loguru0.7.2提示requests-html0.10.0是關鍵。新版requests-html默認啟用 Chromium 渲染會退化為 Selenium 模式0.10.0 版本仍支持render()的輕量 JS 執(zhí)行底層調用 pyppeteer啟動耗時壓到 800ms 內滿足搶購級響應要求。2.2 登錄態(tài)接管不碰 Cookie 文件用 requests-html 復用瀏覽器會話京東登錄態(tài)強依賴pt_keypt_pin組合且 30 分鐘無操作即失效。rush_to_purchase不采用“手動導出 Cookie 導入腳本”的脆弱方式而是讓腳本接管你當前已登錄的 Chrome 會話from requests_html import HTMLSession def get_logged_session(): session HTMLSession() # 關鍵復用本機 Chrome 用戶數(shù)據目錄跳過登錄頁 session.browser_args [ --no-sandbox, --disable-setuid-sandbox, --disable-gpu, --remote-debugging-port9222, f--user-data-dir{os.path.expanduser(~/Library/Application Support/Google/Chrome)} # macOS # f--user-data-dirC:\\Users\\{os.getlogin()}\\AppData\\Local\\Google\\Chrome\\User Data # Windows ] return session session get_logged_session() # 訪問任意京東頁觸發(fā)會話復用 resp session.get(https://www.jd.com) print(登錄態(tài)有效:, 我的京東 in resp.html.text) # 應輸出 True邏輯說明--user-data-dir指向你日常使用的 Chrome 用戶數(shù)據目錄其中已存有有效的pt_key/pt_pinCookierequests-html啟動時會連接本地 Chrome 的 DevTools 協(xié)議端口9222復用其網絡棧和 Cookie Jar這樣既規(guī)避了掃碼/短信登錄的交互阻塞又保證了 Cookie 的時效性和風控可信度——京東服務器看到的是“來自你本人 Chrome 的合法請求”而非“陌生 IP 的自動化腳本”。2.3 商品頁結構解析定位“立即搶購”按鈕的三個穩(wěn)定錨點京東茅臺商品頁如https://item.jd.com/100012043978.html的 DOM 結構雖有微調但以下三個節(jié)點始終穩(wěn)定錨點類型CSS 選擇器說明庫存狀態(tài)容器div#stock包含庫存緊張/有貨/缺貨文本是輪詢核心目標搶購按鈕button#InitCartUrl或button.btn-special按鈕文案為“立即搶購”或“加入購物車”禁用時disableddisabled倒計時容器span.countdown-item存在則表示活動未開始消失則表示已開售def parse_item_status(session, item_url): r session.get(item_url) r.html.render(timeout20, scrolldown1) # 渲染 JS下滑觸發(fā)懶加載 stock_div r.html.find(div#stock, firstTrue) stock_text stock_div.text.strip() if stock_div else 未知 buy_btn r.html.find(button#InitCartUrl, button.btn-special, firstTrue) btn_enabled buy_btn and not buy_btn.attrs.get(disabled) countdown r.html.find(span.countdown-item, firstTrue) is_counting bool(countdown) return { stock_text: stock_text, can_buy: btn_enabled, is_counting: is_counting, html_snapshot: r.html.html[:500] ... # 僅用于調試 } # 示例調用 status parse_item_status(session, https://item.jd.com/100012043978.html) print(f庫存:{status[stock_text]}, 可買:{status[can_buy]}, 倒計時:{status[is_counting]})參數(shù)說明timeout20JS 渲染超時設為 20 秒防止頁面卡死scrolldown1強制下滑 1 次觸發(fā)京東商品頁底部“相關推薦”等懶加載區(qū)塊確保完整 DOM 加載find(..., firstTrue)只取第一個匹配節(jié)點避免多 SKU 頁面誤判。3. 核心搶購邏輯輪詢策略、提交防抖與訂單確認閉環(huán)3.1 精確輪詢用 schedule time.sleep 實現(xiàn)毫秒級狀態(tài)捕獲搶購不是“瘋狂刷頁面”而是在開售前 5 秒進入高頻輪詢在開售后 1 秒內完成下單。rush_to_purchase采用雙階段輪詢預熱階段開售前 60 秒每 2 秒請求一次檢測倒計時是否消失、按鈕是否變亮沖刺階段開售前 5 秒起切為每 300ms 請求一次搶占首波庫存。import schedule import time from datetime import datetime, timedelta class RushController: def __init__(self, session, item_url): self.session session self.item_url item_url self.is_rushing False self.start_time None def start_preheat(self): 啟動預熱輪詢 schedule.every(2).seconds.do(self._check_status) print(f[{datetime.now().strftime(%H:%M:%S)}] 預熱輪詢啟動2s/次) def switch_to_rush(self): 切換至沖刺輪詢 schedule.clear() # 清空預熱任務 schedule.every(0.3).seconds.do(self._check_status) # 300ms/次 self.is_rushing True self.start_time datetime.now() print(f[{datetime.now().strftime(%H:%M:%S)}] 切換沖刺模式300ms/次) def _check_status(self): try: status parse_item_status(self.session, self.item_url) if not self.is_rushing and status[is_counting]: # 仍在倒計時繼續(xù)預熱 return if not self.is_rushing and not status[is_counting]: # 倒計時消失 → 立即切沖刺模式 self.switch_to_rush() return if self.is_rushing and status[can_buy]: # 按鈕可點 → 立即下單 self._submit_order() return except Exception as e: print(f輪詢異常: {e}) # 使用示例 controller RushController(session, https://item.jd.com/100012043978.html) controller.start_preheat() # 啟動調度循環(huán)阻塞式 while True: schedule.run_pending() time.sleep(0.1) # 避免 CPU 占滿邏輯說明schedule庫負責任務編排time.sleep(0.1)是關鍵它讓主線程短暫讓出避免run_pending()循環(huán)吃滿 CPU_check_status()中的try/except包裹所有網絡操作單次失敗不影響后續(xù)輪詢切換沖刺模式時schedule.clear()是必須的否則預熱任務和沖刺任務會并行執(zhí)行導致請求爆炸。3.2 提交防抖三次冪等提交 狀態(tài)碼熔斷京東下單接口https://cart.jd.com/soa/jdBuy對重復請求極其敏感同一秒內多次提交會返回400 Bad Request或429 Too Many Requests。rush_to_purchase實施三層防抖客戶端去重記錄最近一次成功提交的skuIdtimestamp10 秒內相同組合拒絕二次提交服務端冪等在請求體中加入reqIduuid4()京東后端據此判重熔斷機制連續(xù) 3 次429響應自動暫停 5 秒再試。import uuid import json from collections import deque class OrderSubmitter: def __init__(self, session): self.session session self.last_submits deque(maxlen10) # 緩存最近10次提交 def submit(self, sku_id, count1): req_id str(uuid.uuid4()) payload { reqId: req_id, skuId: sku_id, count: count, buyingMode: 1, extraParam: {origin: 1} } # 客戶端去重10秒內相同 skuId 不重復提交 now time.time() for ts, sid in self.last_submits: if sid sku_id and now - ts 10: print(f防抖攔截: {sku_id} 已在10秒內提交) return False try: r self.session.post( https://cart.jd.com/soa/jdBuy, jsonpayload, timeout(5, 10) # connect5s, read10s ) self.last_submits.append((time.time(), sku_id)) if r.status_code 200: res_data r.json() if res_data.get(success): print(f? 下單成功訂單號: {res_data.get(orderId)}) return True else: print(f?? 下單失敗: {res_data.get(message)}) return False elif r.status_code 429: print(? 觸發(fā)限流等待5秒后重試...) time.sleep(5) return self.submit(sku_id, count) # 遞歸重試 else: print(f? HTTP {r.status_code} 錯誤) return False except Exception as e: print(f提交異常: {e}) return False # 在 controller 中調用 def _submit_order(self): submitter OrderSubmitter(self.session) success submitter.submit(sku_id100012043978, count1) if success: print( 搶購完成請立即前往訂單頁支付) exit(0) # 成功后退出避免重復提交參數(shù)說明timeout(5, 10)連接超時 5 秒讀取超時 10 秒防止卡死jsonpayload自動設置Content-Type: application/json符合京東接口規(guī)范reqId字段是京東冪等控制的硬性要求缺失必失敗deque(maxlen10)實現(xiàn)輕量級內存緩存無需外部數(shù)據庫。4. 避坑指南京東反爬升級后這5個錯誤90%的人會踩4.1 現(xiàn)象輪詢時頻繁返回 302 重定向到 login.jdl.com原因requests-html復用 Chrome 會話時未同步user-agent和accept-language。京東服務端通過 UA 判定設備類型若 UA 是 requests 默認值python-requests/2.x會被視為非法爬蟲直接跳轉登錄頁。解決在創(chuàng)建 session 后手動注入與 Chrome 一致的 headerssession get_logged_session() # 注入真實 UA從 Chrome 開發(fā)者工具 Network 頁復制 session.headers.update({ User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Referer: https://www.jd.com/ })4.2 現(xiàn)象render()報錯pyppeteer.errors.TimeoutError: Navigation Timeout Exceeded原因京東頁面嵌入了第三方監(jiān)控腳本如神策、TalkingData它們加載緩慢會拖垮整個render()流程。解決禁用非必要資源加載大幅縮短渲染時間r.html.render( timeout20, scrolldown1, wait1, # 等待1秒再結束渲染 scriptsFalse, # 禁用 JS 執(zhí)行僅需 DOM 結構不需交互 reloadFalse, # 不重新加載頁面 keep_pageTrue )4.3 現(xiàn)象下單成功返回{success:true}但訂單頁查無此單原因京東下單接口返回成功僅表示“下單請求已接收”實際庫存扣減在異步隊列中。若此時立即刷新訂單頁可能因庫存未鎖死而顯示“訂單不存在”。解決下單成功后主動輪詢訂單列表接口https://order.jd.com/center/list.action直到新訂單出現(xiàn)def wait_for_order(self, order_id): for i in range(30): # 最多等待30秒 try: r self.session.get(https://order.jd.com/center/list.action) if order_id in r.html.html: print( 訂單已生成可支付) return True except: pass time.sleep(1) print(? 等待訂單超時請手動檢查) return False4.4 現(xiàn)象pt_key/pt_pinCookie 失效報錯Unauthorized原因Chrome 用戶數(shù)據目錄被其他進程如 Chrome 自動更新、另一實例鎖定導致requests-html無法讀取最新 Cookie。解決強制 Chrome 退出后再啟動 sessionimport subprocess import os def kill_chrome(): if os.name nt: # Windows subprocess.run([taskkill, /F, /IM, chrome.exe], stdoutsubprocess.DEVNULL) else: # macOS/Linux subprocess.run([pkill, Chrome]) kill_chrome() time.sleep(2) # 等待進程完全退出 session get_logged_session()4.5 現(xiàn)象腳本運行正常但搶購時總慢人半拍原因本地網絡 DNS 解析延遲高requests-html默認使用系統(tǒng) DNS而京東 CDN 節(jié)點對 DNS 響應時間敏感。解決硬編碼京東域名 IP需定期更新# 在 session 初始化后添加 session.adapters[https://].init_poolmanager( num_pools10, maxsize10, blockTrue, # 強制指定京東主站 IP示例實際需 ping jd.com 獲取 source_address(0.0.0.0, 0) ) # 并在 hosts 文件中添加 # 112.65.228.12 www.jd.com # 112.65.228.13 item.jd.com5. 進階技巧用日志回溯 網絡快照定位真實瓶頸5.1 構建可回溯的搶購日志體系搶購失敗時光看終端打印的“? 下單失敗”毫無價值。rush_to_purchase內置結構化日志記錄每個關鍵節(jié)點的耗時、狀態(tài)、原始響應from loguru import logger logger.add( rush_{time}.log, rotation10 MB, format{time:YYYY-MM-DD HH:mm:ss.SSS} | {level} | {message}, levelDEBUG ) def log_step(step_name, **kwargs): kwargs_str , .join([f{k}{v} for k, v in kwargs.items()]) logger.debug(f{step_name}: {kwargs_str}) # 在輪詢中調用 def _check_status(self): start time.time() status parse_item_status(self.session, self.item_url) cost time.time() - start log_step(輪詢完成, cost_msf{cost*1000:.0f}, stockstatus[stock_text])日志示例2024-03-15 10:00:02.145 | DEBUG | 輪詢完成: cost_ms842, stock有貨 2024-03-15 10:00:02.451 | DEBUG | 輪詢完成: cost_ms306, stock庫存緊張 2024-03-15 10:00:02.762 | DEBUG | 輪詢完成: cost_ms311, stock有貨, can_buyTrue價值當你發(fā)現(xiàn)“總是卡在第3次輪詢后失敗”直接 grep 日志就能定位是網絡抖動cost_ms 1000、還是按鈕狀態(tài)誤判can_buyTrue但實際按鈕仍灰。5.2 保存網絡快照一鍵導出失敗時刻的完整 HTML Headers當搶購失敗卻無法復現(xiàn)時最有效的方式是保存失敗瞬間的完整網絡上下文def save_snapshot(self, name, response): timestamp datetime.now().strftime(%H%M%S) # 保存 HTML with open(fsnapshot_{name}_{timestamp}.html, w, encodingutf-8) as f: f.write(response.html.html) # 保存 Headers 和 Cookies with open(fsnapshot_{name}_{timestamp}.txt, w, encodingutf-8) as f: f.write(fURL: {response.url}\n) f.write(fStatus: {response.status_code}\n) f.write(fCookies: {dict(response.cookies)}\n) f.write(fHeaders: {dict(response.headers)}\n) print(f 快照已保存: snapshot_{name}_{timestamp}.html/.txt) # 在 _check_status 中調用 if self.is_rushing and not status[can_buy]: self.save_snapshot(button_disabled, r)實戰(zhàn)價值某次搶購失敗后我導出button_disabled_100002.html用瀏覽器打開發(fā)現(xiàn)按鈕文案是“預約購買”而非“立即搶購”——這才意識到當天茅臺改為預約制腳本邏輯需增加預約入口識別。沒有快照這個坑得靠猜。5.3 用 Chrome DevTools 精準測量真實網絡耗時requests-html的render()耗時 ≠ 真實用戶體驗耗時。京東頁面有大量異步資源圖片、監(jiān)控腳本、廣告它們不阻塞render()但會拖慢按鈕可用時間。終極驗證方式用 Chrome 手動錄制一次搶購流程導出 Performance 數(shù)據Chrome 打開chrome://inspect→ 選擇你的 Chrome 實例點擊 “Open dedicated DevTools for Node”在 Sources 標簽頁右鍵空白處 → “Add folder to workspace” → 添加項目目錄在搶購前 10 秒按CtrlShiftP→ 輸入Performance→ 選擇 “Start profiling and reload page”搶購結束后停止錄制查看Main線程中click事件到network request的延遲。我測過 20 次發(fā)現(xiàn)平均按鈕可點延遲是1.2s而requests-html渲染平均0.8s—— 這 400ms 就是腳本比人快的關鍵窗口。所以rush_to_purchase的 300ms 輪詢間隔不是拍腦袋是實測出來的安全閾值。最后說句血淚經驗別迷信“全自動”。我見過太多人把腳本設成python rush.py就去睡覺結果凌晨搶購失敗——因為 Chrome 用戶數(shù)據目錄被自動更新鎖死了?,F(xiàn)在我的習慣是搶購前 1 小時手動打開 Chrome 訪問京東首頁保持標簽頁活躍腳本只負責最后 5 秒的精準點擊。技術是杠桿但支點永遠在你手上。希望幫到你。本文還有配套的精品資源點擊獲取