最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python實現(xiàn)并行抓取整站40萬條房價數(shù)據(jù)(可更換抓取城市)

 更新時間:2016年12月14日 09:59:27   作者:Data&Truth  
本文主要是以房價網(wǎng)房價信息爬蟲為例,對Python實現(xiàn)整站40萬條房價數(shù)據(jù)并行抓取(可更換抓取城市)的方法進行分析介紹。需要的朋友一起來看下吧

寫在前面

這次的爬蟲是關(guān)于房價信息的抓取,目的在于練習10萬以上的數(shù)據(jù)處理及整站式抓取。

數(shù)據(jù)量的提升最直觀的感覺便是對函數(shù)邏輯要求的提高,針對Python的特性,謹慎的選擇數(shù)據(jù)結(jié)構(gòu)。以往小數(shù)據(jù)量的抓取,即使函數(shù)邏輯部分重復(fù),I/O請求頻率密集,循環(huán)套嵌過深,也不過是1~2s的差別,而隨著數(shù)據(jù)規(guī)模的提高,這1~2s的差別就有可能擴展成為1~2h。

因此對于要抓取數(shù)據(jù)量較多的網(wǎng)站,可以從兩方面著手降低抓取信息的時間成本。

1)優(yōu)化函數(shù)邏輯,選擇適當?shù)臄?shù)據(jù)結(jié)構(gòu),符合Pythonic的編程習慣。例如,字符串的合并,使用join()要比“+”節(jié)省內(nèi)存空間。

2)依據(jù)I/O密集與CPU密集,選擇多線程、多進程并行的執(zhí)行方式,提高執(zhí)行效率。

一、獲取索引

包裝請求request,設(shè)置超時timeout

# 獲取列表頁面
def get_page(url):
 headers = {
  'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
      r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3',
  'Referer': r'http://bj.fangjia.com/ershoufang/',
  'Host': r'bj.fangjia.com',
  'Connection': 'keep-alive'
 }
 timeout = 60
 socket.setdefaulttimeout(timeout) # 設(shè)置超時
 req = request.Request(url, headers=headers)
 response = request.urlopen(req).read()
 page = response.decode('utf-8')
 return page

一級位置:區(qū)域信息

二級位置:板塊信息(根據(jù)區(qū)域位置得到板塊信息,以key_value對的形式存儲在dict中)

以dict方式存儲,可以快速的查詢到所要查找的目標。-> {'朝陽':{'工體','安貞','健翔橋'......}}

三級位置:地鐵信息(搜索地鐵周邊房源信息)

將所屬位置地鐵信息,添加至dict中。  -> {'朝陽':{'工體':{'5號線','10號線' , '13號線'},'安貞','健翔橋'......}}

對應(yīng)的url:http://bj.fangjia.com/ershoufang/--r-%E6%9C%9D%E9%98%B3%7Cw-5%E5%8F%B7%E7%BA%BF%7Cb-%E6%83%A0%E6%96%B0%E8%A5%BF%E8%A1%97

解碼后的urlhttp://bj.fangjia.com/ershoufang/--r-朝陽|w-5號線|b-惠新西街

根據(jù)url的參數(shù)模式,可以有兩種方式獲取目的url:

1)根據(jù)索引路徑獲得目的url

# 獲取房源信息列表(嵌套字典遍歷)
def get_info_list(search_dict, layer, tmp_list, search_list):
 layer += 1 # 設(shè)置字典層級
 for i in range(len(search_dict)):
  tmp_key = list(search_dict.keys())[i] # 提取當前字典層級key
  tmp_list.append(tmp_key) # 將當前key值作為索引添加至tmp_list
  tmp_value = search_dict[tmp_key]
  if isinstance(tmp_value, str): # 當鍵值為url時
   tmp_list.append(tmp_value) # 將url添加至tmp_list
   search_list.append(copy.deepcopy(tmp_list)) # 將tmp_list索引url添加至search_list
   tmp_list = tmp_list[:layer] # 根據(jù)層級保留索引
  elif tmp_value == '': # 鍵值為空時跳過
   layer -= 2   # 跳出鍵值層級
   tmp_list = tmp_list[:layer] # 根據(jù)層級保留索引
  else:
   get_info_list(tmp_value, layer, tmp_list, search_list) # 當鍵值為列表時,迭代遍歷
   tmp_list = tmp_list[:layer]
 return search_list

2)根據(jù)dict信息包裝url

 {'朝陽':{'工體':{'5號線'}}}

參數(shù):

——  r-朝陽

——  b-工體

——  w-5號線

組裝參數(shù):http://bj.fangjia.com/ershoufang/--r-朝陽|w-5號線|b-工體

1 # 根據(jù)參數(shù)創(chuàng)建組合url
2 def get_compose_url(compose_tmp_url, tag_args, key_args):
3  compose_tmp_url_list = [compose_tmp_url, '|' if tag_args != 'r-' else '', tag_args, parse.quote(key_args), ]
4  compose_url = ''.join(compose_tmp_url_list)
5  return compose_url

二、獲取索引頁最大頁數(shù)

# 獲取當前索引頁面頁數(shù)的url列表
def get_info_pn_list(search_list):
 fin_search_list = []
 for i in range(len(search_list)):
  print('>>>正在抓取%s' % search_list[i][:3])
  search_url = search_list[i][3]
  try:
   page = get_page(search_url)
  except:
   print('獲取頁面超時')
   continue
  soup = BS(page, 'lxml')
  # 獲取最大頁數(shù)
  pn_num = soup.select('span[class="mr5"]')[0].get_text()
  rule = re.compile(r'\d+')
  max_pn = int(rule.findall(pn_num)[1])
  # 組裝url
  for pn in range(1, max_pn+1):
   print('************************正在抓取%s頁************************' % pn)
   pn_rule = re.compile('[|]')
   fin_url = pn_rule.sub(r'|e-%s|' % pn, search_url, 1)
   tmp_url_list = copy.deepcopy(search_list[i][:3])
   tmp_url_list.append(fin_url)
   fin_search_list.append(tmp_url_list)
 return fin_search_list

三、抓取房源信息Tag

這是我們要抓取的Tag:

['區(qū)域', '板塊', '地鐵', '標題', '位置', '平米', '戶型', '樓層', '總價', '單位平米價格']

# 獲取tag信息
def get_info(fin_search_list, process_i):
 print('進程%s開始' % process_i)
 fin_info_list = []
 for i in range(len(fin_search_list)):
  url = fin_search_list[i][3]
  try:
   page = get_page(url)
  except:
   print('獲取tag超時')
   continue
  soup = BS(page, 'lxml')
  title_list = soup.select('a[class="h_name"]')
  address_list = soup.select('span[class="address]')
  attr_list = soup.select('span[class="attribute"]')
  price_list = soup.find_all(attrs={"class": "xq_aprice xq_esf_width"}) # select對于某些屬性值(屬性值中間包含空格)無法識別,可以用find_all(attrs={})代替
  for num in range(20):
   tag_tmp_list = []
   try:
    title = title_list[num].attrs["title"]
    print(r'************************正在獲取%s************************' % title)
    address = re.sub('\n', '', address_list[num].get_text()) 
    area = re.search('\d+[\u4E00-\u9FA5]{2}', attr_list[num].get_text()).group(0) 
    layout = re.search('\d[^0-9]\d.', attr_list[num].get_text()).group(0)
    floor = re.search('\d/\d', attr_list[num].get_text()).group(0)
    price = re.search('\d+[\u4E00-\u9FA5]', price_list[num].get_text()).group(0)
    unit_price = re.search('\d+[\u4E00-\u9FA5]/.', price_list[num].get_text()).group(0)
    tag_tmp_list = copy.deepcopy(fin_search_list[i][:3])
    for tag in [title, address, area, layout, floor, price, unit_price]:
     tag_tmp_list.append(tag)
    fin_info_list.append(tag_tmp_list)
   except:
    print('【抓取失敗】')
    continue
 print('進程%s結(jié)束' % process_i)
 return fin_info_list

四、分配任務(wù),并行抓取

對任務(wù)列表進行分片,設(shè)置進程池,并行抓取。

# 分配任務(wù)
def assignment_search_list(fin_search_list, project_num): # project_num每個進程包含的任務(wù)數(shù),數(shù)值越小,進程數(shù)越多
 assignment_list = []
 fin_search_list_len = len(fin_search_list)
 for i in range(0, fin_search_list_len, project_num):
  start = i
  end = i+project_num
  assignment_list.append(fin_search_list[start: end]) # 獲取列表碎片
 return assignment_list
 p = Pool(4) # 設(shè)置進程池
 assignment_list = assignment_search_list(fin_info_pn_list, 3) # 分配任務(wù),用于多進程
 result = [] # 多進程結(jié)果列表
 for i in range(len(assignment_list)):
  result.append(p.apply_async(get_info, args=(assignment_list[i], i)))
 p.close()
 p.join()
 for result_i in range(len(result)):
  fin_info_result_list = result[result_i].get()
  fin_save_list.extend(fin_info_result_list) # 將各個進程獲得的列表合并

通過設(shè)置進程池并行抓取,時間縮短為單進程抓取時間的3/1,總計時間3h。

電腦為4核,經(jīng)過測試,任務(wù)數(shù)為3時,在當前電腦運行效率最高。

五、將抓取結(jié)果存儲到excel中,等待可視化數(shù)據(jù)化處理

# 存儲抓取結(jié)果
def save_excel(fin_info_list, file_name):
 tag_name = ['區(qū)域', '板塊', '地鐵', '標題', '位置', '平米', '戶型', '樓層', '總價', '單位平米價格']
 book = xlsxwriter.Workbook(r'C:\Users\Administrator\Desktop\%s.xls' % file_name) # 默認存儲在桌面上
 tmp = book.add_worksheet()
 row_num = len(fin_info_list)
 for i in range(1, row_num):
  if i == 1:
   tag_pos = 'A%s' % i
   tmp.write_row(tag_pos, tag_name)
  else:
   con_pos = 'A%s' % i
   content = fin_info_list[i-1] # -1是因為被表格的表頭所占
   tmp.write_row(con_pos, content)
 book.close()

附上源碼

#! -*-coding:utf-8-*-
# Function: 房價調(diào)查
# Author:蘭茲
from urllib import parse, request
from bs4 import BeautifulSoup as BS
from multiprocessing import Pool
import re
import lxml
import datetime
import cProfile
import socket
import copy
import xlsxwriter
starttime = datetime.datetime.now()
base_url = r'http://bj.fangjia.com/ershoufang/'
test_search_dict = {'昌平': {'霍營': {'13號線': 'http://bj.fangjia.com/ershoufang/--r-%E6%98%8C%E5%B9%B3|w-13%E5%8F%B7%E7%BA%BF|b-%E9%9C%8D%E8%90%A5'}}}
search_list = [] # 房源信息url列表
tmp_list = [] # 房源信息url緩存列表
layer = -1
# 獲取列表頁面
def get_page(url):
 headers = {
  'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
      r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3',
  'Referer': r'http://bj.fangjia.com/ershoufang/',
  'Host': r'bj.fangjia.com',
  'Connection': 'keep-alive'
 }
 timeout = 60
 socket.setdefaulttimeout(timeout) # 設(shè)置超時
 req = request.Request(url, headers=headers)
 response = request.urlopen(req).read()
 page = response.decode('utf-8')
 return page
# 獲取查詢關(guān)鍵詞dict
def get_search(page, key):
 soup = BS(page, 'lxml')
 search_list = soup.find_all(href=re.compile(key), target='')
 search_dict = {}
 for i in range(len(search_list)):
  soup = BS(str(search_list[i]), 'lxml')
  key = soup.select('a')[0].get_text()
  value = soup.a.attrs['href']
  search_dict[key] = value
 return search_dict
# 獲取房源信息列表(嵌套字典遍歷)
def get_info_list(search_dict, layer, tmp_list, search_list):
 layer += 1 # 設(shè)置字典層級
 for i in range(len(search_dict)):
  tmp_key = list(search_dict.keys())[i] # 提取當前字典層級key
  tmp_list.append(tmp_key) # 將當前key值作為索引添加至tmp_list
  tmp_value = search_dict[tmp_key]
  if isinstance(tmp_value, str): # 當鍵值為url時
   tmp_list.append(tmp_value) # 將url添加至tmp_list
   search_list.append(copy.deepcopy(tmp_list)) # 將tmp_list索引url添加至search_list
   tmp_list = tmp_list[:layer] # 根據(jù)層級保留索引
  elif tmp_value == '': # 鍵值為空時跳過
   layer -= 2   # 跳出鍵值層級
   tmp_list = tmp_list[:layer] # 根據(jù)層級保留索引
  else:
   get_info_list(tmp_value, layer, tmp_list, search_list) # 當鍵值為列表時,迭代遍歷
   tmp_list = tmp_list[:layer]
 return search_list
# 獲取房源信息詳情
def get_info_pn_list(search_list):
 fin_search_list = []
 for i in range(len(search_list)):
  print('>>>正在抓取%s' % search_list[i][:3])
  search_url = search_list[i][3]
  try:
   page = get_page(search_url)
  except:
   print('獲取頁面超時')
   continue
  soup = BS(page, 'lxml')
  # 獲取最大頁數(shù)
  pn_num = soup.select('span[class="mr5"]')[0].get_text()
  rule = re.compile(r'\d+')
  max_pn = int(rule.findall(pn_num)[1])
  # 組裝url
  for pn in range(1, max_pn+1):
   print('************************正在抓取%s頁************************' % pn)
   pn_rule = re.compile('[|]')
   fin_url = pn_rule.sub(r'|e-%s|' % pn, search_url, 1)
   tmp_url_list = copy.deepcopy(search_list[i][:3])
   tmp_url_list.append(fin_url)
   fin_search_list.append(tmp_url_list)
 return fin_search_list
# 獲取tag信息
def get_info(fin_search_list, process_i):
 print('進程%s開始' % process_i)
 fin_info_list = []
 for i in range(len(fin_search_list)):
  url = fin_search_list[i][3]
  try:
   page = get_page(url)
  except:
   print('獲取tag超時')
   continue
  soup = BS(page, 'lxml')
  title_list = soup.select('a[class="h_name"]')
  address_list = soup.select('span[class="address]')
  attr_list = soup.select('span[class="attribute"]')
  price_list = soup.find_all(attrs={"class": "xq_aprice xq_esf_width"}) # select對于某些屬性值(屬性值中間包含空格)無法識別,可以用find_all(attrs={})代替
  for num in range(20):
   tag_tmp_list = []
   try:
    title = title_list[num].attrs["title"]
    print(r'************************正在獲取%s************************' % title)
    address = re.sub('\n', '', address_list[num].get_text())
    area = re.search('\d+[\u4E00-\u9FA5]{2}', attr_list[num].get_text()).group(0)
    layout = re.search('\d[^0-9]\d.', attr_list[num].get_text()).group(0)
    floor = re.search('\d/\d', attr_list[num].get_text()).group(0)
    price = re.search('\d+[\u4E00-\u9FA5]', price_list[num].get_text()).group(0)
    unit_price = re.search('\d+[\u4E00-\u9FA5]/.', price_list[num].get_text()).group(0)
    tag_tmp_list = copy.deepcopy(fin_search_list[i][:3])
    for tag in [title, address, area, layout, floor, price, unit_price]:
     tag_tmp_list.append(tag)
    fin_info_list.append(tag_tmp_list)
   except:
    print('【抓取失敗】')
    continue
 print('進程%s結(jié)束' % process_i)
 return fin_info_list
# 分配任務(wù)
def assignment_search_list(fin_search_list, project_num): # project_num每個進程包含的任務(wù)數(shù),數(shù)值越小,進程數(shù)越多
 assignment_list = []
 fin_search_list_len = len(fin_search_list)
 for i in range(0, fin_search_list_len, project_num):
  start = i
  end = i+project_num
  assignment_list.append(fin_search_list[start: end]) # 獲取列表碎片
 return assignment_list
# 存儲抓取結(jié)果
def save_excel(fin_info_list, file_name):
 tag_name = ['區(qū)域', '板塊', '地鐵', '標題', '位置', '平米', '戶型', '樓層', '總價', '單位平米價格']
 book = xlsxwriter.Workbook(r'C:\Users\Administrator\Desktop\%s.xls' % file_name) # 默認存儲在桌面上
 tmp = book.add_worksheet()
 row_num = len(fin_info_list)
 for i in range(1, row_num):
  if i == 1:
   tag_pos = 'A%s' % i
   tmp.write_row(tag_pos, tag_name)
  else:
   con_pos = 'A%s' % i
   content = fin_info_list[i-1] # -1是因為被表格的表頭所占
   tmp.write_row(con_pos, content)
 book.close()
if __name__ == '__main__':
 file_name = input(r'抓取完成,輸入文件名保存:')
 fin_save_list = [] # 抓取信息存儲列表
 # 一級篩選
 page = get_page(base_url)
 search_dict = get_search(page, 'r-')
 # 二級篩選
 for k in search_dict:
  print(r'************************一級抓取:正在抓取【%s】************************' % k)
  url = search_dict[k]
  second_page = get_page(url)
  second_search_dict = get_search(second_page, 'b-')
  search_dict[k] = second_search_dict
 # 三級篩選
 for k in search_dict:
  second_dict = search_dict[k]
  for s_k in second_dict:
   print(r'************************二級抓?。赫谧ト ?s】************************' % s_k)
   url = second_dict[s_k]
   third_page = get_page(url)
   third_search_dict = get_search(third_page, 'w-')
   print('%s>%s' % (k, s_k))
   second_dict[s_k] = third_search_dict
 fin_info_list = get_info_list(search_dict, layer, tmp_list, search_list)
 fin_info_pn_list = get_info_pn_list(fin_info_list)
 p = Pool(4) # 設(shè)置進程池
 assignment_list = assignment_search_list(fin_info_pn_list, 2) # 分配任務(wù),用于多進程
 result = [] # 多進程結(jié)果列表
 for i in range(len(assignment_list)):
  result.append(p.apply_async(get_info, args=(assignment_list[i], i)))
 p.close()
 p.join()
 for result_i in range(len(result)):
  fin_info_result_list = result[result_i].get()
  fin_save_list.extend(fin_info_result_list) # 將各個進程獲得的列表合并
 save_excel(fin_save_list, file_name)
 endtime = datetime.datetime.now()
 time = (endtime - starttime).seconds
 print('總共用時:%s s' % time)

總結(jié):

當抓取數(shù)據(jù)規(guī)模越大,對程序邏輯要求就愈嚴謹,對python語法要求就越熟練。如何寫出更加pythonic的語法,也需要不斷學習掌握的。

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關(guān)文章

  • Python中比較兩個字符串操作實例深究

    Python中比較兩個字符串操作實例深究

    本文深入探討Python中字符串比較的多種方法,并通過豐富的示例代碼演示如何靈活運用這些技巧,從基本的相等性檢查到更高級的正則表達式模式匹配,讀者將了解如何利用Python強大的字符串處理功能,提高對字符串數(shù)據(jù)的操作技能,以解決日常編程任務(wù)中的挑戰(zhàn)
    2023-12-12
  • Python實現(xiàn)按目錄層級輸出文件名并保存為excel

    Python實現(xiàn)按目錄層級輸出文件名并保存為excel

    當我們發(fā)現(xiàn)電腦的內(nèi)存很滿,或平時工作中文件夾管理不清晰,導(dǎo)致里面的文件數(shù)據(jù)很雜亂,查找很不方便,一個一個文件夾去看去找然后刪除又很浪費時間。本文將介紹如何利用Python實現(xiàn)按目錄層級輸出文件名并保存為excel,需要的可以參考一下
    2022-02-02
  • 總結(jié)的幾個Python函數(shù)方法設(shè)計原則

    總結(jié)的幾個Python函數(shù)方法設(shè)計原則

    這篇文章主要介紹了總結(jié)的幾個Python函數(shù)方法設(shè)計原則,本文講解了每個函數(shù)只做一件事、保持簡單、保持簡短、輸入使用參數(shù)、輸出使用return語句等內(nèi)容,需要的朋友可以參考下
    2015-06-06
  • YOLOv5小目標切圖檢測的思路與方法

    YOLOv5小目標切圖檢測的思路與方法

    目標檢測Yolo算法是非常經(jīng)典且應(yīng)用廣泛的算法,下面這篇文章主要給大家介紹了關(guān)于YOLOv5小目標切圖檢測的思路與方法,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-12-12
  • PyCharm如何設(shè)置Console控制臺輸出自動換行

    PyCharm如何設(shè)置Console控制臺輸出自動換行

    這篇文章主要介紹了PyCharm如何設(shè)置Console控制臺輸出自動換行問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 將Python腳本打包成exe文件

    將Python腳本打包成exe文件

    大家好,本篇文章主要講的是將Python腳本打包成exe文件,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-02-02
  • Python3 pywin32模塊安裝的詳細步驟

    Python3 pywin32模塊安裝的詳細步驟

    這篇文章主要介紹了Python3 pywin32模塊安裝的詳細步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • 十個Python中常用的pip命令總結(jié)

    十個Python中常用的pip命令總結(jié)

    相信對于大多數(shù)熟悉Python的人來說,一定都聽說并且使用過pip這個工具,但是對它的了解可能還不一定是非常的透徹,今天小編就來為大家介紹10個使用pip的小技巧,相信對大家以后管理和使用Python當中的標準庫會有幫助
    2022-07-07
  • 集成開發(fā)環(huán)境Pycharm的安裝及模板設(shè)置圖文教程

    集成開發(fā)環(huán)境Pycharm的安裝及模板設(shè)置圖文教程

    PyCharm是一種Python的集成開發(fā)環(huán)境,帶有一整套可以幫助用戶在使用Python語言開發(fā)時提高效率的工具,這篇文章主要介紹了集成開發(fā)環(huán)境Pycharm的安裝及模板設(shè)置,需要的朋友可以參考下
    2022-07-07
  • python最常用的三種輸出格式解讀

    python最常用的三種輸出格式解讀

    這篇文章主要介紹了python最常用的三種輸出格式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02

最新評論

四子王旗| 寿光市| 盐山县| 紫阳县| 临夏市| 涟水县| 嵊州市| 马山县| 遵义县| 津南区| 西青区| 晋江市| 登封市| 寻乌县| 嘉兴市| 锡林郭勒盟| 波密县| 庄浪县| 布拖县| 新化县| 马鞍山市| 宁都县| 平顺县| 东乡族自治县| 调兵山市| 固阳县| 宾川县| 汝南县| 奉化市| 得荣县| 甘孜| 峨眉山市| 栖霞市| 盖州市| 黔西县| 临邑县| 曲周县| 安化县| 北辰区| 盱眙县| 蓬莱市|