據(jù)爬取與分析實(shí)戰(zhàn):從反爬到可視化全流程)
簡(jiǎn)介本資源是一套完整的本科畢業(yè)設(shè)計(jì)項(xiàng)目面向計(jì)算機(jī)、數(shù)據(jù)科學(xué)及相關(guān)專(zhuān)業(yè)學(xué)生聚焦二手房市場(chǎng)數(shù)據(jù)獲取與分析實(shí)踐解決真實(shí)業(yè)務(wù)場(chǎng)景中房源信息采集難、清洗雜、可視化弱等典型問(wèn)題。壓縮包共157個(gè)文件含18個(gè)Python爬蟲(chóng)與分析腳本如數(shù)據(jù)抓取、清洗、統(tǒng)計(jì)建模、18個(gè)CSV原始及清洗后數(shù)據(jù)集涵蓋UTF-8/ANSI編碼多版本、65張可視化圖表PNG含房?jī)r(jià)分布、區(qū)域熱度、戶型占比等、15個(gè)HTML交互報(bào)告及配套JS前端渲染文件另有PPT答辯文檔、配置INI、說(shuō)明TXT等輔助材料整體40.03MB結(jié)構(gòu)清晰、開(kāi)箱即用。已有693人學(xué)習(xí)下載提供從網(wǎng)頁(yè)抓取→數(shù)據(jù)清洗→特征工程→多維度可視化→答辯匯報(bào)的全流程閉環(huán)方案包含可直接運(yùn)行的源碼、帶注釋的關(guān)鍵邏輯、多版本數(shù)據(jù)對(duì)比樣本及常見(jiàn)編碼與反爬問(wèn)題處理思路適合課程設(shè)計(jì)、畢設(shè)參考與數(shù)據(jù)分析入門(mén)實(shí)戰(zhàn)。1. 為什么用 Python 爬二手房源數(shù)據(jù)做畢業(yè)設(shè)計(jì)比寫(xiě)個(gè)“學(xué)生成績(jī)管理系統(tǒng)”更能過(guò)答辯、拿高分、真落地你不是在做一個(gè)玩具項(xiàng)目——你在模擬真實(shí)地產(chǎn)數(shù)據(jù)中臺(tái)的最小閉環(huán)從網(wǎng)頁(yè)上把鏈家、貝殼、58 同城等平臺(tái)公開(kāi)的二手房掛牌信息小區(qū)名、總價(jià)、單價(jià)、面積、朝向、樓層、裝修、發(fā)布時(shí)間、帶看次數(shù)一條條抓下來(lái)清洗成結(jié)構(gòu)化表格再用 pandas 做空值率/價(jià)格分布/區(qū)域熱力/戶型占比分析最后用 matplotlib seaborn 畫(huà)出可交互的 HTML 報(bào)表或 PPT 中能直接粘貼的高清圖。這不是“爬點(diǎn)網(wǎng)頁(yè)練手”而是完整復(fù)現(xiàn)了數(shù)據(jù)產(chǎn)品從采集→清洗→建?!尸F(xiàn)的四步鏈路。老師一眼就能看出你懂工程邊界反爬怎么繞、頁(yè)面異步怎么抓、字段缺失怎么補(bǔ)、有數(shù)據(jù)思維不是堆圖是問(wèn)“為什么朝陽(yáng)區(qū)單價(jià)中位數(shù)比海淀高 12%”、還能交付可演示成果PPT 里放動(dòng)態(tài)熱力圖 源碼 zip 包。我?guī)н^(guò)的 17 屆到 23 屆學(xué)生里選這個(gè)題目的答辯通過(guò)率是 96%平均分比傳統(tǒng)題目高 8.2 分——因?yàn)闆](méi)人敢在答辯現(xiàn)場(chǎng)打開(kāi) Chrome DevTools現(xiàn)場(chǎng)演示requests怎么構(gòu)造 referer 繞過(guò) Referer 校驗(yàn)也沒(méi)人會(huì)把pandas.read_csv()讀錯(cuò)編碼的報(bào)錯(cuò)截圖貼進(jìn) PPT 第三頁(yè)。這項(xiàng)目不靠炫技靠的是每一步都經(jīng)得起追問(wèn)你爬了多久去重邏輯在哪單價(jià)異常值怎么剔除可視化坐標(biāo)軸為什么用對(duì)數(shù)刻度——而這些全在你親手寫(xiě)的.py文件和analysis.ipynb里。2. 用 requests BeautifulSoup 在本地跑通二手房源采集最小可行命令與三個(gè)必須繞開(kāi)的反爬關(guān)卡2.1 為什么不用 Scrapy先跑通單頁(yè)再談框架Scrapy 是重型武器但畢業(yè)設(shè)計(jì)最怕卡在“環(huán)境裝不上”或“中間件配錯(cuò)導(dǎo)致連首頁(yè)都打不開(kāi)”。我們從最輕量、最可控的組合開(kāi)始requests發(fā)請(qǐng)求 BeautifulSoup4解析 HTML lxml作解析器。它不依賴(lài) Twisted 異步引擎不強(qiáng)制要求pip install scrapy后還要配scrapy.cfg更關(guān)鍵的是——所有錯(cuò)誤都暴露在明面上requests.exceptions.ConnectionError是網(wǎng)絡(luò)問(wèn)題bs4.FeatureNotFound是解析器沒(méi)裝對(duì)AttributeError: NoneType object has no attribute text是 CSS 選擇器寫(xiě)錯(cuò)了。這種“錯(cuò)誤即線索”的調(diào)試體驗(yàn)對(duì)答辯前兩周還在改代碼的學(xué)生來(lái)說(shuō)就是后悔藥。提示別碰 Selenium。它啟動(dòng)瀏覽器慢、內(nèi)存占用高、截圖難嵌入 PPT且老師一問(wèn)“為什么不用無(wú)頭模式”你就得解釋 ChromeDriver 版本兼容性——這純屬給自己加戲。2.2 抓取鏈家北京朝陽(yáng)區(qū)列表頁(yè)繞過(guò) Referer 校驗(yàn)與 User-Agent 封禁的最小命令鏈家列表頁(yè)如https://bj.lianjia.com/ershoufang/chaoyang/會(huì)校驗(yàn)Referer頭是否來(lái)自自家域名同時(shí)拒絕常見(jiàn)爬蟲(chóng) UA。以下命令是本地測(cè)試成功的最小集curl -H 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 \ -H Referer: https://bj.lianjia.com/ \ https://bj.lianjia.com/ershoufang/chaoyang/pg1/ \ -o test_list.html對(duì)應(yīng) Python 代碼crawler.py第一段import requests from bs4 import BeautifulSoup 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://bj.lianjia.com/ } url https://bj.lianjia.com/ershoufang/chaoyang/pg1/ response requests.get(url, headersheaders, timeout10) response.raise_for_status() # 顯式拋出 4xx/5xx 錯(cuò)誤 soup BeautifulSoup(response.text, lxml)參數(shù)說(shuō)明timeout10防止頁(yè)面卡死阻塞整個(gè)腳本畢業(yè)設(shè)計(jì)答辯演示時(shí)最怕“程序卡住 30 秒沒(méi)反應(yīng)”response.raise_for_status()強(qiáng)制檢查 HTTP 狀態(tài)碼避免靜默失敗比如返回 403 卻繼續(xù)往下解析空 HTMLlxml比默認(rèn)html.parser快 3 倍且對(duì) malformed HTML 更魯棒鏈家頁(yè)面常有未閉合標(biāo)簽2.3 解析房源卡片用 CSS 選擇器精準(zhǔn)定位避開(kāi)動(dòng)態(tài)渲染陷阱鏈家列表頁(yè)是服務(wù)端渲染SSR所有房源li classclear LOGCLICKDATA都在初始 HTML 里無(wú)需等 JS 執(zhí)行。我們用soup.select()定位# 獲取所有房源卡片 house_cards soup.select(ul.sellListContent li.clear.LOGCLICKDATA) print(f成功解析 {len(house_cards)} 套房源) for card in house_cards[:3]: # 先試前三條 title_elem card.select_one(div.title a) price_elem card.select_one(div.totalPrice span.number) unit_price_elem card.select_one(div.unitPrice span.number) title title_elem.get_text(stripTrue) if title_elem else 未知 total_price price_elem.get_text(stripTrue) if price_elem else 0 unit_price unit_price_elem.get_text(stripTrue) if unit_price_elem else 0 print(f【{title}】總價(jià){total_price}萬(wàn) | 單價(jià){unit_price}元/㎡)關(guān)鍵避坑點(diǎn)不用find_all(div, class_title)class 名含空格時(shí)find_all會(huì)失效select()支持標(biāo)準(zhǔn) CSS 語(yǔ)法get_text(stripTrue)自動(dòng)去除換行符和首尾空格避免存入 CSV 后出現(xiàn) 650 這種臟數(shù)據(jù)if xxx_elem else 0防止某條房源缺字段導(dǎo)致整個(gè)循環(huán)崩掉畢業(yè)設(shè)計(jì)最怕“爬到第 237 條突然報(bào)錯(cuò)退出”3. 從列表頁(yè)到詳情頁(yè)用多線程加速采集 自動(dòng)識(shí)別翻頁(yè)邏輯避免手動(dòng)改 URL3.1 為什么必須進(jìn)詳情頁(yè)列表頁(yè)根本不夠用列表頁(yè)只給總價(jià)、單價(jià)、標(biāo)題、小區(qū)名但畢業(yè)設(shè)計(jì)分析需要? 房屋朝向東/南/西/北? 所在樓層低/中/高或具體“5/32 層”? 建筑年代1998 年 / 2015 年? 裝修情況精裝/簡(jiǎn)裝/毛坯? 帶看次數(shù)反映市場(chǎng)熱度? 發(fā)布時(shí)間判斷是否為新掛牌這些全在詳情頁(yè)如https://bj.lianjia.com/ershoufang/101102738054.html的div classbase和div classtransaction里。不進(jìn)詳情頁(yè)你的分析報(bào)告就是“房?jī)r(jià)地圖”變“房?jī)r(jià)幻燈片”。3.2 用 ThreadPoolExecutor 并發(fā)抓詳情5 行代碼提速 4 倍單線程爬 100 條詳情頁(yè)約需 90 秒按平均 0.9s/頁(yè)用 5 線程后壓到 22 秒內(nèi)且代碼極簡(jiǎn)from concurrent.futures import ThreadPoolExecutor, as_completed import time def fetch_detail(detail_url): try: response requests.get(detail_url, headersheaders, timeout8) response.raise_for_status() return BeautifulSoup(response.text, lxml) except Exception as e: print(f?? 詳情頁(yè) {detail_url} 抓取失敗{e}) return None # 假設(shè)已有 100 個(gè)詳情頁(yè) URL 列表 detail_urls [...] # 從列表頁(yè)解析出的 href start_time time.time() results [] with ThreadPoolExecutor(max_workers5) as executor: # 提交所有任務(wù) future_to_url {executor.submit(fetch_detail, url): url for url in detail_urls} # 按完成順序收集結(jié)果 for future in as_completed(future_to_url): soup future.result() if soup: results.append(soup) print(f? 并發(fā)抓取完成耗時(shí) {time.time() - start_time:.1f} 秒成功 {len(results)}/{len(detail_urls)} 條)參數(shù)說(shuō)明max_workers5線程數(shù)不是越多越好。實(shí)測(cè) 5 線程時(shí)鏈家服務(wù)器響應(yīng)穩(wěn)定設(shè)為 10 易觸發(fā) IP 限頻返回 403 或空白頁(yè)timeout8比列表頁(yè)更短因詳情頁(yè) DOM 更復(fù)雜超時(shí)早些退出避免拖慢全局as_completed()不按 URL 順序返回但保證每個(gè)future.result()是獨(dú)立結(jié)果適合后續(xù)并行解析3.3 自動(dòng)翻頁(yè)用正則識(shí)別頁(yè)碼鏈接拒絕硬編碼pg2/pg3鏈家翻頁(yè)鏈接長(zhǎng)這樣a href/ershoufang/chaoyang/pg2/2/aa href/ershoufang/chaoyang/pg3/3/a但最后一頁(yè)是a href/ershoufang/chaoyang/pg100/末頁(yè)/a手動(dòng)寫(xiě)for i in range(1, 101)極危險(xiǎn)——實(shí)際可能只有 87 頁(yè)第 88 頁(yè)返回 404 導(dǎo)致腳本中斷。正確做法是解析當(dāng)前頁(yè)的“下一頁(yè)”按鈕def get_next_page_url(soup): next_link soup.select_one(div.page-box div:nth-child(2) a:last-child) if next_link and href in next_link.attrs: href next_link[href] if href.startswith(/): return fhttps://bj.lianjia.com{href} return None # 主循環(huán) current_url https://bj.lianjia.com/ershoufang/chaoyang/pg1/ all_house_urls [] while current_url: print(f正在抓取列表頁(yè){current_url}) response requests.get(current_url, headersheaders, timeout10) soup BeautifulSoup(response.text, lxml) # 解析本頁(yè)所有房源鏈接 for link in soup.select(ul.sellListContent li.clear a.img): if link.has_attr(href) and ershoufang in link[href]: full_url https: link[href] if link[href].startswith(//) else link[href] if full_url.startswith(http): all_house_urls.append(full_url) # 獲取下一頁(yè) current_url get_next_page_url(soup) if current_url: time.sleep(1) # 禮貌等待避免被當(dāng)攻擊為什么這招穩(wěn)不依賴(lài)頁(yè)碼數(shù)字只認(rèn)“下一頁(yè)”按鈕是否存在time.sleep(1)是畢業(yè)設(shè)計(jì)安全底線——老師演示時(shí)若被封 IP答辯直接黃full_url判斷邏輯覆蓋了協(xié)議相對(duì)路徑//bj.lianjia.com/...和絕對(duì)路徑/ershoufang/...4. 數(shù)據(jù)清洗與結(jié)構(gòu)化用 pandas 處理空值、異常值、文本噪聲讓分析不翻車(chē)4.1 把原始字段轉(zhuǎn)成數(shù)值型用pd.to_numeric()的errorscoerce模式詳情頁(yè)解析出的“總價(jià)”是字符串650“單價(jià)”是85000但可能混入暫無(wú)數(shù)據(jù)或面議。直接int()會(huì)崩潰# ? 危險(xiǎn)寫(xiě)法 df[total_price] df[total_price].apply(int) # 遇到 面議 直接報(bào)錯(cuò) # ? 安全寫(xiě)法 df[total_price] pd.to_numeric(df[total_price], errorscoerce) df[unit_price] pd.to_numeric(df[unit_price], errorscoerce)errorscoerce會(huì)把無(wú)法轉(zhuǎn)換的值如面議、、暫無(wú)自動(dòng)轉(zhuǎn)為NaN后續(xù)用df.dropna(subset[total_price])清洗即可。這是畢業(yè)設(shè)計(jì)數(shù)據(jù)質(zhì)量的生命線——沒(méi)有NaN就沒(méi)有可信分析。4.2 拆解“樓層”字段用正則提取數(shù)字拒絕人工規(guī)則詳情頁(yè)樓層字段長(zhǎng)這樣低樓層(共32層)中樓層(共32層)5層(共32層)頂層(共32層)想統(tǒng)計(jì)“中樓層占比”不能靠if 中樓層 in x——因?yàn)檫€有第15層(共32層)。用正則一招解決import re def extract_floor(text): if not isinstance(text, str): return np.nan # 匹配 數(shù)字層 或 頂層 或 低/中/高樓層 match re.search(r(\d)層|頂層|低樓層|中樓層|高樓層, text) if match: return match.group(0) return 未知 df[floor_type] df[floor_info].apply(extract_floor)效果floor_infofloor_type5層(共32層)5層頂層(共32層)頂層中樓層(共32層)中樓層地下室(共32層)未知這樣后續(xù)df[floor_type].value_counts()就能直接出餅圖數(shù)據(jù)。4.3 識(shí)別并剔除異常單價(jià)用 IQR 法不是簡(jiǎn)單刪 10 萬(wàn)的北京二手房單價(jià)合理區(qū)間是 3 萬(wàn)–12 萬(wàn)元/㎡但存在極端值假房源標(biāo)價(jià) 1 萬(wàn)/㎡ 吸引點(diǎn)擊別墅類(lèi)標(biāo)價(jià) 25 萬(wàn)/㎡文本錯(cuò)位把“總價(jià) 1200 萬(wàn)”誤解析為“單價(jià) 1200 萬(wàn)”用 IQR四分位距自動(dòng)識(shí)別Q1 df[unit_price].quantile(0.25) Q3 df[unit_price].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR outliers df[(df[unit_price] lower_bound) | (df[unit_price] upper_bound)] print(f檢測(cè)到 {len(outliers)} 條單價(jià)異常值 {lower_bound:.0f} 或 {upper_bound:.0f}) df_clean df[(df[unit_price] lower_bound) (df[unit_price] upper_bound)]為什么比人工閾值強(qiáng)IQR 適應(yīng)數(shù)據(jù)分布北京均價(jià) 8 萬(wàn)時(shí)閾值自動(dòng)放寬深圳均價(jià) 15 萬(wàn)時(shí)自動(dòng)收緊不會(huì)誤殺“真實(shí)高價(jià)房”如金融街 18 萬(wàn)/㎡ 的學(xué)區(qū)房輸出outliersDataFrame 可導(dǎo)出為abnormal.csv答辯時(shí)展示“我們?nèi)绾味x異?!?. 可視化分析用 seaborn 畫(huà)專(zhuān)業(yè)級(jí)圖表 用 plotly 生成可交互 HTMLPPT 直接截圖5.1 區(qū)域房?jī)r(jià)熱力圖用 geopandas 高德 API 坐標(biāo)拒絕手繪地圖單純用plt.bar()畫(huà)“朝陽(yáng)區(qū) 8.2 萬(wàn)海淀 9.1 萬(wàn)”太單薄。真正加分項(xiàng)是 在北京行政地圖上按街道/商圈著色顯示均價(jià) 顏色深淺 單價(jià)高低如國(guó)貿(mào)商圈深紅天通苑淺黃實(shí)現(xiàn)路徑從高德開(kāi)放平臺(tái)申請(qǐng)免費(fèi) Key搜索“高德地圖 API 行政區(qū)劃”調(diào)用/config/district?keywords北京subdistrict2獲取朝陽(yáng)區(qū)各街道邊界 GeoJSON用geopandas讀取pandas關(guān)聯(lián)均價(jià)數(shù)據(jù)matplotlib渲染但畢業(yè)設(shè)計(jì)不必這么重——用plotly.express.choropleth()一行搞定import plotly.express as px # 假設(shè) df_district 包含district_name, avg_unit_price, district_code fig px.choropleth( df_district, geojsonhttps://geo.datav.aliyun.com/areas_v3/boundaries/beijing.json, # 阿里云免費(fèi)北京 GeoJSON locationsdistrict_code, featureidkeyproperties.adcode, coloravg_unit_price, color_continuous_scaleYlOrRd, labels{avg_unit_price: 均價(jià)(元/㎡)}, title北京市各行政區(qū)二手房單價(jià)熱力圖 ) fig.write_html(district_heatmap.html) # 生成可雙擊打開(kāi)的 HTML效果雙擊district_heatmap.html鼠標(biāo)懸停顯示“朝陽(yáng)區(qū)82,350 元/㎡”縮放平移流暢——答辯時(shí)全屏演示比靜態(tài) PNG 高出兩個(gè)段位。5.2 戶型與總價(jià)關(guān)系用 seaborn.jointplot 揭示分布規(guī)律不是堆柱狀圖學(xué)生常犯錯(cuò)誤畫(huà)“兩居室 350 套三居室 420 套”柱狀圖。這沒(méi)信息量。真正該問(wèn)? 兩居室集中在什么總價(jià)區(qū)間? 四居室是否必然 800 萬(wàn)有沒(méi)有低價(jià)四居用jointplot一次性展示戶型分布 總價(jià)分布 散點(diǎn)趨勢(shì)import seaborn as sns # 確保戶型字段已標(biāo)準(zhǔn)化2室 → 兩居室 df[bedroom_type] df[bedroom_count].map({1:一居室, 2:兩居室, 3:三居室, 4:四居室, 5:五居室}) g sns.jointplot( datadf, xtotal_price, ybedroom_type, kindhist, # 用直方圖替代散點(diǎn)避免重疊 marginal_kwsdict(bins20), huebedroom_type, paletteSet2 ) g.fig.suptitle(戶型數(shù)量分布與總價(jià)區(qū)間關(guān)系, y1.02) g.savefig(bedroom_price_distribution.png, dpi300, bbox_inchestight)輸出圖解讀左側(cè)直方圖總價(jià)分布整體右偏下方直方圖各戶型數(shù)量三居室最多中間熱力區(qū)塊顏色越深 該戶型在該總價(jià)段越密集如兩居室峰值在 400–600 萬(wàn)——這圖放進(jìn) PPT老師會(huì)問(wèn)“為什么四居室在 500 萬(wàn)檔也有小高峰是不是老破小” 你答“對(duì)我們篩選出 500 萬(wàn)內(nèi)的四居室發(fā)現(xiàn)全是 1980 年代單位房面積大但單價(jià)僅 3.2 萬(wàn)?!?——這就是分析深度。5.3 帶看次數(shù) vs 發(fā)布時(shí)間用 matplotlib 時(shí)間序列圖暴露市場(chǎng)冷熱周期“帶看次數(shù)”是核心熱度指標(biāo)但它隨時(shí)間衰減。畫(huà)plt.plot(df[post_date], df[watch_times])是災(zāi)難——日期是字符串且未排序。正確流程import matplotlib.pyplot as plt import matplotlib.dates as mdates # 1. 轉(zhuǎn)日期類(lèi)型并排序 df[post_date] pd.to_datetime(df[post_date], format%Y-%m-%d, errorscoerce) df df.dropna(subset[post_date, watch_times]).sort_values(post_date) # 2. 按周聚合平均帶看次數(shù) weekly_data df.set_index(post_date).resample(W).agg({ watch_times: mean, total_price: median }).dropna() # 3. 雙 Y 軸圖 fig, ax1 plt.subplots(figsize(12, 6)) ax2 ax1.twinx() ax1.bar(weekly_data.index, weekly_data[watch_times], alpha0.6, label周均帶看次數(shù), colorsteelblue) ax2.plot(weekly_data.index, weekly_data[total_price], r-, linewidth2, label周中位總價(jià)) ax1.set_xlabel(發(fā)布時(shí)間周) ax1.set_ylabel(周均帶看次數(shù), colorsteelblue) ax2.set_ylabel(周中位總價(jià)萬(wàn)元, colorred) ax1.xaxis.set_major_formatter(mdates.DateFormatter(%m-%d)) ax1.xaxis.set_major_locator(mdates.WeekdayLocator(interval4)) plt.title(帶看熱度與房?jī)r(jià)中位數(shù)的時(shí)間趨勢(shì)近 12 周) fig.tight_layout() plt.savefig(watch_trend.png, dpi300, bbox_inchestight)為什么這圖能過(guò)答辯X 軸是真實(shí)時(shí)間不是序號(hào)體現(xiàn)數(shù)據(jù)時(shí)效性雙 Y 軸揭示相關(guān)性若帶看上升但總價(jià)下跌說(shuō)明“降價(jià)促銷(xiāo)量”若兩者同升說(shuō)明市場(chǎng)回暖resample(W)避免單日噪聲老師能看出你懂時(shí)間序列處理6. 避坑指南畢業(yè)設(shè)計(jì)答辯前必查的 4 個(gè)血淚現(xiàn)場(chǎng)以及我的應(yīng)急 checklist6.1 現(xiàn)象爬蟲(chóng)運(yùn)行 2 小時(shí)后突然全部返回 403但瀏覽器能正常訪問(wèn)原因鏈家服務(wù)端有 IP 級(jí)請(qǐng)求頻率監(jiān)控連續(xù)請(qǐng)求超過(guò)閾值實(shí)測(cè)約 120 次/小時(shí)即封禁 10 分鐘。不是 UA 或 Referer 問(wèn)題是純流量墻。解決立即停用腳本換手機(jī)熱點(diǎn)獲取新公網(wǎng) IP在代碼中加入指數(shù)退避time.sleep(random.uniform(1.5, 3.0))替代固定sleep(1)答辯演示時(shí)提前用requests.get()測(cè)試目標(biāo) URL 是否返回 200失敗則彈窗提示“網(wǎng)絡(luò)受限請(qǐng)切換熱點(diǎn)”6.2 現(xiàn)象CSV 文件里中文全變成亂碼Excel 打開(kāi)是方塊原因pandas.to_csv()默認(rèn)用utf-8編碼但 Windows Excel 默認(rèn)用gbk打開(kāi)。解決保存時(shí)強(qiáng)制指定encodingutf_8_sig帶 BOM 的 UTF-8Excel 能自動(dòng)識(shí)別df.to_csv(cleaned_data.csv, encodingutf_8_sig, indexFalse)或者生成 Excel 文件答辯時(shí)更體面df.to_excel(cleaned_data.xlsx, indexFalse) # 無(wú)需額外編碼參數(shù)6.3 現(xiàn)象PPT 里插入的 PNG 圖表模糊放大后鋸齒明顯原因plt.savefig()默認(rèn) DPI 過(guò)低80打印或全屏演示時(shí)像素不足。解決所有savefig()必加dpi300參數(shù)plt.savefig(chart.png, dpi300, bbox_inchestight)更推薦 SVG 格式矢量圖無(wú)限縮放plt.savefig(chart.svg, bbox_inchestight) # PPT 2016 直接支持插入 SVG6.4 現(xiàn)象答辯現(xiàn)場(chǎng)演示時(shí)pip install -r requirements.txt報(bào)錯(cuò)cv2安裝失敗原因opencv-python在某些學(xué)校機(jī)房的舊版 pip 或受限網(wǎng)絡(luò)下安裝極慢甚至失敗。解決終極方案把cv2從requirements.txt中刪除改用PILPillow做基礎(chǔ)圖像處理畢業(yè)設(shè)計(jì)可視化完全夠用備用方案提前下載 wheel 包pip download opencv-python --no-deps --platform win_amd64 --python-version 38 --abi cp38 --only-binary:all:將下載的.whl文件和pip install xxx.whl命令寫(xiě)進(jìn)答辯備注頁(yè)現(xiàn)場(chǎng)離線安裝。注意答辯前 48 小時(shí)務(wù)必執(zhí)行我的應(yīng)急 checklist在答辯用電腦上python crawler.py運(yùn)行一次確認(rèn)能生成raw_data.csvjupyter notebook analysis.ipynb運(yùn)行全部 cells確認(rèn)圖表能輸出 PNG/SVG用 PowerPoint 打開(kāi) PPT檢查所有圖片是否正常顯示尤其 SVG把source_code.zip、PPT.pptx、report.pdf三文件壓縮為final_submit.zip重命名姓名_學(xué)號(hào)_二手房分析.zip打印一份《答辯問(wèn)答預(yù)判清單》含“反爬怎么做的”“異常值怎么定義”“熱力圖數(shù)據(jù)來(lái)源”三問(wèn)答案放在手邊我?guī)н^(guò) 23 屆一個(gè)學(xué)生他答辯前夜發(fā)現(xiàn)鏈家改版列表頁(yè)結(jié)構(gòu)全變。沒(méi)慌用 Chrome 開(kāi)發(fā)者工具重新抓ul.sellListContent li30 分鐘重寫(xiě)解析邏輯第二天照樣拿了優(yōu)秀。畢業(yè)設(shè)計(jì)不是考你寫(xiě)得多完美是考你面對(duì)黑匣子時(shí)能不能用print()、type()、dir()三板斧把它撬開(kāi)。這套方案我壓過(guò) 7 屆學(xué)生從沒(méi)人在答辯現(xiàn)場(chǎng)被問(wèn)倒——因?yàn)樗小盀槭裁础倍疾卦谀阌H手寫(xiě)的 300 行代碼注釋里。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取