:從抓包到Python/VC++封裝)
如果你正想在項目里獲取京東評論的API接口大概率已經(jīng)發(fā)現(xiàn)在官方文檔里翻半天根本找不到一個叫“獲取商品評論”的現(xiàn)成接口。更頭疼的是網(wǎng)上一搜“京東評論爬蟲”出來的教程多半已經(jīng)過期京東前端接口的名稱和參數(shù)隔幾個月就換一次照抄代碼大概率直接撲街。這篇文章不是教你“破解”什么黑科技而是把我實際踩出來的路子整理一遍從瀏覽器抓包定位評論接口到用 Python 快速驗證再到用 VC 訪問 HTTP 服務(wù)端 API 接口并解析 JSON最后把那些 403、空數(shù)據(jù)、分頁錯亂之類的坑一起列出來。無論你是做數(shù)據(jù)分析、競品監(jiān)控還是想給內(nèi)部工具補(bǔ)一個“商品口碑”模塊這套方法論都能直接用學(xué)完還能橫移到小說 API、股票數(shù)據(jù)接口這類 RESTful API 場景上。1. 京東評論API難拿的真相與三條可行路徑1.1 為什么官方API里沒有現(xiàn)成的獲取評論接口很多人覺得京東這么大的開放平臺肯定提供了“評論API接口”只是自己沒找到。實話實話京東開放平臺確實給商家和合作服務(wù)商提供了一些評價相關(guān)的接口但主要用于商家后臺查看訂單評價、回復(fù)評價不是給普通開發(fā)者“拿某個商品的公開評論列表”用的。這類接口通常要求你有商家賬號、建應(yīng)用、申請權(quán)限、拿到 access_token而且在審核階段基本就卡掉了個人開發(fā)者。就算你通過了商家端 API 權(quán)限接口返回的數(shù)據(jù)也不是你想的那種“直接能展示的評論全文”更多是評價維度、評分統(tǒng)計之類。所以純民間場景下大家嘴里說的“京東評論API”實際上指的是商品詳情頁里那個前端的 H5 異步接口。它不是公開文檔里的正式服務(wù)但只要你模擬瀏覽器正常請求就能拿到結(jié)構(gòu)化的 JSON 評論數(shù)據(jù)。這屬于“頁面接口抓包復(fù)用”本質(zhì)上是爬蟲但有別于繞過加密簽名的高危操作它在技術(shù)門檻和數(shù)據(jù)量上都比較溫和。1.2 三條可行路徑的對比與選型建議先把我實際用過的方案擺出來按推薦程度從低到高排方案獲取難度數(shù)據(jù)完整性合規(guī)風(fēng)險適合場景官方開放平臺 API高需商家資質(zhì)審核較強(qiáng)但僅限授權(quán)范圍低商家自營系統(tǒng)、正規(guī)合作服務(wù)方第三方聚合數(shù)據(jù)服務(wù)商低付費(fèi)購買次數(shù)取決于服務(wù)商渠道中注意服務(wù)商數(shù)據(jù)來源快速上線、對數(shù)據(jù)時效要求不高的小工具自己抓包 H5 評論接口中需要會抓包和反風(fēng)控完整包含評論正文、時間、評分、追評中高需要遵守平臺規(guī)則和頻率學(xué)習(xí)研究、內(nèi)部監(jiān)控、低頻個人項目個人角度我一般推薦第三條路徑但強(qiáng)烈建議把它定位成“學(xué)習(xí)研究”而不是“商業(yè)爬蟲”。理由很簡單接口是活的官方頁面怎么改你的解析腳本就得怎么跟。把它當(dāng)成一個可復(fù)用的“評論API接口封裝”來做比每次手動復(fù)制數(shù)據(jù)省心得多也比依賴第三方服務(wù)商的“黑盒數(shù)據(jù)”更可控。2. 核心接口拆解抓包定位京東評論數(shù)據(jù)接口2.1 用瀏覽器開發(fā)者工具找到真實評論接口先把某件商品的詳情頁打開比如你在地址欄里能看到??sku100012043978之類這個sku就是商品ID后面所有請求都靠它。然后按 F12 打開開發(fā)者工具切到 Network 面板刷新頁面后在商品評價Tab里隨便翻一頁。這時候面板里會出現(xiàn)大量請求別亂看直接聚焦在 XHR 請求上搜關(guān)鍵字comment或者getCommentListPage。我自己實測下來穩(wěn)定能用的一個 H5 評論接口是https://sclub.jd.com/comment/page.service.CommentService/getCommentListPage.action它的核心參數(shù)就那么幾個productId: 100012043978 page: 0 pageSize: 10 sortType: 5 score: 0 isShadowSku: 0 fold: 1注意這里第一頁的page是 0不是 1。這個接口不加callback參數(shù)時返回純 JSON加了callback會返回 JSONP 格式方便瀏覽器端繞跨域。抓包時你看到的可能是帶callbackjQueryxxxxx的 URL去掉 callback 再請求一樣能拿到數(shù)據(jù)而且解析更干凈。抓包有個習(xí)慣要養(yǎng)成不要只復(fù)制 URL要把請求頭里的User-Agent和Referer一起抄下來。評論接口對Referer不算苛刻但缺了它偶爾會返回 403。User-Agent直接用一個真實 Chrome 的值就行千萬別用 Python 默認(rèn)的python-requests。2.2 關(guān)鍵參數(shù)說明與分頁邏輯這些參數(shù)我是反復(fù)試出來的說幾個容易踩坑的地方sortType排序方式5 是按時間排序6 是按推薦排序0 貌似是默認(rèn)綜合。想抓最新評論就用 5想抓熱評就用 6。score評分篩選0 是全部1 是一星2 是二星3 是三星4 是四星5 是五星。要分析差評直接讓 score1。isShadowSku這個參數(shù)挺關(guān)鍵。有些商品會有“關(guān)聯(lián)SKU”的評論聚合比如多規(guī)格商品的一些評論掛在舊SKU下。填 0 表示只拿當(dāng)前SKU的評論填 1 會把隱藏款式的評論也帶出來但返回結(jié)構(gòu)會變。我建議先用 0。fold折行參數(shù)1 表示不折疊用來拿更多追評內(nèi)容。接口返回的 JSON 里頂層是一個comments數(shù)組數(shù)組里每個元素包含content評論內(nèi)容、creationTime評論時間、score評分、nickname脫敏昵稱、productColor、productSize等字段。還可能有一個maxPage字段用來判斷總共有多少頁。真要翻全量評論就while page maxPage一頁一頁拉但實際跑下來建議加個次數(shù)上限別一次性刷幾千頁否則很容易被風(fēng)控。3. 從快速驗證到工程封裝Python 與 VC 兩種實戰(zhàn)打法3.1 先用 Python 五分鐘驗證評論接口不管你最終打算用 Python 還是 VC我強(qiáng)烈建議先用 Python 把接口跑通驗證參數(shù)、字段、分頁邏輯都沒問題之后再移植到別的語言。原因很簡單Python 寫起來快調(diào)試成本低接口長什么樣、返回什么字段一眼就能看明白。下面這段代碼是我常用的最小驗證腳本import requests import time product_id 100012043978 url https://sclub.jd.com/comment/page.service.CommentService/getCommentListPage.action session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36, Referer: fhttps://item.jd.com/{product_id}.html, Accept: application/json, text/plain, */*, Accept-Encoding: gzip, deflate, br, }) params { productId: product_id, page: 0, pageSize: 10, sortType: 5, score: 0, isShadowSku: 0, fold: 1, } resp session.get(url, paramsparams, timeout10) data resp.json() for comment in data[comments]: print(comment[creationTime], comment[score], comment[content])跑通之后你就可以在這個基礎(chǔ)上做二次封裝比如包裝成一個fetch_jd_review(product_id, page, page_size)函數(shù)再丟給后續(xù)的數(shù)據(jù)清洗模塊。需要注意一點(diǎn)接口返回的是 gzip 壓縮內(nèi)容但requests會自動解壓不需要手動處理。如果你用的是 VC就得自己考慮解壓這一步了。3.2 用 VC/WinHTTP 調(diào)用 RESTful API 的封裝樣例有些場景必須用 VC比如你的產(chǎn)品本身是 Windows 桌面客戶端不想為了一個評論接口再內(nèi)嵌一個 Python 解釋器又或者你要把這個接口封裝成底層庫給現(xiàn)有 C 業(yè)務(wù)模塊調(diào)用。這時候用 WinHTTP 是最直接的辦法它是 Windows 系統(tǒng)自帶的 HTTP 棧不需要引入第三方重量級依賴。先看核心的 HTTP GET 封裝我習(xí)慣寫成一個長這樣的函數(shù)#include windows.h #include winhttp.h #include string #include vector #include nlohmann/json.hpp #pragma comment(lib, winhttp.lib) std::string HttpGetJson(const std::wstring host, const std::wstring path, const std::wstring referer, const std::wstring cookie, DWORD timeoutMs) { HINTERNET hSession WinHttpOpen( LJDCommentClient/1.0, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); HINTERNET hConnect WinHttpConnect( hSession, host.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0); HINTERNET hRequest WinHttpOpenRequest( hConnect, LGET, path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); WinHttpSetTimeouts(hRequest, timeoutMs, timeoutMs, timeoutMs, timeoutMs); if (!referer.empty()) { std::wstring refererHeader LReferer: referer; WinHttpAddRequestHeaders(hRequest, refererHeader.c_str(), (ULONG)-1L, WINHTTP_ADDREQ_FLAG_REPLACE); } if (!cookie.empty()) { std::wstring cookieHeader LCookie: cookie; WinHttpAddRequestHeaders(hRequest, cookieHeader.c_str(), (ULONG)-1L, WINHTTP_ADDREQ_FLAG_REPLACE); } BOOL sent WinHttpSendRequest( hRequest, WINHTTP_NO_EXTRA_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); if (!sent) { DWORD err GetLastError(); WinHttpCloseHandle(hRequest); WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); throw std::runtime_error(WinHttpSendRequest failed: std::to_string(err)); } WinHttpReceiveResponse(hRequest, NULL); std::vectorchar responseData; DWORD available 0; do { WinHttpQueryDataAvailable(hRequest, available); if (available 0) break; size_t oldSize responseData.size(); responseData.resize(oldSize available 1); DWORD bytesRead 0; if (!WinHttpReadData(hRequest, responseData[oldSize], available, bytesRead)) { break; } responseData.resize(oldSize bytesRead); } while (available 0); WinHttpCloseHandle(hRequest); WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return std::string(responseData.begin(), responseData.end()); }調(diào)用的時候把 host、path、referer 拼好就行std::wstring host Lsclub.jd.com; std::wstring path L/comment/page.service.CommentService/ LgetCommentListPage.action? LproductId100012043978page0pageSize10 LsortType5score0isShadowSku0fold1; std::wstring referer Lhttps://item.jd.com/100012043978.html; std::wstring cookie L; // 如果頁面需要cookie這里填抓包復(fù)制的值 std::string jsonText HttpGetJson(host, path, referer, cookie, 10000);這個封裝的核心點(diǎn)在于 WinHTTP 的句柄管理WinHttpOpen管 SessionWinHttpConnect管連接WinHttpOpenRequest管請求用完必須逐個CloseHandle。別只關(guān)最后一個否則句柄泄漏。另外WinHttpSendRequest的網(wǎng)絡(luò)超時默認(rèn)可能很長一定要用WinHttpSetTimeouts設(shè)置否則接口卡住時界面直接假死。拿到j(luò)sonText之后原生 C 沒有內(nèi)置 JSON 解析我用的是nlohmann/json一個 header-only 庫整個項目里加個頭文件就能用auto data nlohmann::json::parse(jsonText); for (const auto item : data[comments]) { std::string content item.value(content, ); std::string time item.value(creationTime, ); int score item.value(score, 0); // 這里自己決定是打印、入庫還是回調(diào)業(yè)務(wù)層 }如果你的項目不方便引入 nlohmann也可以用 Windows 自帶的IXMLDOMDocument把 JSON 當(dāng) XML 解析但體驗非常痛苦。相比之下nlohmann 幾乎是現(xiàn)在 VC 世界里最省心的 JSON 方案。3.3 順帶說一句讓豆包這類AI助手幫你寫綁定代碼最近不是特別流行讓“豆包”這種 AI 助手幫忙寫代碼嗎我在做 VC 封裝時也試過先把上一節(jié)抓包得到的接口請求參數(shù)整理成一段自然語言描述再讓豆包生成客戶端調(diào)用代碼效果相當(dāng)可以。尤其是從零開始寫 WinHTTP 封裝時AI 能給你一個能跑的骨架你再花幾分鐘把超時、Header、錯誤處理補(bǔ)扎實效率比手敲快一倍。不過我得提醒一句AI 生成的代碼里經(jīng)常有想當(dāng)然的參數(shù)名比如把sortType寫成sort、把page當(dāng)成從 1 開始。這種問題不會報錯但會返回錯誤數(shù)據(jù)。所以任何 AI 生成的請求代碼都要先用 Python 腳本跑一遍同樣的參數(shù)確認(rèn)返回值后再往 C 里搬。4. 實測過程中的高頻坑與排查速查4.1 403 與風(fēng)控我剛開始寫這個接口的時候臉不紅心不跳地用一個for循環(huán)直接從第 0 頁刷到第 100 頁結(jié)果刷到第 20 頁左右請求開始返回 403。這是京東對非正常瀏覽行為的攔截最常見的原因有兩個請求頭的User-Agent太假或者Referer缺失。單 IP 短時間請求頻率過高。解決辦法也不復(fù)雜加延時是必須的我個人習(xí)慣在兩次請求之間隨機(jī)睡 1 到 3 秒不要用固定間隔固定間隔更容易被識別成機(jī)器。另外User-Agent一定不要讓它保留python-requests這種默認(rèn)值。如果 403 已經(jīng)出現(xiàn)通常過幾分鐘會自動解封這時候停下來冷靜一下別繼續(xù)懟。提示千萬別嘗試?yán)@過滑塊驗證或者通過異常手段偽造用戶身份這條路不僅不穩(wěn)定還會帶來法律風(fēng)險。合規(guī)處理比數(shù)據(jù)量重要得多。4.2 返回空數(shù)據(jù)另一個高頻問題是接口正常返回 200但comments數(shù)組是空的。這種情況十有八九是參數(shù)沒配對。我遇到過幾種productId傳錯了從商品鏈接里復(fù)制的時候多帶了小數(shù)點(diǎn)或者其他字符。page超出maxPage返回空是正常的。score填了 5而這個商品確實一個五星評價都沒有接口一樣會返回空數(shù)組。排查的時候先固定pageSize10、score0、sortType5用瀏覽器打開同一個商品的評價頁做對照。如果瀏覽器有數(shù)據(jù)而請求沒有那就是請求頭少了Referer。還有一個細(xì)節(jié)有些商品的評價接口不是sclub.jd.com而是club.jd.com下的舊版路徑兩者返回結(jié)構(gòu)略有差異。建議以瀏覽器抓包到的主機(jī)名為準(zhǔn)不要只看網(wǎng)上抄來的代碼。4.3 數(shù)據(jù)合規(guī)你必須有數(shù)聊了這么多技術(shù)細(xì)節(jié)我必須把這條放前面京東評論數(shù)據(jù)是用戶生成內(nèi)容受平臺用戶協(xié)議保護(hù)。未經(jīng)授權(quán)大規(guī)模抓取并用于商業(yè)運(yùn)營存在明確的法律風(fēng)險即便只是學(xué)習(xí)研究也要嚴(yán)格控制頻率不要給目標(biāo)服務(wù)器造成壓力。我的做法是單商品數(shù)據(jù)量控制在幾百條以內(nèi)跑完就停不搞全量備份更不會把數(shù)據(jù)打包賣出去。如果你是真的想做一個長期供貨的評論數(shù)據(jù)服務(wù)我不建議自己爬而是去申請正規(guī)數(shù)據(jù)合作渠道。寧可花錢買穩(wěn)定也別讓自己陷入隨時可能收到律師函的被動局面。4.4 高頻問題對照表下面這個表是我從多個項目里整理出來的最適合貼在項目文檔里當(dāng)速查現(xiàn)象可能原因排查方法返回 403User-Agent 缺失、Referer 不對、訪問過快檢查請求頭加上真實 UA增加隨機(jī)延時返回 200 但 comments 為空productId 錯、page 越界、score 篩選過嚴(yán)瀏覽器對照參數(shù)先固定全量篩選JSON 解析報錯返回了 JSONP 而不是純 JSON去掉 callback 參數(shù)或用文本替換處理中文字段亂碼請求時沒帶 gzip 頭或編碼識別錯保證 Accept-Encoding 正確用 UTF-8 解碼VC 請求一直卡住沒有設(shè)置 WinHttpSetTimeouts設(shè)置發(fā)送、接收超時建議 10 到 30 秒再分享一個我實際的排查小技巧寫 VC 封裝時最怕的不是接口請求本身而是接口返回數(shù)據(jù)被截斷。WinHTTP 讀取響應(yīng)時必須循環(huán)調(diào)用WinHttpQueryDataAvailable和WinHttpReadData不能只讀一次。我第一次寫示例代碼時就只讀了一次結(jié)果總是只能拿到半個 JSON后面解析一直報錯。所以上面那套循環(huán)讀法建議原樣保留別精簡。5. 最后再補(bǔ)充一點(diǎn)我的個人心得如果你只是拿這個接口練手我建議你在 Python 版本跑通之后再費(fèi)點(diǎn)時間把 VC 版本整理成一個帶緩存、帶超時重試的小類。因為京東評論接口的數(shù)據(jù)時效性很強(qiáng)今天寫的腳本明天可能就少了幾個參數(shù)把它封裝成清晰的函數(shù)后續(xù)改起來會快很多。我在實際項目中就是靠這個方式把評論接口的維護(hù)周期從一個星期縮短到了半天。另外別把目光只鎖在京東評論上。你現(xiàn)在會抓包定位接口、會構(gòu)造請求參數(shù)、會用 Python 或 VC 解析 JSON這套能力放到小說 API、股票數(shù)據(jù)接口、天氣接口、遠(yuǎn)程壓縮服務(wù)接口上完全通用。說白了所謂“獲取京東評論API接口”本質(zhì)就是一次標(biāo)準(zhǔn)的 HTTP RESTful 接口對接找對 URL帶對頭解析好響應(yīng)剩下的全是耐心。把這個思路吃透以后再遇到任何“某某平臺接口怎么拿”的問題你都具備了自己動手解決問題的能力。