Selenium+Python爬取房天下二手房資料
阿新 • • 發佈:2018-12-04
注意!注意!注意!本文中大圖較多,建議使用PC檢視,手機端效果較差!
在上篇“Selenuim+Python網路爬蟲基礎講解”博文中講了一些Selenium的基礎知識,接下來就要開始實戰了。
其實使用Selenium爬取網頁的思路很簡單,首先梳理一下爬取流程。
開啟二手房珠海地區首頁http://zh.esf.fang.com/,首先會出現一個遮蔽頁,我們需要點選“我知道了”,才能繼續點選其他內容。
在上面的頁面中,我們可以看到在區域頁簽下,有香洲、金灣、斗門等大區,點選香洲大區頁籤後出現翠微、鳳凰北等子區。繼續點選翠微後會出現翠微的二手房相關資訊。
所以我們的思路就是先獲取大區列表,然後點選大區,獲取該大區下的子區 列表,逐一按子區爬取就行了。但是我們可以看到當前顯示的房屋資訊不全,需要我們點進去檢視詳細資訊。詳細資訊中需要爬取的有以下被框資訊。
爬取完以上資訊後,需要關閉當前房屋詳細資訊頁籤,繼續按房屋資訊列表爬取。
遍歷完當前頁的房屋資訊後,就需要跳轉到下一頁。
我們可以看到,這裡最方便的頁面切換方式就是通過點選“下一頁”按鈕,到最後一頁的時候就沒有“下一頁”按鈕,我們切換子區頁籤,進入下一個子區繼續按上述方式爬取就成。
Go~就按上面的分析開始實現!
每一步基本上都有註釋。
from selenium import webdriver from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC import os import time import pandas as pd browser=webdriver.Chrome() #設定瀏覽器 browser.maximize_window() #瀏覽器視窗最大化 wait=WebDriverWait(browser,20) #設定顯示等待 list_dq=[] #已爬取大區列表,為了防止中斷後重新爬取,需手動新增 list_zq=[] #已爬取子區列表,為了防止中斷後重新爬取,需手動新增 #需要獲取的資訊列表 columns1=['價格','戶型','建築面積','單價','朝向','樓層','總樓層','裝修'] #8 columns2=['小區','區域','子區域','小學'] #4 columns3=['建築年代','有無電梯','產權性質','建築類別','住宅類別','建築結構','掛牌時間'] #7 columns4=['參考價格'] #1 columns5=['物業型別','物業費用','建築型別','產權年限','綠 化 率','容 積 率','人車分流','總樓棟數','總 戶 數'] #9 columns_all=columns1+columns2+columns3+columns4+columns5 #儲存爬取資料 def save_file(title,data_div): path='E:/fcwdata1' if os.path.exists(path): pass else: os.mkdir(path) path_file_name =str(path +'/'+ title + '.csv') file=pd.DataFrame(data_div,columns=columns_all) file.to_csv(path_file_name,index=False) #獲取房屋資訊 def get_houseInfo(): #詳細資訊列表 f_list=list() #判斷元素是否存在 def isElementExist(xpath): flag=True try: browser.find_element_by_xpath(xpath) return flag except: flag=False return flag #獲取xpath中資訊 def get_info(xpath): if isElementExist(xpath): f_list.append(wait.until(EC.presence_of_element_located((By.XPATH,xpath))).text) else: f_list.append(-1) #columns1和columns2中的資訊的xpath路徑 f1=pd.Series() f1['price']='/html/body/div[5]/div[1]/div[4]/div[1]/div[1]/div[1]' f1['house_type']='/html/body/div[5]/div[1]/div[4]/div[2]/div[1]/div[1]' f1['area']='/html/body/div[5]/div[1]/div[4]/div[2]/div[2]/div[1]' f1['per_price']='/html/body/div[5]/div[1]/div[4]/div[2]/div[3]/div[1]' f1['direction']='/html/body/div[5]/div[1]/div[4]/div[3]/div[1]/div[1]' f1['floor']='/html/body/div[5]/div[1]/div[4]/div[3]/div[2]/div[1]' f1['floor_sum']='/html/body/div[5]/div[1]/div[4]/div[3]/div[2]/div[2]' f1['decoration']='/html/body/div[5]/div[1]/div[4]/div[3]/div[3]/div[1]' f1['plot']='//*[@id="kesfsfbxq_A01_01_05"]' f1['region1']='//*[@id="kesfsfbxq_C03_07"]' f1['region2']='//*[@id="kesfsfbxq_C03_08"]' f1['school']='//*[@id="kesfsfbxq_C03_09"]' #獲取columns1和columns2中資訊 for i in range(len(f1)): get_info(f1[i]) #獲取coulumns3中資訊 fyInfo=browser.find_element_by_class_name('fydes-item').find_elements_by_class_name('text-item') dict_fyInfo=dict() #建立字典 for info_i in range(len(fyInfo)): dict_fyInfo[fyInfo[info_i].text.split('\n')[0]]=fyInfo[info_i].text.split('\n')[1] #以字典形式儲存 for name in columns3: if name in dict_fyInfo.keys(): f_list.append(dict_fyInfo[name]) else: f_list.append(-1) #缺失資訊用-1填充 #獲取columns4中資訊 try: ave_price=browser.find_element_by_class_name('topt').find_element_by_class_name('rcont').text f_list.append(ave_price) except: f_list.append(-1) #獲取columns5中資訊 xqInfo=browser.xqInfo=browser.find_element_by_class_name('pt30').find_elements_by_class_name('clearfix')[4].find_elements_by_class_name('text-item') dict_xqInfo=dict() for info_i in range(len(xqInfo)): dict_xqInfo[xqInfo[info_i].text.split('\n')[0]]=xqInfo[info_i].text.split('\n')[1] for name in columns5: if name in dict_xqInfo.keys(): f_list.append(dict_xqInfo[name]) else: f_list.append(-1) return f_list print('開始測試') page_allinfo=[] #儲存當前頁所有資訊 browser.get('http://zh.esf.fang.com/') #登入主頁 wait.until(EC.presence_of_element_located((By.XPATH,'//*[@id="closemengceng"]'))).click() #點選遮蔽頁上的我知道了 diqu = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="kesfqbfylb_A01_03_01"]/ul'))) #找到大區 quyus=diqu.find_elements_by_xpath('.//li') #將大區內的所有地區賦值給quyus quyus_num=len(quyus) #統計大區數量 for i in range(quyus_num): #逐一遍歷每個大區 diqu = wait.until(EC.presence_of_all_elements_located((By.XPATH, '//*[@id="kesfqbfylb_A01_03_01"]/ul/li'))) #將大區內的所有地區賦值給quyus daqu_node=diqu[i].find_element_by_xpath('.//a') #定位到要爬取的大區 daqu_node_name=daqu_node.text print('大區',daqu_node_name) #輸出當前大區名 if daqu_node_name in list_dq: #如果當前大區的所有子區已經爬完,則手動新增至list_dq列表,直接略過該大區 continue daqu_node.click() #點選大區名稱,顯示子區列表 ziquyu=wait.until(EC.presence_of_all_elements_located((By.XPATH, '//*[@id="ri010"]/div[1]/ul/li[2]/ul/li'))) #將大區內的所有子區賦值給ziquyu ziquyu_num=len(ziquyu) #統計當前大區下子區的數量 for j in range(ziquyu_num): #逐一遍歷當前大區下的每個子區 ziquyu=wait.until(EC.presence_of_all_elements_located((By.XPATH, '//*[@id="ri010"]/div[1]/ul/li[2]/ul/li'))) #獲取子區地區,將子區內的所有地區名稱賦值給ziquyu ziquyu_node=ziquyu[j].find_element_by_xpath('.//a') #定位到要爬取的子區 ziquyu_node_name=ziquyu_node.text print('子區域',ziquyu_node_name) #輸出當前子區名 ziquyu_node.click() #點選當前子區 if ziquyu_node_name in list_zq: #如果當前子區已經爬完,則手動新增至list_zq列表,直接略過該子區 continue try: #試圖搜尋頁碼塊 page_list_end=wait.until(EC.presence_of_all_elements_located((By.XPATH, '//*[@id="list_D10_15"]/p'))) except: #有時網速不好造成搜尋不到,此時嘗試重新整理頁面重新爬取 browser.refresh() #重新整理頁面 page_list_end=wait.until(EC.presence_of_all_elements_located((By.XPATH, '//*[@id="list_D10_15"]/p'))) for page_but in page_list_end: #檢視子區資訊共多少頁 if page_but.text[0] in '共': endStr=page_but.text totalNum=int(endStr[1:len(endStr)-1]) #擷取總頁數,eg:共100頁,totalNum則為100 for k in range(totalNum): #逐一遍歷每頁 if ziquyu_node_name=='': #如果當前子區前面n頁被爬取,則跳過 eg:if ziquyu_node_name=='新香洲' and k in range(15):表示新香洲的前15頁已經爬取,直接跳過 pass else: page_allinfo=[] handle = browser.current_window_handle #記錄當前頁的控制代碼 data_div=browser.find_element_by_css_selector("[class='shop_list shop_list_4']").find_elements_by_css_selector('[dataflag="bg"]') #搜尋當前頁下,所有房屋元素 for house_info_i in range(len(data_div)): #遍歷當前頁房屋元素 try: #嘗試爬取房屋詳細資訊 data_div[house_info_i].find_element_by_class_name('clearfix').click() #點選房屋標題,進入房屋詳細資訊頁 handles = browser.window_handles #記錄當前頁的控制代碼 for newhandle in handles: if newhandle!=handle: browser.switch_to_window(newhandle) #找到開啟的新頁籤,並跳轉至新頁籤 page_allinfo.append(get_houseInfo()) #爬取詳細資訊 browser.close() #關閉頁籤 browser.switch_to_window(handle) #跳轉至房屋列表頁籤 break except: #當網速不好的時候可能爬取不到,所有再重新爬取一遍 try: browser.close() browser.switch_to_window(handle) time.sleep(5) data_div[house_info_i].find_element_by_class_name('clearfix').click() handles = browser.window_handles for newhandle in handles: if newhandle!=handle: browser.switch_to_window(newhandle) page_allinfo.append(get_houseInfo()) browser.close() browser.switch_to_window(handle) break except: #有時候有些意外情況導致房屋詳細資訊頁籤沒有關閉,所以關閉房屋詳細資訊頁籤並跳轉至房屋列表頁籤 if len(handles)>1: browser.close() browser.switch_to_window(handle) else: #實在爬取不到該房屋詳細資訊就跳過,因為網速確實不穩定,也不差那一兩個房屋資訊 pass title=daqu_node_name+"-"+ziquyu_node_name+"-"+str(k+1) #以大區名+子區名+頁碼作為檔案標題,eg:香洲-翠微-1 save_file(title,page_allinfo) #儲存當前頁資訊 print(title) try: #有時候當前頁沒有顯示頁碼列表,所以無法點選下一頁,所以沒有顯示的時候重新重新整理一下 page_nodes=wait.until(EC.presence_of_element_located((By.XPATH,'//*[@id="list_D10_15"]'))).find_elements_by_tag_name('p') except: browser.refresh() page_nodes=wait.until(EC.presence_of_element_located((By.XPATH,'//*[@id="list_D10_15"]'))).find_elements_by_tag_name('p') for node in page_nodes: if '下一頁' in node.text: next_node=node.find_element_by_xpath('.//a').click() #跳轉至下一頁 break print('--------------------------')
嗯~大概就是這個樣子!
這樣我們會得到很多檔案,如下圖所示。
這麼多檔案不方便我們進行分析處理,所以我們需要將各檔案進行合併,合併程式碼如下,
import pandas as pd
import glob
outputfile='f:/hebing.csv'
csv_list = glob.glob('F:/*.csv')
print(u'共發現%s個CSV檔案'% len(csv_list))
print(u'正在處理............')
def hebing():
for inputfile in csv_list:
f=open(inputfile)
data=pd.read_csv(f)
data.to_csv(outputfile,mode='a',index=False,header=None)
print('完成合並')
def quchong(file):
df = pd.read_csv(file,header=0)
datalist = df.drop_duplicates()
datalist.to_csv(file)
print('完成去重')
if __name__ == '__main__':
hebing()
quchong(outputfile)
合併好資料,我們看看爬到的資料是什麼樣子的,資料如下所示,
可以看到格式不是很標準,我們可以格式化處理一下資料,程式碼如下,
import pandas as pd
columns1=['價格','戶型','建築面積','單價','朝向','樓層','總樓層','裝修'] #8
columns2=['小區','區域','子區域','小學'] #4
columns3=['建築年代','有無電梯','產權性質','建築類別','住宅類別','建築結構','掛牌時間'] #7
columns4=['參考價格'] #1
columns5=['物業型別','物業費用','建築型別','產權年限','綠 化 率','容 積 率','人車分流','總樓棟數','總 戶 數'] #9
columns_all=columns1+columns2+columns3+columns4+columns5
data=pd.read_csv('e:/fcwdata/gen/hebing1.csv',names=columns_all)
data['戶型'].replace('暫無','-1',inplace=True)
data['室']=''
data['廳']=''
data['衛']=''
for i in range(len(data)):
data.loc[i,'價格']=data.loc[i,'價格'].split('\r')[0]
if data.loc[i,'戶型']=='-1':
data.loc[i,'室']='-1'
data.loc[i,'廳']='-1'
data.loc[i,'衛']='-1'
else:
data.loc[i,'室']=data.loc[i,'戶型'].split('室')[0]
data.loc[i,'廳']=data.loc[i,'戶型'].split('室')[1].split('廳')[0]
data.loc[i,'衛']=data.loc[i,'戶型'].split('室')[1].split('廳')[1].split('衛')[0]
if data.loc[i,'建築面積']!='-1':
data.loc[i,'建築面積']= data.loc[i,'建築面積'][:-2]
if data.loc[i,'單價']!='-1':
data.loc[i,'單價']= data.loc[i,'單價'][:-4]
data.loc[i,'總樓層']=data.loc[i,'總樓層'].split('共')[1].split('層')[0]
if data.loc[i,'建築年代']!='-1':
data.loc[i,'建築年代']=data.loc[i,'建築年代'][:-1]
if data.loc[i,'有無電梯']=='有':
data.loc[i,'有無電梯']=1
if data.loc[i,'有無電梯']=='無':
data.loc[i,'有無電梯']=0
if data.loc[i,'參考價格']!='-1':
data.loc[i,'參考價格']= data.loc[i,'參考價格'][:-4]
if data.loc[i,'物業費用']=='暫無資料':
data.loc[i,'物業費用']='-1'
if data.loc[i,'物業費用']!='暫無資料':
data.loc[i,'物業費用']=data.loc[i,'物業費用'].split('元')[0]
data.loc[i,'產權年限']=data.loc[i,'產權年限'].split('年')[0]
data.loc[i,'綠 化 率']=data.loc[i,'綠 化 率'].split('%')[0]
data['產權性質'].replace('普通商品房','商品房',inplace=True)
data.fillna('-1',inplace=True)
data.to_csv('e:/fcwdata/gen/hebing1.csv')
處理後的資料如下所示,
大功告成,可以用這些資料可以進行分析預測 ~