本文的運作環境是Win10,IDE是Pycharm,Python版本是3.6。
請先保證自己安裝好Pycharm和Scrapy。
- 爬取的網站是國内著名的房天下網,網址: http://esf.xm.fang.com/ ,網站界面如下圖所示。 網站清單界面.png
基于Scrapy架構爬取廈門房價 基于Scrapy架構爬取廈門房價 網站詳情界面.png
可以看出該網站資訊較為全面。
-
用Scrapy的Shell測試該網站是否能爬取。
方法是在任意位置打開cmd或者PowerShell,輸入指令scrapy shell "esf.xm.fang.com",
一般來說不會出現錯誤,如果報錯ImportError: DLL load failed: 作業系統無法運作 %1。,解決方法是把C:\Windows\System32目錄下的libeay32.dll和ssleay32.dll删除即可。
确定指令正确後運作,結果如下圖。
基于Scrapy架構爬取廈門房價 測試能否爬取1.png
在In[1]:後輸入指令view(response),确認指令正确後運作,會自動彈出浏覽器視窗,如果出現如下圖所示網站,則表示scrapy可以順利從網站擷取資訊,即可以完成爬蟲任務。
基于Scrapy架構爬取廈門房價 測試能夠爬取2.png
從上圖看出運作指令後打開的是本地的網站,即網站内容可以順利從伺服器緩存到本地。
-
在你的工程檔案中按住Shit,滑鼠右擊呼喚出下圖所示菜單。
選擇下圖所辨別的在此處打開PowerShell視窗,cmd和PowerShell起到的效果相同。
基于Scrapy架構爬取廈門房價 打開PowerShell.png
在PoweShell中運作指令scrapy startproject XiamenHouse
基于Scrapy架構爬取廈門房價 建立工程成功.png
建立工程成功後,在PowerShell中進入工程的檔案,指令是 cd .\XiamenHouse
建立爬蟲檔案的指令是scrapy genspider house "esf.xm.fang.com"
建立爬蟲成功.png基于Scrapy架構爬取廈門房價 - 用Pycharm打開爬蟲工程
基于Scrapy架構爬取廈門房價 打開爬蟲工程1.png
選擇工程所在的檔案夾打開後,工程結構如下圖所示。
image.png基于Scrapy架構爬取廈門房價 -
觀察房屋詳情界面,需要提取15個字段,分别是:标題title,總價price,首付downPayment,戶型sizeType,建築面積size,單價unitPrice,朝向orientation,樓層floor,裝修decoration,社群community, 區域region,學校school,房源資訊houseDetail,核心賣點keySellingPoint,小區配套equipment
月供是動态計算生成,較難爬取。
基于Scrapy架構爬取廈門房價 根據上述字段總結,編寫工程檔案夾中的items.py檔案基于Scrapy架構爬取廈門房價
import scrapy
from scrapy import Field
class XiamenHouseItem(scrapy.Item):
title = Field()
price = Field()
downPayment = Field()
monthInstallment = Field()
sizeType = Field()
size = Field()
unitPrice = Field()
orientation = Field()
floor = Field()
decoration = Field()
community = Field()
region = Field()
school = Field()
houseDetail = Field()
keySellingPoint = Field()
equipment = Field()
-
編寫工程檔案夾中的house.py檔案
需要進行多級頁面爬取,從scrapy.http中引入Request方法。
爬蟲名為house,用于scrapy crawl house指令中。
廈門市有6個區,分别為集美、翔安、同安、海滄、湖裡、思明。
每個區有8個價格分類
基于Scrapy架構爬取廈門房價 價格分類.png
在start_urls這個清單中有6*8=48個url,parse函數用于解析這48個url,即分析每個區每個價格區間有多少頁房價資訊。
parse函數得到每個區每個價格區間的房價資訊最大頁面數之後,通過字元串拼接得到每一頁的url。
每一頁的url用yield Request(url,callback=self.parse1)發起請求,并調用parse1函數進行解析。
parse1函數用于擷取每一頁30個房價詳情頁面的url連結,通過yield Request(detailUrl,callback=self.parse2)發起請求,并調用parse2函數進行解析。
parse2的難點在于xpath的書寫,需要懂xpath基本文法,書寫時可以在浏覽器的調試器中檢查是否正确。
确定xpath書寫正确,成功擷取到字段後,将字段存入item,最後通過yield item交給管道處理。
python3可以把變量名設定為中文,但必須全部是中文,不能為100萬以下這種形式。
# -*- coding: utf-8 -*-
import scrapy
from scrapy.http import Request
from XiamenHouse.items import XiamenHouseItem
import json
class HouseSpider(scrapy.Spider):
name = 'house'
allowed_domains = ['esf.xm.fang.com']
start_urls = []
region_dict = dict(
集美 = "house-a0354",
翔安 = "house-a0350",
同安 = "house-a0353",
海滄 = "house-a0355",
湖裡 = "house-a0351",
思明 = "house-a0352"
)
price_dict = dict(
d100 = "d2100",
c100d200 = "c2100-d2200",
c200d250 = "c2200-d2250",
c250d300 = "c2250-d2300",
c300d400 = "c2300-d2400",
c400d500 = "c2400-d2500",
c500d600 = "c2500-d2600",
c600 = "c2600"
)
for region in list(region_dict.keys()):
for price in list(price_dict.keys()):
url = "http://esf.xm.fang.com/{}/{}/".format(region_dict[region],price_dict[price])
start_urls.append(url)
#start_urls共有48個,parse函數的作用是找出這48個分類中每個分類的最大頁數
def parse(self, response):
pageNum = response.xpath("//span[@class='txt']/text()").extract()[0].strip('共').strip('頁')
for i in range(1,int(pageNum)+1):
url = "{}-i3{}/".format(response.url.strip('/'),i)
yield Request(url,callback=self.parse1)
def parse1(self, response):
house_list = response.xpath("//div[@class='houseList']/dl")
for house in house_list:
if "list" in house.xpath("@id").extract()[0]:
detailUrl = "http://esf.xm.fang.com" + house.xpath("dd[1]/p/a/@href").extract()[0]
yield Request(detailUrl,callback=self.parse2)
def parse2(self, response):
def find(xpath,pNode=response):
if len(pNode.xpath(xpath)):
return pNode.xpath(xpath).extract()[0]
else:
return ''
item = XiamenHouseItem()
item['title'] = find("//h1[@class='title floatl']/text()").strip()
item['price'] = find("//div[@class='trl-item_top']/div[1]/i/text()") + "萬"
item['downPayment'] = find("//div[@class='trl-item']/text()").strip().strip("首付約 ")
item['sizeType'] = find("//div[@class='tab-cont-right']/div[2]/div[1]/div[1]/text()").strip()
item['size'] = find("//div[@class='tab-cont-right']/div[2]/div[2]/div[1]/text()")
item['unitPrice'] = find("//div[@class='tab-cont-right']/div[2]/div[3]/div[1]/text()")
item['orientation'] = find("//div[@class='tab-cont-right']/div[3]/div[1]/div[1]/text()")
item['floor'] = find("//div[@class='tab-cont-right']/div[3]/div[2]/div[1]/text()") + ' ' + \
find("//div[@class='tab-cont-right']/div[3]/div[2]/div[2]/text()")
item['decoration'] = find("//div[@class='tab-cont-right']/div[3]/div[3]/div[1]/text()")
item['community'] = find("//div[@class='tab-cont-right']/div[4]/div[1]/div[2]/a/text()")
item['region'] = find("//div[@class='tab-cont-right']/div[4]/div[2]/div[2]/a[1]/text()").strip() + \
'-' + find("//div[@class='tab-cont-right']/div[4]/div[2]/div[2]/a[2]/text()").strip()
item['school'] = find("//div[@class='tab-cont-right']/div[4]/div[3]/div[2]/a[1]/text()")
detail_list = response.xpath("//div[@class='content-item fydes-item']/div[2]/div")
detail_dict = {}
for detail in detail_list:
key = find("span[1]/text()",detail)
value = find("span[2]/text()",detail).strip()
detail_dict[key] = value
item['houseDetail'] = json.dumps(detail_dict,ensure_ascii=False)
item['keySellingPoint'] = '\n'.join(response.xpath("//div[text()='核心賣點']/../div[2]/div/text()").extract()).strip()
item['equipment'] = '\n'.join(response.xpath("//div[text()='小區配套']/../div[2]/text()").extract()).strip()
yield item
-
編寫工程檔案夾中的pipelines.py檔案
house_list用于收集每次傳遞進來的item
close_spider函數用于指明爬蟲結束時進行的操作,函數中把house_list先轉化為pandas的DataFrame,然後DataFrame轉化為excel,最後通過time.process_time() 函數列印程式運作的總時間。
import time
import pandas as pd
class XiamenhousePipeline(object):
house_list = []
def process_item(self, item, spider):
self.house_list.append(dict(item))
return item
def close_spider(self, spider):
df = pd.DataFrame(self.house_list)
df.to_excel("廈門房價資料(房天下版).xlsx",columns=[k for k in self.house_list[0].keys()])
print("爬蟲程式共運作{}秒".format(time.process_time()))
-
編寫工程檔案夾中settings.py檔案
删除掉了檔案中自帶的注釋内容,真正起作用的是下面這些代碼。
BOT_NAME = 'XiamenHouse'
SPIDER_MODULES = ['XiamenHouse.spiders']
NEWSPIDER_MODULE = 'XiamenHouse.spiders'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
ROBOTSTXT_OBEY = False
CONCURRENT_REQUESTS = 96
ITEM_PIPELINES = {
'XiamenHouse.pipelines.XiamenhousePipeline': 300,
}
9.在工程檔案夾下的任意一級目錄在cmd或PowerShell中運作指令scrapy crawl house啟動爬蟲程式,運作程式産生的excel截圖如下。
産生的excel截圖.png
提示:
- 按照上述步驟正确進行,能夠擷取房天下網站廈門房産的全部資訊,本文作者在2018年6月17日的測試結果是共爬取26332條房價資訊,總共用時1363秒,即22分43秒。平均爬取速度為19.32條/秒,1159條/分。
- 確定程式能夠正确運作,隻需要完全複制上述4個檔案即可,整個工程已經上傳github,連結: https://github.com/StevenLei2017/XiamenHouse
- 自己編寫代碼,進行測試的時候,可以修改下面代碼減少運作時間。
for region in list(region_dict.keys()):
for price in list(price_dict.keys()):
url = "http://esf.xm.fang.com/{}/{}/".format(region_dict[region],price_dict[price])
start_urls.append(url)
改為
for region in list(region_dict.keys())[:1]:
for price in list(price_dict.keys())[:1]:
url = "http://esf.xm.fang.com/{}/{}/".format(region_dict[region],price_dict[price])
start_urls.append(url)