:構建京東評論爬蟲,應對反爬與數(shù)據(jù)分類保存)
簡介本資源是一個面向Python初學者與數(shù)據(jù)采集實踐者的京東商品評論爬蟲實戰(zhàn)項目聚焦于利用requests庫高效獲取并結構化保存電商用戶反饋解決市場調研、情感分析等場景下的原始數(shù)據(jù)獲取難題。壓縮包共7個文件含3個按情感傾向分類的CSV樣本數(shù)據(jù)正面/中性/負面、核心爬蟲腳本py文件、項目說明文檔md及開源協(xié)議文件整體僅2.5MB輕量易上手。已有47人學習下載適合希望掌握HTTP請求模擬、HTML解析、反爬應對基礎如請求頭偽裝、間隔控制及多類別數(shù)據(jù)歸檔邏輯的學習者。讀者可直接運行腳本復現(xiàn)完整流程獲得可擴展的評論采集框架、標準化的CSV分類存儲結構以及適配京東頁面結構的Selector提取經(jīng)驗為后續(xù)接入NLP分析或可視化打下堅實基礎。1. 項目概述從零構建一個健壯的京東評論爬蟲最近在分析一些消費電子產(chǎn)品的市場反饋手動收集評論數(shù)據(jù)效率太低于是決定自己動手寫一個爬蟲。目標很明確用 Python 的 requests 庫把京東上指定商品的評論數(shù)據(jù)抓下來并且要按好評、中評、差評分類保存到不同的文件里。這聽起來是個很典型的爬蟲入門項目但真做起來你會發(fā)現(xiàn)從簡單的“請求-解析”到能穩(wěn)定運行、應對反爬、處理異常的分類保存工具中間要踩的坑可不少。特別是面對京東這樣的大型電商平臺它的反爬機制和動態(tài)頁面結構對新手來說是個不小的挑戰(zhàn)。這個項目適合有一定 Python 基礎想從“寫個簡單腳本”進階到“構建實用數(shù)據(jù)采集工具”的朋友。接下來我會把整個從思路設計、代碼實現(xiàn)到問題排查的完整過程拆開揉碎了講你跟著做不僅能得到一個可用的工具更能掌握處理類似數(shù)據(jù)抓取任務的通用方法論。2. 核心思路與工具選型背后的考量2.1 為什么選擇 Requests 而不是 Scrapy 或 Selenium很多爬蟲教程一上來就推薦 Scrapy 框架或者 Selenium 自動化工具。對于京東評論這種需要處理分頁、可能涉及一些動態(tài)參數(shù)的項目它們確實有優(yōu)勢。但我堅持用最基礎的requests庫起步原因有三點。第一降低認知負擔聚焦核心邏輯。Scrapy 框架雖強大但其異步架構、中間件、管道等概念對新手來說過于復雜。我們的核心任務是理解網(wǎng)絡請求的構成、數(shù)據(jù)的提取和保存。用requests可以讓我們更清晰地看到“發(fā)送一個 HTTP 請求”和“接收一個 HTTP 響應”這一最本質的過程避免被框架的抽象層分散注意力。第二京東評論接口本質是靜態(tài) API。通過瀏覽器開發(fā)者工具分析可以發(fā)現(xiàn)京東商品評論頁的數(shù)據(jù)是通過一個獨立的 JSON 接口加載的頁面本身只是提供了一個展示殼。這意味著我們不需要模擬完整的瀏覽器環(huán)境Selenium 的主要用途去渲染 JavaScript只需要找到并正確調用這個后臺接口即可。requests庫完全勝任此類任務且效率遠高于 Selenium。第三輕量化和可控性。一個純requests的腳本依賴極少部署和運行非常方便。你可以更容易地控制請求頭、請求參數(shù)、代理、超時和重試策略這對于應對反爬策略至關重要。等我們用requests把整個流程跑通理解了反爬的各個環(huán)節(jié)后再考慮遷移到 Scrapy 以獲得更好的工程化管理會是更順滑的學習路徑。2.2 技術棧與關鍵庫解析確定了核心庫我們還需要幾個幫手來共同完成這個項目。Requests: 負責發(fā)送 HTTP 請求獲取服務器返回的數(shù)據(jù)HTML 或 JSON。它是我們與京東服務器對話的唯一工具。JSON: Python 內置庫。京東的評論數(shù)據(jù)接口返回的是 JSON 格式的字符串我們需要用這個庫將其解析成 Python 的字典或列表方便我們提取信息。Pandas (或 csv 模塊): 用于數(shù)據(jù)保存。Pandas 的DataFrame和to_csv、to_excel方法能非常優(yōu)雅地將結構化數(shù)據(jù)保存為文件。如果追求極簡Python 內置的csv模塊也完全夠用。這里我選擇 Pandas因為它后續(xù)做簡單數(shù)據(jù)分析更方便。Time / Random: 用于控制請求頻率。這是應對反爬的“道德”與“技術”必備手段。在請求間插入隨機延時可以模擬人類操作避免觸發(fā)服務器的“訪問頻率過高”限制。Re (正則表達式) 或 BeautifulSoup4: 用于解析數(shù)據(jù)。雖然核心數(shù)據(jù)來自 JSON 接口但有時我們需要從商品主頁 HTML 中提取一些初始參數(shù)如商品ID、店鋪ID。BeautifulSoup4 是更友好、更強大的 HTML 解析庫推薦使用。正則表達式則更靈活但編寫和維護難度稍大。注意不要一上來就安裝所有庫。建議先裝requests和pandas遇到解析需求時再裝beautifulsoup4。使用pip install requests pandas beautifulsoup4即可。2.3 反爬策略的預先規(guī)劃在寫第一行代碼之前我們必須想好如何應對反爬。京東常見的反爬手段包括請求頭校驗、IP 頻率限制、請求參數(shù)簽名、返回狀態(tài)碼 429 (Too Many Requests)。請求頭 (Headers): 這是最基本也是最重要的一環(huán)。我們的請求頭必須看起來像一個真實的瀏覽器。關鍵字段包括User-Agent用戶代理、Referer來源頁、Accept-Language接受語言等。缺少或使用默認的python-requests的 User-Agent會立刻被識別為爬蟲。請求頻率: 這是觸發(fā) 429 狀態(tài)碼的主要原因。我們必須在每次請求后強制休眠一段隨機時間。例如time.sleep(random.uniform(1, 3))表示休眠1到3秒之間的一個隨機浮點數(shù)秒。這能有效降低請求頻率。會話維持: 使用requests.Session()對象。Session 可以自動處理 Cookies在多次請求間保持一定的會話狀態(tài)比單次requests.get更接近真實用戶行為。異常處理與重試: 網(wǎng)絡是不穩(wěn)定的服務器也可能臨時出錯。我們必須用try...except包裹核心請求代碼并對特定的異常如連接超時、狀態(tài)碼非200設計重試機制。參數(shù)構造: 評論接口的 URL 通常包含一系列查詢參數(shù)如商品ID、頁碼、排序方式、評論類型等。這些參數(shù)必須完全模擬正常請求有時參數(shù)值可能經(jīng)過簡單編碼或計算。3. 實戰(zhàn)一步步拆解京東評論接口3.1 定位目標數(shù)據(jù)源找到真正的評論接口這是最關鍵的一步。我們不能去爬取商品詳情頁的 HTML因為評論是動態(tài)加載的。打開京東網(wǎng)站進入任意商品頁面例如一個手機。按下F12打開開發(fā)者工具切換到“Network” (網(wǎng)絡)選項卡。在商品頁面上找到“商品評價”區(qū)域點擊“只看當前商品評價”或者切換“好評”、“中評”、“差評”標簽同時觀察 Network 面板的變化。你會看到一個新的網(wǎng)絡請求出現(xiàn)其類型 (Type) 通常是fetch或xhr名稱可能包含fetchComment、productPageComments等關鍵詞。點擊這個請求查看其詳細信息。在“Headers”標簽頁你可以看到完整的請求 URL (Request URL)。這個 URL 就是我們要攻擊的目標。它通常很長包含https://club.jd.com/comment/productPageComments.action?這樣的基礎路徑后面跟著一串連接的參數(shù)。在“Preview”或“Response”標簽頁你可以看到服務器返回的數(shù)據(jù)正是結構化的 JSON 格式里面包含了評論列表、評論總數(shù)、分頁信息等。實操記錄以某款手機為例我抓取到的核心接口 URL 模式如下https://club.jd.com/comment/productPageComments.action?callbackfetchJSON_comment98productId100012345678score0sortType5page0pageSize10isShadowSku0fold1我們需要重點關注并提取其中的幾個關鍵參數(shù)productId: 商品ID從商品詳情頁的 URL 中就能找到。score: 評論類型。0-全部1-差評2-中評3-好評。page: 頁碼從0開始。pageSize: 每頁顯示的評論數(shù)最大可嘗試設置為30或50有時服務器會限制。sortType: 排序類型5通常表示按推薦排序6表示按時間排序。3.2 構造請求頭與參數(shù)有了接口 URL我們需要用 Python 來模擬這個請求。首先構造一個看起來像瀏覽器的請求頭。你可以直接從開發(fā)者工具的 “Headers” 里復制 “Request Headers” 部分。import requests import time import random import json # 定義一個常用的請求頭字典 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://item.jd.com/, # 替換為具體商品頁地址這很重要 Accept: application/json, text/javascript, */*; q0.01, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, # 注意requests 會自動處理 gzip這里寫上與瀏覽器一致即可 Connection: keep-alive, }接下來將 URL 中的查詢參數(shù)提取出來構造成一個獨立的參數(shù)字典這樣便于我們動態(tài)修改page和score。# 基礎參數(shù) params { callback: fetchJSON_comment98, # 有時需要有時不需要視接口而定 productId: 100012345678, # 替換為目標商品ID score: 0, # 0:全部 1:差評 2:中評 3:好評 sortType: 5, page: 0, # 頁碼從0開始 pageSize: 10, isShadowSku: 0, fold: 1 }3.3 發(fā)送請求與解析 JSON 數(shù)據(jù)現(xiàn)在我們可以使用requests.Session()發(fā)送請求了。使用 Session 的好處是它會自動管理 Cookies。# 創(chuàng)建會話 session requests.Session() session.headers.update(headers) # 為會話設置默認請求頭 try: # 發(fā)送 GET 請求 response session.get(https://club.jd.com/comment/productPageComments.action, paramsparams, timeout10) # 檢查請求是否成功 response.raise_for_status() # 如果狀態(tài)碼不是200將拋出HTTPError異常 # 打印狀態(tài)碼和部分響應內容用于調試 print(f狀態(tài)碼: {response.status_code}) print(f響應內容前500字符: {response.text[:500]}) except requests.exceptions.RequestException as e: print(f請求發(fā)生錯誤: {e}) # 這里可以加入重試邏輯如果請求成功我們會得到一段文本。但直接response.json()可能會失敗因為京東的接口返回的數(shù)據(jù)有時被一個函數(shù)調用包裹著例如fetchJSON_comment98({...})。我們需要先清理這個包裝。# 假設響應文本是 fetchJSON_comment98({...}) text response.text # 去除函數(shù)包裝和末尾的分號只提取 JSON 部分 if text.startswith(fetchJSON_comment98() and text.endswith();): json_str text[len(fetchJSON_comment98():-2] else: json_str text # 如果沒有包裝直接使用 # 將 JSON 字符串解析為 Python 字典 try: data_dict json.loads(json_str) except json.JSONDecodeError as e: print(fJSON 解析失敗: {e}) print(f原始文本: {text[:200]}) data_dict None if data_dict: # 提取評論列表 comments data_dict.get(comments, []) print(f本頁獲取到 {len(comments)} 條評論。) # 遍歷評論列表提取所需字段 for comment in comments: content comment.get(content, ) # 評論內容 creation_time comment.get(creationTime, ) # 評論時間 score comment.get(score, 0) # 評分5分制 # ... 提取其他你需要的字段如用戶昵稱、點贊數(shù)、回復等 print(f時間{creation_time}, 評分{score}, 內容{content[:50]}...)3.4 實現(xiàn)分頁爬取與分類邏輯單頁數(shù)據(jù)是遠遠不夠的。我們需要一個循環(huán)來爬取多頁數(shù)據(jù)并根據(jù)score參數(shù)來分類爬取。def fetch_comments_by_score(product_id, score_type, max_pages10): 根據(jù)評分類型爬取評論 :param product_id: 商品ID :param score_type: 1差評, 2中評, 3好評 :param max_pages: 最大爬取頁數(shù) :return: 評論數(shù)據(jù)列表 all_comments [] session requests.Session() session.headers.update(headers) base_params { productId: product_id, score: score_type, sortType: 5, pageSize: 10, isShadowSku: 0, fold: 1 } for page in range(0, max_pages): # 頁碼通常從0開始 print(f正在爬取 {score_type} 評價第 {page1} 頁...) params base_params.copy() params[page] page try: resp session.get(https://club.jd.com/comment/productPageComments.action, paramsparams, timeout15) resp.raise_for_status() # 清理和解析 JSON text resp.text # 這里需要根據(jù)實際接口返回調整清理邏輯 # 假設接口返回的是純JSON沒有回調函數(shù)包裝 page_data json.loads(text) comments page_data.get(comments, []) if not comments: # 如果當前頁沒有評論說明已爬完 print(f第 {page1} 頁無數(shù)據(jù)可能已爬取完畢。) break all_comments.extend(comments) print(f 本頁獲取 {len(comments)} 條累計 {len(all_comments)} 條。) # 關鍵隨機延時避免請求過快 sleep_time random.uniform(2, 5) print(f 等待 {sleep_time:.2f} 秒...) time.sleep(sleep_time) except requests.exceptions.RequestException as e: print(f第 {page1} 頁請求失敗: {e}) break except json.JSONDecodeError as e: print(f第 {page1} 頁 JSON 解析失敗: {e}) print(f 響應內容: {resp.text[:200]}) break return all_comments # 主程序 product_id 100012345678 # 替換為真實商品ID max_pages_per_score 20 # 每種評價類型最多爬20頁 # 分別爬取好評、中評、差評 print(開始爬取好評...) good_comments fetch_comments_by_score(product_id, 3, max_pages_per_score) print(開始爬取中評...) neutral_comments fetch_comments_by_score(product_id, 2, max_pages_per_score) print(開始爬取差評...) bad_comments fetch_comments_by_score(product_id, 1, max_pages_per_score) print(f爬取完成好評{len(good_comments)} 條 中評{len(neutral_comments)} 條 差評{len(bad_comments)} 條。)4. 數(shù)據(jù)清洗、保存與結構化4.1 從原始數(shù)據(jù)中提取關鍵字段爬取到的每條評論是一個字典里面包含大量信息。我們需要根據(jù)分析目標提取出最有用的字段。def extract_comment_info(comment_dict): 從單條評論字典中提取關鍵信息 info { 用戶ID: comment_dict.get(id), # 評論ID 用戶昵稱: comment_dict.get(nickname, 匿名用戶), 評論內容: comment_dict.get(content, ).strip().replace(\n, ), # 清洗換行符 評分: comment_dict.get(score, 0), # 1-5分 評論時間: comment_dict.get(creationTime, ), 點贊數(shù): comment_dict.get(usefulVoteCount, 0), 回復數(shù): comment_dict.get(replyCount, 0), 是否追評: comment_dict.get(afterUserComment, {}).get(hAfterUserComment, False), 商品顏色: comment_dict.get(productColor, ), 商品規(guī)格: comment_dict.get(productSize, ), 是否plus會員: comment_dict.get(plusAvailable, 0) 1, } # 有時圖片信息在一個嵌套的列表里 images comment_dict.get(images, []) info[圖片數(shù)量] len(images) if images else 0 info[圖片鏈接] ;.join([img.get(imgUrl, ) for img in images]) if images else return info # 應用提取函數(shù)到所有評論列表 good_comments_cleaned [extract_comment_info(c) for c in good_comments] neutral_comments_cleaned [extract_comment_info(c) for c in neutral_comments] bad_comments_cleaned [extract_comment_info(c) for c in bad_comments]4.2 使用 Pandas 進行分類保存將清洗后的數(shù)據(jù)列表轉換為 Pandas DataFrame然后保存為 CSV 或 Excel 文件。分類保存意味著我們要生成三個獨立的文件。import pandas as pd # 創(chuàng)建DataFrame df_good pd.DataFrame(good_comments_cleaned) df_neutral pd.DataFrame(neutral_comments_cleaned) df_bad pd.DataFrame(bad_comments_cleaned) # 定義文件名前綴 file_prefix fjd_comment_{product_id} # 保存為 CSV 文件 (推薦通用性好) df_good.to_csv(f{file_prefix}_good.csv, indexFalse, encodingutf-8-sig) # utf-8-sig 解決 Excel 打開中文亂碼 df_neutral.to_csv(f{file_prefix}_neutral.csv, indexFalse, encodingutf-8-sig) df_bad.to_csv(f{file_prefix}_bad.csv, indexFalse, encodingutf-8-sig) # 或者保存為 Excel 文件 (需要安裝 openpyxl: pip install openpyxl) # with pd.ExcelWriter(f{file_prefix}_all.xlsx, engineopenpyxl) as writer: # df_good.to_excel(writer, sheet_name好評, indexFalse) # df_neutral.to_excel(writer, sheet_name中評, indexFalse) # df_bad.to_excel(writer, sheet_name差評, indexFalse) print(f數(shù)據(jù)已保存。好評文件: {file_prefix}_good.csv)4.3 數(shù)據(jù)保存的進階技巧與注意事項增量爬取如果你需要定期更新評論可以記錄已爬取評論的 ID。每次爬蟲運行時先讀取已保存的文件獲取已有的評論ID集合然后只保存新出現(xiàn)的評論。異常數(shù)據(jù)記錄在爬取過程中可以將失敗的請求 URL、狀態(tài)碼、異常信息記錄到一個單獨的日志文件中便于后續(xù)排查和重試。數(shù)據(jù)去重雖然按頁爬取通常不會重復但為了嚴謹可以在保存前根據(jù)評論ID進行去重df.drop_duplicates(subset[‘用戶ID’], inplaceTrue)。文件組織可以為每個商品創(chuàng)建一個文件夾里面存放該商品的所有評論文件使項目結構更清晰。5. 高級話題應對反爬與提升穩(wěn)定性5.1 處理 429 Too Many Requests 錯誤這是爬蟲最常遇到的錯誤之一。當服務器檢測到你的請求頻率過高時就會返回 429 狀態(tài)碼。我們的防御策略是“預防為主處理為輔”。增加隨機延時如前所述time.sleep(random.uniform(2, 5))是基本操作。對于反爬嚴格的網(wǎng)站可以將休眠時間延長并增加隨機性例如random.uniform(5, 10)。使用代理 IP當單個 IP 被限制后最有效的方法是切換 IP。你可以使用付費或免費的代理 IP 池。在 requests 中使用代理非常簡單proxies { http: http://your_proxy_ip:port, https: http://your_proxy_ip:port, # 注意很多代理服務器的http和https協(xié)議都用http地址 } response session.get(url, paramsparams, proxiesproxies, timeout10)你需要自己管理代理IP的獲取、驗證和輪換邏輯。這是一個相對復雜的子系統(tǒng)。優(yōu)雅的重試機制當遇到 429 或網(wǎng)絡超時等錯誤時不應該立即崩潰而應該等待更長時間后重試。import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry # 配置重試策略 retry_strategy Retry( total3, # 總重試次數(shù) backoff_factor1, # 重試等待時間 {backoff factor} * (2 ** ({number of total retries} - 1)) status_forcelist[429, 500, 502, 503, 504], # 遇到這些狀態(tài)碼就重試 ) adapter HTTPAdapter(max_retriesretry_strategy) session requests.Session() session.mount(http://, adapter) session.mount(https://, adapter) # 然后使用這個 session 進行請求它會自動處理重試5.2 動態(tài)參數(shù)與簽名破解有些網(wǎng)站的接口參數(shù)是經(jīng)過加密或簽名的比如一個sign或token參數(shù)。對于京東評論的基礎接口目前通常不需要處理復雜的簽名。但如果未來接口升級你可能需要仔細對比請求在開發(fā)者工具中對比你手動點擊“下一頁”時發(fā)出的多個請求觀察哪些參數(shù)發(fā)生了變化變化的規(guī)律是什么。搜索 JavaScript 源碼在開發(fā)者工具的 “Sources” 面板搜索關鍵參數(shù)名如sign、token、_等找到生成該參數(shù)的 JavaScript 函數(shù)。使用 PyExecJS 或 Node.js 環(huán)境如果參數(shù)生成邏輯不復雜可以嘗試用 Python 重寫。如果邏輯復雜涉及瀏覽器環(huán)境、加密庫可以考慮使用PyExecJS庫來執(zhí)行 JavaScript 代碼片段從而計算得到正確的參數(shù)值。這是爬蟲進階路上的一道坎。5.3 代碼健壯性封裝將上述所有功能封裝成一個類會使代碼更易管理和復用。class JDCommentCrawler: def __init__(self, product_id, base_headersNone): self.product_id product_id self.session requests.Session() self.session.headers.update(base_headers or self._get_default_headers()) # 配置重試 retry_strategy Retry(total3, backoff_factor1, status_forcelist[429, 500, 502, 503, 504]) adapter HTTPAdapter(max_retriesretry_strategy) self.session.mount(http://, adapter) self.session.mount(https://, adapter) staticmethod def _get_default_headers(): return { ... } # 返回默認請求頭 def fetch_page(self, score, page): 爬取單頁數(shù)據(jù) params { ... } # 構造參數(shù) try: resp self.session.get(API_URL, paramsparams, timeout15) resp.raise_for_status() return self._parse_response(resp.text) except Exception as e: self._log_error(e, params) return None def crawl_by_score(self, score_type, max_pages): 爬取指定類型的所有評論 all_comments [] for page in range(max_pages): data self.fetch_page(score_type, page) if not data: break all_comments.extend(data) time.sleep(random.uniform(2, 4)) return all_comments def run(self): 主運行函數(shù) comments_map {} for score, name in [(3, good), (2, neutral), (1, bad)]: print(f開始爬取{name}評價...) comments self.crawl_by_score(score, 20) comments_map[name] comments self.save_to_csv(comments, name) return comments_map # ... 其他方法如 _parse_response, _log_error, save_to_csv 等6. 常見問題排查與實戰(zhàn)心得6.1 問題速查表問題現(xiàn)象可能原因解決方案返回狀態(tài)碼 403 Forbidden請求頭不完整或被識別為爬蟲特別是缺少Referer或User-Agent太假。1. 檢查并完善請求頭從瀏覽器直接復制。2. 嘗試添加Origin頭。3. 使用requests.Session()。返回狀態(tài)碼 429 Too Many Requests請求頻率過高IP 被暫時限制。1.立即大幅增加請求間隔如 sleep(5~10秒)。2. 暫停爬蟲一段時間如半小時后再試。3. 考慮使用代理IP。json.loads()解析失敗報錯JSONDecodeError響應內容不是純 JSON可能被回調函數(shù)包裝或返回了錯誤頁面如驗證碼。1. 打印response.text[:500]查看原始返回。2. 清理 JSONP 包裝如去除fetchJSON_comment98(...)。3. 檢查是否觸發(fā)了反爬返回了HTML。爬取到的comments列表為空但網(wǎng)頁上有評論1. 請求參數(shù)錯誤如productId,score。2. 接口已更新URL 或參數(shù)格式變化。3. 需要登錄才能查看。1. 用瀏覽器開發(fā)者工具抓取新的請求對比參數(shù)差異。2. 檢查score參數(shù)值是否正確0,1,2,3。3. 對于需登錄內容難度劇增需考慮模擬登錄或獲取Cookie。只能爬到前幾頁后面返回空數(shù)據(jù)1. 頁碼page參數(shù)可能不是從0開始或增長步長不是1。2. 服務器對非登錄用戶限制了查看頁數(shù)。1. 仔細分析翻頁時請求參數(shù)的變化。2. 嘗試在登錄狀態(tài)下抓取請求獲取完整的參數(shù)。保存的 CSV 文件用 Excel 打開中文亂碼CSV 文件默認使用 UTF-8 編碼而 Excel 在中文系統(tǒng)下默認預期是 GBK/ANSI。使用encodingutf-8-sig參數(shù)保存 CSV。utf-8-sig會在文件開頭添加 BOM 頭幫助 Excel 正確識別編碼。6.2 實操心得與避坑指南“慢就是快”對于爬蟲尤其是新手最大的美德就是“慢”。把請求間隔設置得足夠長比如3秒以上能規(guī)避90%的反爬問題。急于求成狂發(fā)請求只會導致IP被封得不償失。從瀏覽器復制不要自己編請求頭、URL 參數(shù)盡可能從瀏覽器開發(fā)者工具里直接復制。你的“想當然”很可能和服務器預期的不一樣。先驗證單次請求再構建循環(huán)不要一上來就寫for循環(huán)爬100頁。先確保你的代碼能正確獲取并解析第一頁的數(shù)據(jù)。用print把關鍵步驟的結果輸出看看確認無誤后再加循環(huán)和延時。關注接口的變化網(wǎng)站的接口不是一成不變的。今天能用的代碼下個月可能就失效了。如果突然爬不到數(shù)據(jù)第一件事就是重新用開發(fā)者工具抓包看看接口地址和參數(shù)有沒有變化。尊重robots.txt與法律邊界在爬取任何網(wǎng)站前理論上應該查看其robots.txt文件例如https://www.jd.com/robots.txt了解哪些路徑是允許爬取的。更重要的是爬取的數(shù)據(jù)應僅用于個人學習或合法的分析研究不得用于商業(yè)用途、不得惡意攻擊服務器、不得侵犯用戶隱私??刂婆廊∷俣缺苊鈱δ繕司W(wǎng)站造成負擔這是基本的網(wǎng)絡禮儀。數(shù)據(jù)存儲考慮如果爬取數(shù)據(jù)量很大CSV 文件可能會變得笨重。可以考慮使用 SQLitesqlite3庫或更專業(yè)的數(shù)據(jù)庫如 MySQL, PostgreSQL進行存儲便于查詢和管理。異常處理要具體不要只用except Exception一把抓。針對requests.exceptions.Timeout、ConnectionError、HTTPError以及JSONDecodeError分別進行處理可以讓你更清楚地知道程序在哪一環(huán)出了問題。這個項目從簡單的想法到能穩(wěn)定運行的工具涉及了網(wǎng)絡請求、數(shù)據(jù)解析、反爬策略、文件操作和代碼設計多個層面。核心不在于代碼多么復雜而在于對目標網(wǎng)站行為的細致觀察和對異常情況的周全考慮。當你成功運行起自己的爬蟲看到數(shù)據(jù)規(guī)整地保存到文件里時那種解決問題的成就感才是學習編程最大的樂趣之一。本文還有配套的精品資源點擊獲取