瀏覽器的copy as fetch和copy as bash的區別
位置:devTools->network->請求列表右鍵
copy as fetch
fetch("https://www.kuaishou.com/graphql", {"headers": {"accept": "*/*","accept-language": "en,zh-CN;q=0.9,zh;q=0.8","cache-control": "no-cache","content-type": "application/json","pragma": "no-cache","sec-ch-ua": "\"Google Chrome\";v=\"129\", \"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"129\"","sec-ch-ua-mobile": "?0","sec-ch-ua-platform": "\"Windows\"","sec-fetch-dest": "empty","sec-fetch-mode": "cors","sec-fetch-site": "same-origin"},"referrer": "https://www.kuaishou.com/search/video?searchKey=22","referrerPolicy": "unsafe-url","body": "{\"operationName\":\"visionSearchPhoto\",\"variables\":{\"keyword\":\"22\",\"pcursor\":\"\",\"page\":\"search\"},\"query\":\"fragment photoContent on PhotoEntity {\\n __typename\\n id\\n duration\\n caption\\n originCaption\\n likeCount\\n viewCount\\n commentCount\\n realLikeCount\\n coverUrl\\n photoUrl\\n photoH265Url\\n manifest\\n manifestH265\\n videoResource\\n coverUrls {\\n url\\n __typename\\n }\\n timestamp\\n expTag\\n animatedCoverUrl\\n distance\\n videoRatio\\n liked\\n stereoType\\n profileUserTopPhoto\\n musicBlocked\\n riskTagContent\\n riskTagUrl\\n}\\n\\nfragment recoPhotoFragment on recoPhotoEntity {\\n __typename\\n id\\n duration\\n caption\\n originCaption\\n likeCount\\n viewCount\\n commentCount\\n realLikeCount\\n coverUrl\\n photoUrl\\n photoH265Url\\n manifest\\n manifestH265\\n videoResource\\n coverUrls {\\n url\\n __typename\\n }\\n timestamp\\n expTag\\n animatedCoverUrl\\n distance\\n videoRatio\\n liked\\n stereoType\\n profileUserTopPhoto\\n musicBlocked\\n riskTagContent\\n riskTagUrl\\n}\\n\\nfragment feedContent on Feed {\\n type\\n author {\\n id\\n name\\n headerUrl\\n following\\n headerUrls {\\n url\\n __typename\\n }\\n __typename\\n }\\n photo {\\n ...photoContent\\n ...recoPhotoFragment\\n __typename\\n }\\n canAddComment\\n llsid\\n status\\n currentPcursor\\n tags {\\n type\\n name\\n __typename\\n }\\n __typename\\n}\\n\\nquery visionSearchPhoto($keyword: String, $pcursor: String, $searchSessionId: String, $page: String, $webPageArea: String) {\\n visionSearchPhoto(keyword: $keyword, pcursor: $pcursor, searchSessionId: $searchSessionId, page: $page, webPageArea: $webPageArea) {\\n result\\n llsid\\n webPageArea\\n feeds {\\n ...feedContent\\n __typename\\n }\\n searchSessionId\\n pcursor\\n aladdinBanner {\\n imgUrl\\n link\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}","method": "POST","mode": "cors","credentials": "include"
});
copy as bash
curl 'https://www.kuaishou.com/graphql' \-H 'Accept-Language: en,zh-CN;q=0.9,zh;q=0.8' \-H 'Cache-Control: no-cache' \-H 'Connection: keep-alive' \-H 'Cookie: weblogger_did=web_140476153E50FC40; kpf=PC_WEB; clientid=3; did=web_6c1bfef1bff5f0e063c16c6381ffc8d0; userId=1926344872; kuaishou.server.webday7_st=ChprdWFpc2hxxxxxxxxxxF5Ny5zdBKwAU-QhpLs58EqWw14KtRN3ktXeVV4IUBH-whoUB9UX3l00ZEd9B3n9SpIk9BsUuMCrjTDKxxxxxxxxxcCZg_WwoR6sy5p6VyZobi7CqqXtr-8XLF7slXPq8_PhjMjB5BRkekJPuKCZCSYTPMFYUWTr1AJEzk2Zt-eTtZox9VWK6XPjUUhVxxxxxxxxEG-4f5yJd10AXaDXiGhJglXWxx8RvW8kJJMTzKPTd1I8iIGnzLs4NhZYSpkCR8AmlC8W8L-ZzYA0UByzvqOerFQahKAUwAQ; kuaishou.server.webday7_ph=1b4a3594exxxxxxxxx3531408cb; kpn=KUAISHOU_VISION' \-H 'Origin: https://www.kuaishou.com' \-H 'Pragma: no-cache' \-H 'Referer: https://www.kuaishou.com/search/video?searchKey=22' \-H 'Sec-Fetch-Dest: empty' \-H 'Sec-Fetch-Mode: cors' \-H 'Sec-Fetch-Site: same-origin' \-H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36' \-H 'accept: */*' \-H 'content-type: application/json' \-H 'sec-ch-ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"' \-H 'sec-ch-ua-mobile: ?0' \-H 'sec-ch-ua-platform: "Windows"' \--data-raw '{"operationName":"visionSearchPhoto","variables":{"keyword":"22","pcursor":"","page":"search"},"query":"fragment photoContent on PhotoEntity {\n __typename\n id\n duration\n caption\n originCaption\n likeCount\n viewCount\n commentCount\n realLikeCount\n coverUrl\n photoUrl\n photoH265Url\n manifest\n manifestH265\n videoResource\n coverUrls {\n url\n __typename\n }\n timestamp\n expTag\n animatedCoverUrl\n distance\n videoRatio\n liked\n stereoType\n profileUserTopPhoto\n musicBlocked\n riskTagContent\n riskTagUrl\n}\n\nfragment recoPhotoFragment on recoPhotoEntity {\n __typename\n id\n duration\n caption\n originCaption\n likeCount\n viewCount\n commentCount\n realLikeCount\n coverUrl\n photoUrl\n photoH265Url\n manifest\n manifestH265\n videoResource\n coverUrls {\n url\n __typename\n }\n timestamp\n expTag\n animatedCoverUrl\n distance\n videoRatio\n liked\n stereoType\n profileUserTopPhoto\n musicBlocked\n riskTagContent\n riskTagUrl\n}\n\nfragment feedContent on Feed {\n type\n author {\n id\n name\n headerUrl\n following\n headerUrls {\n url\n __typename\n }\n __typename\n }\n photo {\n ...photoContent\n ...recoPhotoFragment\n __typename\n }\n canAddComment\n llsid\n status\n currentPcursor\n tags {\n type\n name\n __typename\n }\n __typename\n}\n\nquery visionSearchPhoto($keyword: String, $pcursor: String, $searchSessionId: String, $page: String, $webPageArea: String) {\n visionSearchPhoto(keyword: $keyword, pcursor: $pcursor, searchSessionId: $searchSessionId, page: $page, webPageArea: $webPageArea) {\n result\n llsid\n webPageArea\n feeds {\n ...feedContent\n __typename\n }\n searchSessionId\n pcursor\n aladdinBanner {\n imgUrl\n link\n __typename\n }\n __typename\n }\n}\n"}'
兩個代碼片段分別是使用 JavaScript 的 fetch
API 和命令行工具 curl
發起的 POST 請求,目標均為快手的 GraphQL 接口 https://www.kuaishou.com/graphql
,用于搜索關鍵詞為 22
的視頻數據。以下是兩者的核心區別:
1. 協議與用途
-
fetch
- 屬于 瀏覽器原生 API,用于在前端(如網頁腳本)中發起網絡請求。
- 依賴瀏覽器環境,支持瀏覽器特有的功能(如
credentials: "include"
自動攜帶同源 Cookie)。
-
curl
- 是 命令行工具,用于在終端(如 Linux/macOS/Windows 的命令提示符)中模擬網絡請求。
- 獨立于瀏覽器,需手動指定所有請求參數(如 Cookie、User-Agent 等)。
2. 請求頭(Headers)
-
共同點
- 核心請求頭(如
Accept
、Content-Type
、Referer
、Sec-Fetch-*
等)幾乎完全一致,均聲明了請求為 JSON 格式、跨域模式(CORS)、來源為搜索頁面https://www.kuaishou.com/search/video?searchKey=22
。
- 核心請求頭(如
-
差異點
curl
額外包含:Cookie
頭:顯式攜帶瀏覽器存儲的 Cookie(如用戶登錄狀態、設備標識等),而fetch
中未顯式聲明,但通過credentials: "include"
會自動發送同源 Cookie。User-Agent
:明確指定瀏覽器指紋(Mozilla/5.0 (Windows NT 10.0...
),fetch
中瀏覽器會默認添加,但代碼中未顯式寫出。Connection: keep-alive
:保持長連接,fetch
會自動處理,無需手動聲明。
fetch
額外包含:referrerPolicy: "unsafe-url"
:控制 Referer 頭的發送策略,curl
中無此參數(直接使用Referer
頭)。
3. 請求體(Body)
- 內容完全一致:
均為 GraphQL 查詢字符串,包含operationName
(visionSearchPhoto
)、搜索關鍵詞keyword: "22"
、以及復雜的字段片段(fragment
),用于獲取視頻的詳細信息(如 ID、播放量、封面鏈接等)。 - 格式差異:
fetch
中需將請求體轉換為字符串("body": JSON.stringify(...)
,但用戶代碼中已直接使用字符串)。curl
中通過--data-raw
直接傳遞原始 JSON 數據,無需額外處理。
4. Cookie 處理
-
fetch
:- 通過
credentials: "include"
聲明跨域請求時攜帶 Cookie(需目標域名支持Access-Control-Allow-Credentials: true
)。 - 瀏覽器自動讀取當前域名下的 Cookie 并添加到請求中,代碼中無需顯式寫入
Cookie
頭。
- 通過
-
curl
:- 必須手動在
Cookie
頭中寫入完整的 Cookie 字符串(如用戶提供的weblogger_did=...; kpf=PC_WEB; ...
),否則服務端可能無法識別用戶狀態(如登錄信息)。
- 必須手動在
5. 使用場景
-
fetch
:- 用于前端代碼(如網頁腳本)中,動態獲取數據并渲染頁面(例如快手搜索結果頁的視頻列表)。
- 受瀏覽器同源策略、跨域限制(需服務端配置 CORS)。
-
curl
:- 用于調試、抓包分析(如通過瀏覽器開發者工具獲取請求后,用
curl
復現)、自動化腳本(如爬蟲、數據采集)。 - 可繞過瀏覽器限制,直接構造任意請求(需手動處理所有參數,包括簽名、加密等)。
- 用于調試、抓包分析(如通過瀏覽器開發者工具獲取請求后,用
總結
兩者本質上是 同一請求的不同實現方式:fetch
是瀏覽器環境下的前端代碼,依賴瀏覽器自動處理部分參數(如 Cookie、User-Agent);curl
是獨立的命令行工具,需手動指定所有細節,適合調試和非瀏覽器環境下的請求模擬。核心差異在于 環境依賴、Cookie 處理方式 和 參數聲明的顯式程度。這段代碼展示了兩種向快手 GraphQL API 發送請求的方式,分別使用了瀏覽器的 fetch
API 和命令行工具 curl
。它們的主要區別包括:
-
運行環境
fetch
:在瀏覽器環境中執行,受瀏覽器安全策略限制(如 CORS)。curl
:在命令行或后端環境中執行,沒有跨域限制。
-
Cookie 處理
fetch
:通過credentials: "include"
攜帶 cookies,但需要服務器明確允許(Access-Control-Allow-Credentials
)。curl
:直接在請求頭中包含完整的 cookies(如weblogger_did
、userId
等),無需額外配置。
-
請求頭差異
curl
包含了Connection: keep-alive
和完整的Cookie
信息。fetch
沒有顯式設置Connection
頭,依賴瀏覽器默認行為。
-
數據格式
fetch
:使用 JavaScript 對象定義請求參數。curl
:使用命令行參數和原始字符串數據。
-
實際效果
fetch
:可能因缺少有效 cookies 或 CORS 配置失敗。curl
:由于攜帶完整 cookies,更有可能成功獲取數據。
總結:curl
請求更完整(包含 cookies),適合后端或腳本環境;fetch
請求需要瀏覽器環境支持,且需服務器配合 CORS 策略。如果直接運行,curl
更可能成功獲取數據。