爬蟲系列之鏈家的信息爬取及數據分析


關於鏈家的數據爬取和分析

已經實現

 1.房屋數據爬取並下載  2.房屋按區域分析  3.房屋按經紀人分析  4.前十經紀人  5.經紀人最有可能的位置分析  6.實現以地區划分房屋  目前存在的問題:  1.多線程下載的時候會出現個別文件不繼續寫入了(已經解決)  2.未考慮經紀人重名問題  3.查詢中發現不是每次都能 get 到 url 的數據,具體原因可能跟header有關,或者網站反扒(已經解決,手機端的header有時候訪問pc端會出現None的情況)  4.守護線程那里應該出問題了,如果有文件儲存完成,其他就不運行了(已經解決,多線程下還要有主程序運行,否則會出現問題)  5.json.dumps(dict)方法取出的字符串類型,二進制的,decode不好用,怎么解決  (已經解決json.dumps(content, ensure_ascii=False)保持原有的編碼)
  1 # -*- coding: utf-8 -*-
  2 # @Time :2018/5/1   23:39
  3 # @Author : ELEVEN
  4 # @File : _鏈家_數據分析_修改.py
  5 # @Software: PyCharm
  6 
  7 import time
  8 from lxml import etree
  9 from urllib import request
 10 import threading
 11 import os
 12 import json
 13 import random
 14 
 15 '''
 16 已經實現
 17 1.房屋數據爬取並下載
 18 2.房屋按區域分析
 19 3.房屋按經紀人分析
 20 4.前十經紀人
 21 5.經紀人最有可能的位置分析
 22 6.實現以地區划分房屋
 23 目前存在的問題:
 24 1.多線程下載的時候會出現個別文件不繼續寫入了(已經解決)
 25 2.未考慮經紀人重名問題
 26 3.查詢中發現不是每次都能 get 到 url 的數據,具體原因可能跟header有關,或者網站反扒(已經解決,手機端的header有時候訪問pc端會出現None的情況)
 27 4.守護線程那里應該出問題了,如果有文件儲存完成,其他就不運行了(已經解決,多線程下還要有主程序運行,否則會出現問題)
 28 5.json.dumps(dict)方法取出的字符串類型,二進制的,decode不好用,怎么解決
 29 (已經解決json.dumps(content, ensure_ascii=False)保持原有的編碼)
 30 
 31 
 32 '''
 33 # 獲取能夠 xpath 匹配的 HTML 匹配對象
 34 def get_html(url):
 35     time.sleep(1)
 36     header = {
 37         'Referer':'https://bj.lianjia.com/zufang/',
 38         'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0'
 39     }
 40     req = request.Request(url, headers = header)
 41     # 請求
 42     response = request.urlopen(req)
 43     result = response.read().decode()
 44     # 構建 HTML 匹配對象
 45     html = etree.HTML(result)
 46     return html
 47 # 主程序
 48 def main(p, url):
 49     # 加鎖,寫入本條數據后指針才會進行跳轉
 50     lock = threading.Lock()
 51     # 獲取 get_html() 函數返回的 HTML 匹配對象
 52     html = get_html(url)
 53     # 進行 xpath 初步匹配
 54     house_list = html.xpath('//ul[@id="house-lst"]/li/div[@class = "info-panel"]')
 55     threading_list = []
 56     # 遍歷得到的匹配列表
 57     for index, house in enumerate(house_list):
 58         content, house_address, house_dict ,broker_name = get_info(p,index, house)
 59         print('正在保存第 %d 頁 第 %s 條數據......' % (p, index+1))
 60         lock.acquire()
 61         # save_info(p, str(index+1) + ' ' + content + '\n')
 62         get_class_data(house_address, house_dict, broker_name)
 63         t2 = threading.Thread(target=save_info, args=(p, str(index+1) + ' ' + content + '\n'))
 64         t3 = threading.Thread(target=get_class_data, args=(house_address, house_dict, broker_name))
 65         t2.setDaemon(True)  # 這個好像沒有用,等老師幫助解答
 66         t2.start()
 67         t3.start()
 68         threading_list.append(t2)
 69         threading_list.append(t3)
 70         lock.release()
 71     for t in threading_list:
 72         t.join()
 73     # 這里必須的寫, 這個錯誤得記住,必須要有主進程 ,所有線程才會都運行
 74     print('我是守護線程')
 75 # 獲取分類數據,方便數據分析
 76 def get_class_data(house_address, house_dict, broker_name):
 77     # 按區域划分
 78     if house_address in address_class:
 79         address_class[house_address]['num'] += 1
 80         address_class[house_address]['house'].append(house_dict)
 81     else:
 82         address_class[house_address] =  {'num': 0, 'house': []}
 83         address_class[house_address]['num'] += 1
 84         address_class[house_address]['house'].append(house_dict)
 85     # 按經紀人划分
 86     if broker_name in broker_class:
 87         broker_class[broker_name]['num'] += 1
 88         broker_class[broker_name]['house'].append(house_dict)
 89     else:
 90         broker_class[broker_name] =  {'num': 0, 'house': []}
 91         broker_class[broker_name]['num'] += 1
 92         broker_class[broker_name]['house'].append(house_dict)
 93 # 獲取房產信息
 94 def get_info(p,index, house):
 95     house_url = house.xpath('h2/a/@href')[0]
 96     house_html = get_html(house_url)
 97     broker_name = house_html.xpath(
 98         '//div[@class="brokerInfo"]/div[@class="brokerInfoText"]/div[@class="brokerName"]/a/text()')
 99     broker_phone = house_html.xpath('//div[@class="phone"]/text()')
100     if broker_name != []:
101         broker_name = broker_name[0]
102         broker_phone = str(broker_phone[0].strip()) + '' + str(broker_phone[1].strip())
103     else:
104         broker_name = '暫無相關經紀人!'
105         broker_phone = '請直接聯系客服  10109666'
106     house_name = house.xpath('h2/a/text()')[0]
107     house_style = house.xpath('div[@class="col-1"]/div[@class="where"]/span[@class="zone"]/span/text()')[0]
108     house_size = house.xpath('div[@class="col-1"]/div[@class="where"]/span[@class="meters"]/text()')[0].strip()
109     house_address = house.xpath('div[@class="col-1"]/div[@class="other"]/div[@class="con"]/a/text()')[0]
110     house_price = house.xpath('div[@class="col-3"]/div[@class="price"]/span/text()')[0]
111     house_dict = {
112         'house_name': house_name,
113         'style': house_style.strip(),
114         'size': house_size,
115         'address': house_address,
116         'price': house_price,
117         'broker_name': broker_name,
118         'broker_phone': broker_phone
119     }
120     content = "名字:%(house_name)s 樣式:%(style)s  大小:%(size)s  地址:%(address)s  " \
121               "價格:%(price)s 經紀人:%(broker_name)s 聯系電話:%(broker_phone)s " % house_dict
122     # 構建字典類型 {‘house_name’:{'num':13, 'house':[house_dict]}}
123     print(p, index+1, content)
124     return content,house_address,house_dict,broker_name
125 # 保存文件
126 def save_info(p, content):
127     with open('%s/%s.txt' % ('鏈家房產信息', str(p)), 'a', encoding='utf-8') as f:
128         f.write(content)
129 # 隨機消息頭, 這里修改后再用,應該是出問題了
130 # def random_agent():
131 #     header_str = '''Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50#Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50#Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)#Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1#Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1#Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11#Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)#Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)#Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)#Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SE 2.X MetaSr 1.0; SE 2.X MetaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)#Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5#Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5#MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
132 #     '''
133 #     header = header_str.split('#')
134 #     return header[random.randint(1, len(header)) - 1]
135 # 數據分析
136 def analyze_data():
137     # 查詢信息內房屋總套數
138     address_sum = 0
139     # 區域
140     for key, value in address_class.items():
141         print(key, '一共', value['num'], '')
142         save_info('房屋分類', key+ '一共'+ json.dumps(value['num'])+ ''+'\n')
143         # with open('%s/%s.txt' % ('鏈家房產信息', '房屋分類'), 'a', encoding='utf-8') as f:
144         #     f.write(key+ '一共'+ value['num']+ '套')
145         # 每遍歷到一個區域 房屋總數量變量address_sum 就將這個區域的房屋數量加上
146         address_sum += int(value['num'])
147         # 遍歷每個區域房屋信息
148         for item in value['house']:
149             print(item)
150             save_info('房屋分類', json.dumps(item)+'\n')
151             # with open('%s/%s.txt' % ('鏈家房產信息', '房屋分類'), 'a', encoding='utf-8') as f:
152             #     f.write(item)
153         print('----------------------------------')
154     print('當前查詢房屋一共', address_sum, '')
155     save_info('房屋分類', '當前查詢房屋一共'+ str(address_sum)+ '')
156     # 創建字典:鍵為 經紀人 值為 經紀人所擁有房屋數量 num
157     broker_dict = {}
158     for key, value in broker_class.items():
159         # 打印 經紀人 和 其所擁有的房屋數量
160         print(key, '一共', value['num'], '')
161         save_info('經紀人分類', key+ '一共'+ json.dumps(value['num'])+ ''+'\n')
162         # 將經紀人 和 房屋數量信息添加到 字典中
163         broker_dict[key] = value['num']
164         # 分別打印該 經紀人 下面每套房屋的信息
165         for item in value['house']:
166             print(item)
167             save_info('經紀人分類', json.dumps(item) + '\n')
168         print('----------------------------------')
169     # 如果存在 '暫無相關經紀人!'這種情況,那么統計的聯系人數量需要減一
170     if '暫無相關經紀人!' in list(broker_class.keys()):
171         # broker_sum 為經紀人 數量總數
172         broker_sum = int(len(broker_class)) - 1
173         print(broker_sum)
174         # broker_house 為 經紀人 擁有房屋總套數
175         broker_house = address_sum - int(broker_class['暫無相關經紀人!']['num'])
176         del broker_dict['暫無相關經紀人!']
177     else:
178         broker_sum = len(broker_class)
179         broker_house = address_sum
180     print(broker_dict)
181     # 整理出不含有 '暫無相關經紀人!' 的字典,方便下面進行數據分析
182     # if broker_dict['暫無相關經紀人!']:
183     #     del broker_dict['暫無相關經紀人!']
184 
185     print('當前查詢經紀人共有', broker_sum, '')
186     print('當前查詢有經紀人的房屋', broker_house, '')
187     # 存儲前十名的列表
188     max_list = []
189     print('排序得到前十的經紀人')
190     for i in range(10):
191         # 取出手里擁有房子最多的經紀人
192         max_num = max(zip(broker_dict.values(), broker_dict.keys()))
193         # 將房子最多的聯系人添加到前十的列表中
194         max_list.append(max_num)
195         # 打印,第幾名,是誰
196         print('第 %d 名' % (i + 1), max_num)
197         save_info('排名', '第 %d 名' % (i + 1)+'姓名: '+ max_num[1]+'   '+str(max_num[0])+''+'\n')
198         # 已經被取出的經紀人,從列表中刪除掉,以免影響下一次篩選
199         del broker_dict[max_num[1]]
200         # 創建存儲經紀人位置的字典,
201         broker_postion = {}
202         # 對經紀人手中的房子按照區域划分,並且存入字典中,統計各區域擁有房屋數量
203         # 房屋最多的區域就是 經紀人最有可能在的位置
204         for dict in broker_class[max_num[1]]['house']:
205             # 如果此區域存在字典中,那么相應的區域數量 num + 1
206             # 如果不存在,那么將這個區域添加到字典中,數量 num + 1
207             if dict['address'] in broker_postion:
208                 broker_postion[dict['address']]['num'] += 1
209             else:
210                 broker_postion[dict['address']] = {'num': 0}
211                 broker_postion[dict['address']]['num'] += 1
212         # 取出經紀人按找區域分類的字典中 ,數量num最大的那個區域元組
213         postion = max(zip(broker_postion.values(), broker_postion.keys()))
214         print('最可能在的位置', postion[1])
215         save_info('排名', '最可能在的位置'+ postion[1]+'\n')
216         # 此經紀人數據分析已經結束,字典清空釋放掉,方便下次其他經紀人使用
217         broker_postion.clear()
218     # print('排序得到前十的經紀人')
219     # print(max_list)
220 
221 
222 if __name__ == "__main__":
223     # 以地址划分, 創建以 address 為鍵字典
224     address_class = {}
225     # 以經紀人划分, 創建以 broker 為鍵字典
226     broker_class = {}
227     # 加鎖
228     lock = threading.Lock()
229     # 運行初始提示
230     print('   -------------可供選擇的區域--------------\n東城 西城 朝陽 海淀 豐台 石景山 通州 昌平 大興 順義\n亦庄開發區 房山 門頭溝 平谷 懷柔 密雲 延慶 燕郊 香河')
231     option = input('請輸入相應區域的拼音,默認視為選擇全部:')
232     page = int(input('請輸入要獲取的頁數:'))
233     # 創建文件夾
234     if not os.path.exists('鏈家房產信息'):
235         os.mkdir('鏈家房產信息')
236     # 多線程列表
237     thread_list = []
238     for p in range(1, page+1):
239         if p == 1:
240             url = 'https://bj.lianjia.com/zufang/%s/' % option
241         else:
242             url = 'https://bj.lianjia.com/zufang/%s/pg%d/' % (option, p)
243         print('----------開始打印第 %d 頁信息----------' % p)
244         lock.acquire()
245         t1 = threading.Thread(target=main, args=(p,url))
246         # 設置守護線程
247         t1.setDaemon(True)
248         t1.start()
249         thread_list.append(t1)
250         lock.release()
251         print('----------打印第 %d 頁信息結束----------' % p)
252     for t1 in thread_list:
253         t1.join()
254     # 執行數據分析
255     analyze_data()

代碼還有很大的優化空間,python 是藝術品,需要慢慢的精雕細刻,在努力的路上!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM