據(jù)采集的智能房產(chǎn)分析系統(tǒng)構(gòu)建實(shí)踐)
1. 項(xiàng)目概述當(dāng)AI Agent成為你的“數(shù)字房產(chǎn)顧問(wèn)”最近幾年身邊想買房的朋友聊起看房經(jīng)歷總繞不開(kāi)一個(gè)詞信息過(guò)載。從貝殼、鏈家到各種本地論壇小區(qū)信息、戶型圖、成交價(jià)、業(yè)主評(píng)價(jià)……數(shù)據(jù)散落在各處真?zhèn)坞y辨。更頭疼的是當(dāng)你試圖橫向比較幾個(gè)心儀的小區(qū)時(shí)光是整理各自的優(yōu)缺點(diǎn)、周邊配套、歷史價(jià)格走勢(shì)就足以讓人望而卻步。傳統(tǒng)的做法是依賴房產(chǎn)中介的口頭介紹或者自己花大量時(shí)間做Excel表格但前者可能帶有傾向性后者則效率低下且容易遺漏關(guān)鍵信息。這正是“用Agent幫我買房”這個(gè)項(xiàng)目想解決的核心痛點(diǎn)。它不是一個(gè)簡(jiǎn)單的信息聚合工具而是一個(gè)由AI驅(qū)動(dòng)的、自動(dòng)化的“數(shù)字房產(chǎn)顧問(wèn)”。其核心邏輯是將購(gòu)房決策中繁瑣、重復(fù)的信息搜集、整理、分析工作交給一個(gè)智能體Agent去完成。這個(gè)Agent能夠根據(jù)你的初步需求比如預(yù)算、區(qū)域、戶型偏好自動(dòng)在互聯(lián)網(wǎng)上爬取目標(biāo)小區(qū)的多維數(shù)據(jù)然后進(jìn)行分析、對(duì)比并最終生成一份結(jié)構(gòu)清晰、圖文并茂的測(cè)評(píng)報(bào)告。這相當(dāng)于為你配備了一個(gè)不知疲倦、絕對(duì)客觀、且數(shù)據(jù)處理能力超強(qiáng)的私人助理。這個(gè)項(xiàng)目的價(jià)值在于它將購(gòu)房決策從一種依賴經(jīng)驗(yàn)和運(yùn)氣的“藝術(shù)”部分轉(zhuǎn)變?yōu)榛跀?shù)據(jù)和邏輯的“科學(xué)”。對(duì)于購(gòu)房者而言它極大地提升了信息獲取的效率和決策的理性程度對(duì)于房產(chǎn)領(lǐng)域的從業(yè)者或研究者它則提供了一套可復(fù)用的自動(dòng)化分析框架。整個(gè)過(guò)程涉及幾個(gè)關(guān)鍵技術(shù)環(huán)節(jié)需求理解與任務(wù)規(guī)劃、多源數(shù)據(jù)采集與清洗、數(shù)據(jù)分析與測(cè)評(píng)模型構(gòu)建、以及最終的圖文報(bào)告生成。接下來(lái)我將拆解這其中的每一個(gè)環(huán)節(jié)分享我是如何從零搭建這樣一個(gè)系統(tǒng)的以及過(guò)程中踩過(guò)的坑和總結(jié)的經(jīng)驗(yàn)。2. 核心思路與系統(tǒng)架構(gòu)設(shè)計(jì)在動(dòng)手寫代碼之前明確整個(gè)系統(tǒng)的設(shè)計(jì)思路至關(guān)重要。一個(gè)高效的購(gòu)房Agent不應(yīng)該是一個(gè)“一次性腳本”而應(yīng)該是一個(gè)模塊化、可擴(kuò)展的智能系統(tǒng)。我的設(shè)計(jì)核心是“任務(wù)驅(qū)動(dòng)”和“流水線作業(yè)”。2.1 以任務(wù)規(guī)劃為核心的智能體設(shè)計(jì)傳統(tǒng)的爬蟲(chóng)程序是“死”的它只會(huì)按照預(yù)設(shè)的規(guī)則去抓取固定結(jié)構(gòu)的數(shù)據(jù)。而AI Agent的“智能”首先體現(xiàn)在任務(wù)規(guī)劃能力上。我的系統(tǒng)入口是一個(gè)大型語(yǔ)言模型LLM例如GPT-4或國(guó)內(nèi)可用的深度求索、智譜AI的模型。用戶用自然語(yǔ)言提出需求如“幫我找一下北京海淀區(qū)預(yù)算800萬(wàn)左右房齡10年以內(nèi)帶學(xué)區(qū)的小三居并對(duì)比一下‘橡樹(shù)灣’和‘清楓華景園’這兩個(gè)小區(qū)?!盠LM的核心工作是將這個(gè)模糊的需求解析并分解成一個(gè)可執(zhí)行的、結(jié)構(gòu)化的任務(wù)清單Task List。這個(gè)過(guò)程我稱之為“需求工程化”。例如上述需求可能被分解為數(shù)據(jù)采集任務(wù)采集“橡樹(shù)灣”和“清楓華景園”兩個(gè)小區(qū)的基礎(chǔ)信息地址、開(kāi)發(fā)商、物業(yè)費(fèi)、容積率等。市場(chǎng)數(shù)據(jù)任務(wù)采集兩個(gè)小區(qū)近一年的歷史成交價(jià)格、當(dāng)前在售房源及報(bào)價(jià)。配套數(shù)據(jù)任務(wù)采集兩個(gè)小區(qū)周邊的教育資源學(xué)校名稱、距離、評(píng)級(jí)、交通地鐵站、公交線路、商業(yè)商場(chǎng)、超市、醫(yī)療等數(shù)據(jù)。輿情數(shù)據(jù)任務(wù)采集房產(chǎn)論壇、社交媒體上關(guān)于這兩個(gè)小區(qū)的業(yè)主評(píng)價(jià)、討論熱點(diǎn)。分析對(duì)比任務(wù)基于以上數(shù)據(jù)從價(jià)格、房齡、學(xué)區(qū)、交通、居住體驗(yàn)等多個(gè)維度進(jìn)行量化對(duì)比。報(bào)告生成任務(wù)將分析結(jié)果整合成一份包含文字、圖表、總結(jié)建議的測(cè)評(píng)報(bào)告。這個(gè)任務(wù)清單就是整個(gè)Agent執(zhí)行的“藍(lán)圖”。LLM不僅生成清單還會(huì)為每個(gè)任務(wù)分配合適的工具Tool。例如采集房產(chǎn)交易數(shù)據(jù)需要調(diào)用“鏈家/貝殼數(shù)據(jù)采集器”采集周邊配套需要調(diào)用“高德/百度地圖POI查詢接口”采集輿情需要調(diào)用“特定論壇爬蟲(chóng)”。注意任務(wù)規(guī)劃的準(zhǔn)確性直接決定最終結(jié)果的質(zhì)量。在實(shí)踐中我發(fā)現(xiàn)給LLM提供一個(gè)清晰的“角色設(shè)定”Role和“任務(wù)規(guī)劃模板”非常有效。例如我會(huì)在系統(tǒng)提示詞System Prompt中明確“你是一名資深房產(chǎn)數(shù)據(jù)分析師擅長(zhǎng)將用戶的購(gòu)房需求拆解為具體、可執(zhí)行的數(shù)據(jù)查詢與分析步驟?!辈⑻峁┮粋€(gè)JSON格式的任務(wù)輸出示例能顯著提高任務(wù)分解的結(jié)構(gòu)化和穩(wěn)定性。2.2 模塊化流水線架構(gòu)基于任務(wù)清單我設(shè)計(jì)了一個(gè)四層流水線架構(gòu)確保各模塊職責(zé)清晰便于維護(hù)和迭代。第一層控制與調(diào)度層Orchestrator這是系統(tǒng)的大腦通常由一個(gè)主控程序?qū)崿F(xiàn)。它負(fù)責(zé)與用戶交互調(diào)用LLM進(jìn)行任務(wù)規(guī)劃然后根據(jù)任務(wù)類型將子任務(wù)分發(fā)給下游相應(yīng)的模塊執(zhí)行并監(jiān)控整個(gè)流程的狀態(tài)。我選擇用Python的異步框架如asyncio來(lái)構(gòu)建這一層因?yàn)閿?shù)據(jù)采集往往是I/O密集型任務(wù)異步能極大提升并發(fā)效率。第二層數(shù)據(jù)采集層Data Collectors這是系統(tǒng)的“手和腳”由一系列針對(duì)不同數(shù)據(jù)源的采集器組成。每個(gè)采集器都是一個(gè)獨(dú)立的工具Tool。例如房產(chǎn)平臺(tái)采集器針對(duì)鏈家、貝殼等網(wǎng)站使用Playwright或Selenium模擬瀏覽器行為繞過(guò)簡(jiǎn)單的反爬機(jī)制抓取房源列表、詳情頁(yè)信息。地圖POI采集器調(diào)用高德/百度地圖的Web服務(wù)API通過(guò)小區(qū)坐標(biāo)搜索周邊特定類別的POI點(diǎn)信息如學(xué)校、地鐵、商場(chǎng)等并計(jì)算距離。輿情爬蟲(chóng)針對(duì)豆瓣買房小組、本地論壇等使用requestsBeautifulSoup或Scrapy框架進(jìn)行定向抓取和關(guān)鍵詞提取。公開(kāi)數(shù)據(jù)采集器獲取行政區(qū)劃、人口數(shù)據(jù)、規(guī)劃文件等公開(kāi)信息。第三層數(shù)據(jù)處理與分析層Data Engine采集到的原始數(shù)據(jù)是雜亂無(wú)章的。這一層負(fù)責(zé)清洗、結(jié)構(gòu)化、存儲(chǔ)和分析。數(shù)據(jù)清洗處理缺失值、統(tǒng)一格式如將“3室2廳”統(tǒng)一為“3室2廳”、去重、識(shí)別異常價(jià)格等。數(shù)據(jù)存儲(chǔ)使用關(guān)系型數(shù)據(jù)庫(kù)如PostgreSQL存儲(chǔ)結(jié)構(gòu)化的房源、小區(qū)信息使用Elasticsearch存儲(chǔ)和檢索文本輿情數(shù)據(jù)使用對(duì)象存儲(chǔ)如AWS S3或MinIO保存爬取的原始網(wǎng)頁(yè)快照或生成的圖片。分析模型這是體現(xiàn)“智能”的關(guān)鍵。我會(huì)構(gòu)建一系列分析指標(biāo)例如價(jià)格分析計(jì)算小區(qū)均價(jià)、近期漲跌幅、與周邊小區(qū)的價(jià)格對(duì)比。性價(jià)比模型結(jié)合單價(jià)、房齡、物業(yè)費(fèi)、容積率、綠化率等計(jì)算一個(gè)綜合得分。配套評(píng)分根據(jù)學(xué)校、地鐵、商場(chǎng)的數(shù)量、距離和等級(jí)為“教育”、“交通”、“商業(yè)”等維度打分。輿情情感分析使用預(yù)訓(xùn)練的情感分析模型如SnowNLP或BERT微調(diào)模型對(duì)爬取的文本進(jìn)行正面、負(fù)面、中性情感判斷提煉業(yè)主主要抱怨點(diǎn)如物業(yè)、噪音和稱贊點(diǎn)。第四層報(bào)告生成層Report Generator這是系統(tǒng)的“嘴”負(fù)責(zé)輸出最終成果。它接收分析層產(chǎn)出的結(jié)構(gòu)化數(shù)據(jù)JSON或DataFrame再次調(diào)用LLM但這次是用于“內(nèi)容創(chuàng)作”。我會(huì)給LLM提供詳細(xì)的報(bào)告模板指令例如“請(qǐng)生成一份面向購(gòu)房者的測(cè)評(píng)報(bào)告需包含1. 小區(qū)概況表格2. 價(jià)格走勢(shì)與分析3. 配套設(shè)施對(duì)比雷達(dá)圖4. 優(yōu)缺點(diǎn)總結(jié)5. 購(gòu)買建議。”同時(shí)系統(tǒng)會(huì)用Matplotlib或Plotly等庫(kù)根據(jù)數(shù)據(jù)自動(dòng)生成圖表折線圖、雷達(dá)圖、柱狀圖將圖片保存后把圖片路徑和數(shù)據(jù)分析結(jié)果一并交給LLM讓它撰寫包含圖片引用的Markdown或HTML格式報(bào)告。整個(gè)架構(gòu)就像一個(gè)現(xiàn)代化工廠的流水線用戶需求是訂單Orchestrator是生產(chǎn)計(jì)劃部Data Collectors是原料采購(gòu)車間Data Engine是加工裝配車間Report Generator是產(chǎn)品包裝和出廠部門。各司其職協(xié)同作業(yè)。3. 關(guān)鍵技術(shù)實(shí)現(xiàn)與踩坑實(shí)錄有了清晰的架構(gòu)接下來(lái)就是具體的實(shí)現(xiàn)。這里我分享幾個(gè)最核心也最容易出問(wèn)題的技術(shù)環(huán)節(jié)。3.1 多源異構(gòu)數(shù)據(jù)的采集策略數(shù)據(jù)是分析的基石。房產(chǎn)數(shù)據(jù)源眾多且結(jié)構(gòu)各異必須采用“分而治之”的策略。對(duì)于房產(chǎn)交易平臺(tái)如貝殼這是核心數(shù)據(jù)源。早期我用requests直接抓取但很快遭遇了動(dòng)態(tài)渲染和反爬。后來(lái)切換到Playwright它能夠完整模擬瀏覽器環(huán)境輕松應(yīng)對(duì)由JavaScript渲染的頁(yè)面。關(guān)鍵技巧在于設(shè)置合理的等待時(shí)間使用page.wait_for_selector或page.wait_for_load_state(networkidle)確保目標(biāo)元素加載完成而不是用固定的sleep后者效率低且不穩(wěn)定。使用代理IP池大規(guī)模采集時(shí)必須使用高質(zhì)量的住宅代理IP并設(shè)置訪問(wèn)頻率限制模擬真人操作。一個(gè)常見(jiàn)的坑是免費(fèi)代理IP的可用性極低會(huì)嚴(yán)重拖慢進(jìn)度并導(dǎo)致大量失敗。數(shù)據(jù)解析的健壯性網(wǎng)頁(yè)結(jié)構(gòu)可能變動(dòng)。不能只依賴固定的CSS選擇器路徑。我會(huì)結(jié)合多種選擇器如text()內(nèi)容匹配、相對(duì)位置來(lái)定位關(guān)鍵信息并加入異常重試和日志記錄機(jī)制。對(duì)于地圖POI數(shù)據(jù)高德、百度等地圖開(kāi)放平臺(tái)提供了豐富的API。這里的關(guān)鍵是配額管理和數(shù)據(jù)去噪。以高德為例免費(fèi)配額對(duì)于個(gè)人項(xiàng)目初期是夠用的。調(diào)用“周邊搜索”API時(shí)需要以小區(qū)經(jīng)緯度為中心分類別學(xué)校、地鐵、商場(chǎng)等、分距離進(jìn)行多次請(qǐng)求。拿到數(shù)據(jù)后需要清洗掉重復(fù)的、無(wú)關(guān)的POI例如搜索“小學(xué)”可能會(huì)返回“成人教育培訓(xùn)學(xué)?!薄?duì)于輿情數(shù)據(jù)論壇和社交媒體的數(shù)據(jù)非結(jié)構(gòu)化程度高。我的策略是“抓取-篩選-分析”。先批量抓取包含小區(qū)名稱關(guān)鍵詞的帖子標(biāo)題和內(nèi)容然后通過(guò)簡(jiǎn)單的規(guī)則如排除廣告帖、租房帖和文本分類模型進(jìn)行初篩最后對(duì)保留下來(lái)的高質(zhì)量討論進(jìn)行情感和主題分析。這里要注意遵守網(wǎng)站的robots.txt協(xié)議并控制抓取速度避免對(duì)目標(biāo)網(wǎng)站造成壓力。實(shí)操心得不要試圖用一個(gè)爬蟲(chóng)通吃所有網(wǎng)站。為每個(gè)重要的數(shù)據(jù)源編寫?yīng)毩⒌摹⒕木S護(hù)的采集模塊。每個(gè)模塊都應(yīng)具備獨(dú)立的錯(cuò)誤處理、重試邏輯和日志記錄。將采集任務(wù)設(shè)計(jì)成“冪等”的即失敗后重跑不會(huì)導(dǎo)致數(shù)據(jù)重復(fù)或混亂。3.2 基于LLM的任務(wù)規(guī)劃與報(bào)告生成LLM是系統(tǒng)的“智能”擔(dān)當(dāng)?shù)涫褂貌⒎呛?jiǎn)單調(diào)用API那么簡(jiǎn)單。任務(wù)規(guī)劃提示詞工程這是決定Agent是否“聽(tīng)話”的關(guān)鍵。我的提示詞結(jié)構(gòu)如下你是一個(gè)房產(chǎn)分析專家AI助手。請(qǐng)根據(jù)用戶需求生成一個(gè)JSON格式的任務(wù)執(zhí)行計(jì)劃。 用戶需求{user_query} 請(qǐng)按照以下步驟思考并輸出 1. 理解用戶的核心訴求預(yù)算、區(qū)域、戶型、特殊要求等。 2. 識(shí)別出需要分析和對(duì)比的小區(qū)名稱至少兩個(gè)。 3. 為每個(gè)小區(qū)規(guī)劃需要收集的數(shù)據(jù)類別包括基礎(chǔ)信息、價(jià)格信息、周邊配套教育、交通、商業(yè)、醫(yī)療、市場(chǎng)輿情。 4. 將數(shù)據(jù)收集任務(wù)映射到具體的工具上工具列表見(jiàn)下文。 5. 最后規(guī)劃分析對(duì)比維度和報(bào)告大綱。 可用工具列表 - collect_property_basic_info: 采集小區(qū)基礎(chǔ)信息。 - collect_latest_transaction_prices: 采集近期成交價(jià)。 - collect_surrounding_poi: 采集周邊設(shè)施信息。 - collect_public_opinion: 采集網(wǎng)絡(luò)輿情。 - analyze_compare: 執(zhí)行多維度對(duì)比分析。 請(qǐng)輸出如下JSON格式 { target_neighborhoods: [小區(qū)A, 小區(qū)B], tasks: [ {tool: collect_property_basic_info, params: {name: 小區(qū)A}}, {tool: collect_surrounding_poi, params: {name: 小區(qū)A, category: 學(xué)校}}, // ... 更多任務(wù) ], analysis_dimensions: [價(jià)格, 房齡, 學(xué)區(qū), 交通便利性, 居住密度], report_outline: [一、概述, 二、數(shù)據(jù)總覽, 三、分維度對(duì)比, 四、綜合測(cè)評(píng)與建議] }通過(guò)這樣結(jié)構(gòu)化的引導(dǎo)LLM輸出的任務(wù)計(jì)劃非常穩(wěn)定易于被后續(xù)程序解析和執(zhí)行。報(bào)告生成中的“幻覺(jué)”控制讓LLM根據(jù)數(shù)據(jù)和圖表寫報(bào)告時(shí)最大的風(fēng)險(xiǎn)是它可能“捏造”數(shù)據(jù)或做出無(wú)根據(jù)的推論。我的解決方案是“嚴(yán)格的數(shù)據(jù)上下文注入”。在調(diào)用LLM生成報(bào)告的請(qǐng)求中我會(huì)將清洗后的結(jié)構(gòu)化數(shù)據(jù)以表格或列表形式和生成的圖表描述如“圖1顯示了小區(qū)A和B近半年價(jià)格走勢(shì)對(duì)比其中A小區(qū)呈緩慢上升趨勢(shì)B小區(qū)相對(duì)平穩(wěn)”作為系統(tǒng)提示詞的一部分強(qiáng)制提供。同時(shí)在用戶指令中明確強(qiáng)調(diào)“你的所有結(jié)論必須嚴(yán)格基于我提供的數(shù)據(jù)和圖表不得編造任何未被提供的信息。如果數(shù)據(jù)不足以支持某項(xiàng)判斷請(qǐng)明確說(shuō)明‘根據(jù)現(xiàn)有數(shù)據(jù)無(wú)法得出結(jié)論’?!?.3 數(shù)據(jù)分析模型的構(gòu)建采集到的數(shù)據(jù)需要轉(zhuǎn)化為洞察。我構(gòu)建了幾個(gè)簡(jiǎn)單的量化模型價(jià)格健康度指數(shù)計(jì)算(當(dāng)前均價(jià) - 半年均價(jià)) / 半年均價(jià)得到短期漲跌幅計(jì)算(當(dāng)前均價(jià) - 同片區(qū)均價(jià)) / 同片區(qū)均價(jià)得到溢價(jià)率。結(jié)合兩者可以判斷該小區(qū)價(jià)格是處于上升通道還是虛高。配套便利度評(píng)分這是一個(gè)加權(quán)打分模型。例如對(duì)于“教育”維度1公里內(nèi)有市重點(diǎn)小學(xué)得10分區(qū)重點(diǎn)得7分普通小學(xué)得4分1-2公里內(nèi)則分?jǐn)?shù)折半。交通、商業(yè)同理。最后為每個(gè)維度設(shè)置權(quán)重如學(xué)區(qū)剛需用戶教育權(quán)重設(shè)為0.5計(jì)算加權(quán)總分。輿情情感指數(shù)對(duì)爬取的每條有效評(píng)論進(jìn)行情感打分正面1中性0負(fù)面-1然后計(jì)算該小區(qū)的平均情感分。同時(shí)通過(guò)文本聚類如TF-IDF K-Means找出高頻主題詞直觀展示業(yè)主最關(guān)心的問(wèn)題如“停車難”、“物業(yè)差”、“綠化好”。這些模型并不復(fù)雜但能有效將雜亂的數(shù)據(jù)轉(zhuǎn)化為可比較的指標(biāo)為最終決策提供直觀參考。4. 從零搭建的完整操作流程如果你也想嘗試搭建一個(gè)簡(jiǎn)化版的購(gòu)房Agent可以遵循以下步驟。這里我以分析兩個(gè)小區(qū)為例提供一個(gè)可操作的路線圖。4.1 環(huán)境準(zhǔn)備與基礎(chǔ)工具首先確保你的開(kāi)發(fā)環(huán)境就緒。我推薦使用Python 3.9并通過(guò)虛擬環(huán)境管理依賴。# 創(chuàng)建并激活虛擬環(huán)境 python -m venv house_agent_env source house_agent_env/bin/activate # Linux/Mac # house_agent_env\Scripts\activate # Windows # 安裝核心庫(kù) pip install playwright beautifulsoup4 requests pandas numpy matplotlib plotly pip install openai # 或其他LLM SDK如zhipuai, dashscope playwright install # 安裝Playwright瀏覽器驅(qū)動(dòng)此外你需要申請(qǐng)一些必要的API密鑰LLM服務(wù)如OpenAI API Key或國(guó)內(nèi)可用的智譜AI、深度求索等。地圖服務(wù)高德或百度地圖開(kāi)放平臺(tái)的Web服務(wù)API Key。代理IP服務(wù)可選但推薦用于大規(guī)模爬取時(shí)的IP輪換。4.2 分步實(shí)現(xiàn)核心模塊第一步構(gòu)建數(shù)據(jù)采集器我們從最核心的房產(chǎn)信息采集開(kāi)始。以貝殼為例創(chuàng)建一個(gè)beike_crawler.py文件。import asyncio from playwright.async_api import async_playwright import pandas as pd import logging logging.basicConfig(levellogging.INFO) class BeikeCrawler: def __init__(self, proxyNone): self.proxy proxy async def fetch_xiaoqu_info(self, xiaoqu_name, city北京): 根據(jù)小區(qū)名抓取小區(qū)基礎(chǔ)信息 async with async_playwright() as p: # 啟動(dòng)瀏覽器可配置代理 browser await p.chromium.launch(proxyself.proxy, headlessTrue) # 生產(chǎn)環(huán)境建議headless page await browser.new_page() # 構(gòu)造搜索URL這里需要根據(jù)貝殼實(shí)際搜索頁(yè)URL調(diào)整 search_url fhttps://{city}.ke.com/xiaoqu/rs{urllib.parse.quote(xiaoqu_name)}/ try: await page.goto(search_url, timeout60000) # 等待關(guān)鍵元素出現(xiàn)例如第一個(gè)小區(qū)結(jié)果 await page.wait_for_selector(.content__list--item, timeout10000) # 點(diǎn)擊進(jìn)入第一個(gè)匹配的小區(qū)詳情頁(yè)這里邏輯需簡(jiǎn)化實(shí)際應(yīng)更嚴(yán)謹(jǐn) first_item page.locator(.content__list--item).first await first_item.click() # 等待詳情頁(yè)加載 await page.wait_for_load_state(networkidle) # 提取信息這里的選擇器需要根據(jù)貝殼頁(yè)面實(shí)際結(jié)構(gòu)調(diào)整以下為示例 name await page.locator(.xiaoquDetailHeader .detailTitle).inner_text() price_elem await page.locator(.xiaoquUnitPrice).inner_text() year_elem await page.locator(//span[contains(text(),建筑年代)]/following-sibling::span).inner_text() # ... 提取更多字段 info { 小區(qū)名稱: name.strip(), 參考均價(jià): price_elem.strip(), 建筑年代: year_elem.strip(), # ... } return info except Exception as e: logging.error(f抓取小區(qū) {xiaoqu_name} 信息失敗: {e}) return None finally: await browser.close() # 異步調(diào)用示例 async def main(): crawler BeikeCrawler() info await crawler.fetch_xiaoqu_info(橡樹(shù)灣) print(info) if __name__ __main__: asyncio.run(main())重要提示網(wǎng)站結(jié)構(gòu)經(jīng)常變化上述選擇器(.content__list--item)等可能需要隨時(shí)調(diào)整。務(wù)必編寫健壯的異常處理并考慮將選擇器路徑配置化便于維護(hù)。第二步調(diào)用地圖API獲取周邊配套創(chuàng)建map_poi_fetcher.py以高德地圖為例。import requests import pandas as pd class GaodePOIFetcher: def __init__(self, api_key): self.api_key api_key self.base_url https://restapi.amap.com/v3/place/around def fetch_around_poi(self, location, keywords, radius1000): 獲取周邊POI Args: location: 經(jīng)緯度字符串如 116.473168,39.993015 keywords: POI類型關(guān)鍵詞如 小學(xué)|中學(xué) radius: 搜索半徑單位米 params { key: self.api_key, location: location, keywords: keywords, radius: radius, output: json, extensions: all, # 返回詳細(xì)信息 offset: 20 # 每頁(yè)條數(shù) } resp requests.get(self.base_url, paramsparams) data resp.json() if data[status] 1 and int(data[count]) 0: pois [] for poi in data[pois]: pois.append({ name: poi.get(name), type: poi.get(type), address: poi.get(address), location: poi.get(location), distance: poi.get(distance) # 距離中心點(diǎn)的距離 }) return pd.DataFrame(pois) else: print(f未找到關(guān)鍵詞{keywords}附近的POI或請(qǐng)求失敗。) return pd.DataFrame() # 使用示例 if __name__ __main__: fetcher GaodePOIFetcher(你的高德API_KEY) # 假設(shè)橡樹(shù)灣的經(jīng)緯度 df_schools fetcher.fetch_around_poi(116.337643,40.033394, 小學(xué), radius1500) print(df_schools.head())第三步設(shè)計(jì)任務(wù)規(guī)劃與執(zhí)行引擎創(chuàng)建一個(gè)簡(jiǎn)單的orchestrator.py它整合LLM調(diào)用和各工具。import json from openai import OpenAI # 或其他LLM客戶端 from beike_crawler import BeikeCrawler from map_poi_fetcher import GaodePOIFetcher class HouseAgentOrchestrator: def __init__(self, llm_client, tools): self.llm llm_client self.tools tools # 工具字典key為工具名value為可調(diào)用對(duì)象 def plan_tasks(self, user_query): 調(diào)用LLM將用戶需求解析為任務(wù)計(jì)劃 prompt f你是一個(gè)房產(chǎn)分析專家AI助手。請(qǐng)根據(jù)用戶需求生成一個(gè)JSON格式的任務(wù)執(zhí)行計(jì)劃。用戶需求{user_query}... # 此處填入完整的規(guī)劃提示詞 response self.llm.chat.completions.create( modelgpt-4, # 或其它模型 messages[{role: user, content: prompt}], temperature0.1 # 低溫度保證輸出穩(wěn)定性 ) plan_str response.choices[0].message.content # 清理可能存在的markdown代碼塊標(biāo)記 plan_str plan_str.strip().strip(json).strip() try: plan json.loads(plan_str) return plan except json.JSONDecodeError as e: print(fLLM返回的任務(wù)計(jì)劃不是合法JSON: {plan_str}) raise e async def execute_tasks(self, task_plan): 異步執(zhí)行任務(wù)計(jì)劃 results {} for task in task_plan.get(tasks, []): tool_name task[tool] params task[params] if tool_name in self.tools: print(f執(zhí)行任務(wù): {tool_name} with {params}) try: # 簡(jiǎn)單起見(jiàn)這里假設(shè)所有工具都是異步的 result await self.tools[tool_name](**params) results.setdefault(tool_name, []).append(result) except Exception as e: print(f任務(wù) {tool_name} 執(zhí)行失敗: {e}) results.setdefault(tool_name, []).append({error: str(e)}) else: print(f未知工具: {tool_name}) return results # 主程序流程示例 async def main_flow(user_query): # 1. 初始化組件 llm_client OpenAI(api_keyyour_openai_key) crawler BeikeCrawler() poi_fetcher GaodePOIFetcher(your_gaode_key) # 2. 定義工具集 tools { collect_property_basic_info: crawler.fetch_xiaoqu_info, collect_surrounding_poi: poi_fetcher.fetch_around_poi, # ... 注冊(cè)其他工具 } orchestrator HouseAgentOrchestrator(llm_client, tools) # 3. 規(guī)劃任務(wù) print(正在規(guī)劃任務(wù)...) task_plan orchestrator.plan_tasks(user_query) print(f生成任務(wù)計(jì)劃: {json.dumps(task_plan, indent2, ensure_asciiFalse)}) # 4. 執(zhí)行任務(wù) print(開(kāi)始執(zhí)行任務(wù)...) collected_data await orchestrator.execute_tasks(task_plan) # 5. 此處省略數(shù)據(jù)分析和報(bào)告生成步驟... print(數(shù)據(jù)采集完成。) return collected_data第四步數(shù)據(jù)可視化與報(bào)告生成在report_generator.py中我們利用采集到的數(shù)據(jù)生成圖表和文字。import matplotlib.pyplot as plt import pandas as pd from openai import OpenAI def generate_comparison_chart(data_dict, neighborhood_names): 生成價(jià)格對(duì)比柱狀圖 # 假設(shè)data_dict中包含每個(gè)小區(qū)的均價(jià)信息 prices [data_dict.get(name, {}).get(均價(jià), 0) for name in neighborhood_names] plt.figure(figsize(8,5)) bars plt.bar(neighborhood_names, prices, color[skyblue, lightcoral]) plt.ylabel(均價(jià) (元/平米)) plt.title(小區(qū)均價(jià)對(duì)比) # 在柱子上方顯示價(jià)格 for bar, price in zip(bars, prices): plt.text(bar.get_x() bar.get_width()/2, bar.get_height(), f{price:.0f}, hacenter, vabottom) chart_path price_comparison.png plt.tight_layout() plt.savefig(chart_path, dpi300) plt.close() return chart_path def generate_report_with_llm(analysis_results, chart_paths, llm_client): 調(diào)用LLM生成圖文報(bào)告 # 將分析結(jié)果結(jié)構(gòu)化數(shù)據(jù)和圖表描述準(zhǔn)備好 data_summary f 數(shù)據(jù)分析摘要 1. 小區(qū)A {analysis_results[neighborhoods][0]} 均價(jià)為 {analysis_results[prices][0]} 元/平米近半年漲幅{analysis_results[trends][0]}。 2. 小區(qū)B {analysis_results[neighborhoods][1]} 均價(jià)為 {analysis_results[prices][1]} 元/平米近半年漲幅{analysis_results[trends][1]}。 3. 教育配套方面小區(qū)A周邊有{analysis_results[schools_A]}所中小學(xué)小區(qū)B有{analysis_results[schools_B]}所。 4. 輿情情感分析顯示小區(qū)A的業(yè)主評(píng)價(jià)偏{analysis_results[sentiment_A]}主要關(guān)注點(diǎn)為{analysis_results[topics_A]}小區(qū)B偏{analysis_results[sentiment_B]}。 chart_descriptions f 已生成圖表 - {chart_paths[0]}: 展示了兩個(gè)小區(qū)的均價(jià)對(duì)比。 - {chart_paths[1]}: 以雷達(dá)圖形式展示了兩者在價(jià)格、房齡、學(xué)區(qū)、交通、商業(yè)五個(gè)維度的表現(xiàn)。 prompt f你是一名專業(yè)的房產(chǎn)分析師。請(qǐng)根據(jù)以下數(shù)據(jù)和圖表撰寫一份詳細(xì)、客觀、對(duì)購(gòu)房者有直接參考價(jià)值的測(cè)評(píng)報(bào)告。 {data_summary} {chart_descriptions} 報(bào)告要求 1. 格式為Markdown。 2. 包含“概述”、“數(shù)據(jù)對(duì)比”、“分項(xiàng)分析”、“綜合結(jié)論與建議”四個(gè)部分。 3. 在“數(shù)據(jù)對(duì)比”部分以表格形式呈現(xiàn)核心數(shù)據(jù)。 4. 在“分項(xiàng)分析”部分引用上述數(shù)據(jù)和圖表中的發(fā)現(xiàn)進(jìn)行闡述。 5. 所有結(jié)論必須嚴(yán)格基于提供的數(shù)據(jù)不得編造。 6. 最后給出一個(gè)清晰的購(gòu)買傾向性建議并說(shuō)明理由。 response llm_client.chat.completions.create( modelgpt-4, messages[{role: user, content: prompt}], temperature0.7 ) report response.choices[0].message.content return report # 整合調(diào)用 def create_full_report(collected_data): # 1. 數(shù)據(jù)分析此處簡(jiǎn)化實(shí)際應(yīng)有更復(fù)雜的計(jì)算 analysis_results { neighborhoods: [橡樹(shù)灣, 清楓華景園], prices: [95000, 87000], trends: [5%, -2%], schools_A: 4, schools_B: 2, sentiment_A: 正面, topics_A: 綠化好物業(yè)負(fù)責(zé), sentiment_B: 中性, topics_B: 車位緊張 } # 2. 生成圖表 price_chart_path generate_comparison_chart({橡樹(shù)灣:95000, 清楓華景園:87000}, analysis_results[neighborhoods]) # 3. 生成報(bào)告 llm_client OpenAI(api_keyyour_key) report generate_report_with_llm(analysis_results, [price_chart_path], llm_client) # 4. 保存報(bào)告 with open(小區(qū)測(cè)評(píng)報(bào)告.md, w, encodingutf-8) as f: f.write(report) print(報(bào)告已生成小區(qū)測(cè)評(píng)報(bào)告.md) return report5. 常見(jiàn)問(wèn)題、優(yōu)化方向與避坑指南在實(shí)際開(kāi)發(fā)和運(yùn)行過(guò)程中你會(huì)遇到各種各樣的問(wèn)題。下面是我總結(jié)的一些典型問(wèn)題及其解決方案以及未來(lái)可以優(yōu)化的方向。5.1 數(shù)據(jù)采集中的常見(jiàn)挑戰(zhàn)與應(yīng)對(duì)反爬蟲(chóng)封鎖這是最頭疼的問(wèn)題。除了使用代理IP還需要設(shè)置請(qǐng)求頭模擬真實(shí)瀏覽器User-Agent, Accept-Language等??刂普?qǐng)求頻率在關(guān)鍵操作間增加隨機(jī)延遲time.sleep(random.uniform(1, 3))。使用會(huì)話Session保持Cookie模擬登錄態(tài)如果需要??紤]官方API部分平臺(tái)提供有限的公開(kāi)API雖然數(shù)據(jù)可能不全但穩(wěn)定合法。終極方案如果數(shù)據(jù)至關(guān)重要且規(guī)模大可以考慮使用付費(fèi)的、帶驗(yàn)證碼破解和瀏覽器指紋模擬的云爬蟲(chóng)服務(wù)。數(shù)據(jù)字段缺失或格式不一致不同小區(qū)、不同時(shí)間點(diǎn)的頁(yè)面結(jié)構(gòu)可能有微調(diào)。防御性編程在解析每個(gè)字段時(shí)都用try...except包裹并為缺失字段設(shè)置默認(rèn)值如N/A。定期校驗(yàn)與更新編寫數(shù)據(jù)質(zhì)量檢查腳本定期跑一遍發(fā)現(xiàn)解析失敗率高的字段及時(shí)調(diào)整解析邏輯。數(shù)據(jù)標(biāo)準(zhǔn)化管道在數(shù)據(jù)入庫(kù)前增加一個(gè)清洗和標(biāo)準(zhǔn)化步驟統(tǒng)一單位、格式。地理位置坐標(biāo)獲取小區(qū)名稱可能對(duì)應(yīng)多個(gè)地址或地圖API返回的坐標(biāo)不精確。多重驗(yàn)證先用地圖API的地理編碼Geocoding將小區(qū)名轉(zhuǎn)為坐標(biāo)再用這個(gè)坐標(biāo)反向地理編碼獲取詳細(xì)地址與爬取的地址信息交叉驗(yàn)證。人工校準(zhǔn)對(duì)于核心小區(qū)可以建立一個(gè)小型的手工校準(zhǔn)坐標(biāo)庫(kù)。5.2 系統(tǒng)穩(wěn)定性與性能優(yōu)化異步并發(fā)控制同時(shí)爬取多個(gè)小區(qū)、多種數(shù)據(jù)時(shí)異步編程能大幅提升效率。但要注意限制并發(fā)數(shù)避免對(duì)目標(biāo)網(wǎng)站造成過(guò)大壓力也避免自己被封IP。可以使用asyncio.Semaphore來(lái)限制最大并發(fā)任務(wù)數(shù)。良好的錯(cuò)誤處理任何一個(gè)子任務(wù)失敗不應(yīng)導(dǎo)致整個(gè)流程崩潰。要將每個(gè)任務(wù)包裝好捕獲異常并記錄日志確保其他任務(wù)能繼續(xù)。狀態(tài)管理與斷點(diǎn)續(xù)傳一次完整的分析可能耗時(shí)較長(zhǎng)。設(shè)計(jì)任務(wù)狀態(tài)機(jī)為每個(gè)采集任務(wù)如“采集小區(qū)A基礎(chǔ)信息”設(shè)計(jì)狀態(tài)pending, running, success, failed。持久化存儲(chǔ)中間狀態(tài)將任務(wù)狀態(tài)、已采集的數(shù)據(jù)定期保存到數(shù)據(jù)庫(kù)或文件。當(dāng)程序因意外中斷重啟后可以跳過(guò)已完成的任務(wù)從斷點(diǎn)處繼續(xù)。LLM API的成本與穩(wěn)定性GPT-4等模型API調(diào)用成本不低且可能有速率限制。緩存結(jié)果對(duì)于相同的用戶查詢?nèi)绻胺治鲞^(guò)可以直接返回緩存的結(jié)果避免重復(fù)調(diào)用LLM和爬蟲(chóng)。使用更便宜的模型進(jìn)行簡(jiǎn)單任務(wù)任務(wù)規(guī)劃可以用性能稍弱但更便宜的模型如GPT-3.5-turbo報(bào)告生成再用GPT-4。設(shè)置預(yù)算和監(jiān)控在代碼中集成API調(diào)用花費(fèi)的監(jiān)控達(dá)到閾值后停止或報(bào)警。5.3 分析維度與報(bào)告價(jià)值的深化初始版本可能只關(guān)注價(jià)格、房齡、配套等基礎(chǔ)維度。要提升報(bào)告價(jià)值可以考慮加入市場(chǎng)趨勢(shì)分析不僅看當(dāng)前價(jià)格更分析近一年的價(jià)格走勢(shì)、成交量變化判斷小區(qū)處于上升期、平臺(tái)期還是下行期。房源流動(dòng)性分析統(tǒng)計(jì)在售房源數(shù)量、平均成交周期。流動(dòng)性差的小區(qū)未來(lái)出手可能更困難。居住密度與舒適度結(jié)合容積率、綠化率、車位比、戶數(shù)等數(shù)據(jù)量化居住擁擠程度。風(fēng)險(xiǎn)提示通過(guò)輿情分析自動(dòng)識(shí)別并高亮潛在風(fēng)險(xiǎn)點(diǎn)如“頻繁提及物業(yè)糾紛”、“周邊有規(guī)劃中的不利設(shè)施如垃圾站”等。個(gè)性化權(quán)重設(shè)置允許用戶自定義各維度的權(quán)重如“我最看重學(xué)區(qū)和交通價(jià)格和房齡可以放寬”系統(tǒng)根據(jù)個(gè)性化權(quán)重計(jì)算綜合推薦分。5.4 法律與倫理邊界這是一個(gè)必須嚴(yán)肅對(duì)待的問(wèn)題。數(shù)據(jù)版權(quán)與使用條款公開(kāi)數(shù)據(jù)不等于可以任意商用。務(wù)必仔細(xì)閱讀目標(biāo)網(wǎng)站的robots.txt文件和服務(wù)條款。本項(xiàng)目定位應(yīng)為個(gè)人學(xué)習(xí)、研究及輔助決策工具切勿用于大規(guī)模商業(yè)爬取或產(chǎn)生直接競(jìng)爭(zhēng)關(guān)系。個(gè)人信息保護(hù)爬取過(guò)程中可能會(huì)意外接觸到個(gè)人數(shù)據(jù)如業(yè)主電話在部分論壇。在設(shè)計(jì)和實(shí)現(xiàn)時(shí)必須有意識(shí)地過(guò)濾和避免存儲(chǔ)任何個(gè)人敏感信息。報(bào)告免責(zé)聲明生成的測(cè)評(píng)報(bào)告必須包含明確的免責(zé)聲明指出數(shù)據(jù)來(lái)源可能存在滯后或誤差分析結(jié)論僅供參考不構(gòu)成投資建議最終決策需結(jié)合實(shí)地看房和專業(yè)咨詢。搭建這樣一個(gè)Agent的過(guò)程本身就是一個(gè)對(duì)房產(chǎn)市場(chǎng)、數(shù)據(jù)技術(shù)和AI應(yīng)用深入理解的過(guò)程。它不能替代你實(shí)地看房的感受也無(wú)法預(yù)知所有的潛在問(wèn)題但它能作為一個(gè)強(qiáng)大的信息過(guò)濾器和分析儀幫你從信息的海洋中打撈出真正有價(jià)值的信號(hào)讓你的決策建立在更廣泛、更客觀的數(shù)據(jù)基礎(chǔ)之上。我開(kāi)始這個(gè)項(xiàng)目的初衷就是為了解決自己買房時(shí)的信息焦慮而在實(shí)現(xiàn)它之后我發(fā)現(xiàn)它帶給我的遠(yuǎn)不止一份份報(bào)告更是一種用技術(shù)和數(shù)據(jù)思維解決復(fù)雜生活問(wèn)題的全新視角。