基于Scrapy框架爬取廈門房?jī)r(jià)

本文的運(yùn)行環(huán)境是Win10,IDE是Pycharm,Python版本是3.6。
請(qǐng)先保證自己安裝好Pycharm和Scrapy。

  1. 爬取的網(wǎng)站是國內(nèi)著名的房天下網(wǎng),網(wǎng)址:http://esf.xm.fang.com/,網(wǎng)站界面如下圖所示。

    網(wǎng)站列表界面.png

    網(wǎng)站詳情界面.png

    可以看出該網(wǎng)站信息較為全面。

  2. 用Scrapy的Shell測(cè)試該網(wǎng)站是否能爬取。
    方法是在任意位置打開cmd或者PowerShell,輸入命令scrapy shell "esf.xm.fang.com",
    一般來說不會(huì)出現(xiàn)錯(cuò)誤,如果報(bào)錯(cuò)ImportError: DLL load failed: 操作系統(tǒng)無法運(yùn)行 %1。,解決方法是把C:\Windows\System32目錄下的libeay32.dll和ssleay32.dll刪除即可
    確定命令正確后運(yùn)行,結(jié)果如下圖。

    測(cè)試能否爬取1.png

    In[1]:后輸入命令view(response),確認(rèn)命令正確后運(yùn)行,會(huì)自動(dòng)彈出瀏覽器窗口,如果出現(xiàn)如下圖所示網(wǎng)站,則表示scrapy可以順利從網(wǎng)站獲取信息,即可以完成爬蟲任務(wù)。
    測(cè)試能夠爬取2.png

    從上圖看出運(yùn)行命令后打開的是本地的網(wǎng)站,即網(wǎng)站內(nèi)容可以順利從服務(wù)器緩存到本地。

  3. 在你的工程文件中按住Shit,鼠標(biāo)右擊呼喚出下圖所示菜單。
    選擇下圖所標(biāo)識(shí)的在此處打開PowerShell窗口,cmd和PowerShell起到的效果相同。

    打開PowerShell.png

    在PoweShell中運(yùn)行命令scrapy startproject XiamenHouse
    新建工程成功.png

    新建工程成功后,在PowerShell中進(jìn)入工程的文件,命令是 cd .\XiamenHouse
    新建爬蟲文件的命令是scrapy genspider house "esf.xm.fang.com"
    新建爬蟲成功.png

  4. 用Pycharm打開爬蟲工程


    打開爬蟲工程1.png

    選擇工程所在的文件夾打開后,工程結(jié)構(gòu)如下圖所示。


    image.png
  5. 觀察房屋詳情界面,需要提取15個(gè)字段,分別是:標(biāo)題title,總價(jià)price,首付downPayment,戶型sizeType,建筑面積size,單價(jià)unitPrice,朝向orientation,樓層floor,裝修decoration,社區(qū)community, 區(qū)域region,學(xué)校school,房源信息houseDetail,核心賣點(diǎn)keySellingPoint,小區(qū)配套equipment
    月供是動(dòng)態(tài)計(jì)算生成,較難爬取。

    image.png

    image.png

    根據(jù)上述字段總結(jié),編寫工程文件夾中的items.py文件

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()
  1. 編寫工程文件夾中的house.py文件
    需要進(jìn)行多級(jí)頁面爬取,從scrapy.http中引入Request方法。
    爬蟲名為house,用于scrapy crawl house命令中。
    廈門市有6個(gè)區(qū),分別為集美、翔安、同安、海滄、湖里、思明。
    每個(gè)區(qū)有8個(gè)價(jià)格分類
    價(jià)格分類.png

    start_urls這個(gè)列表中有6*8=48個(gè)url,parse函數(shù)用于解析這48個(gè)url,即分析每個(gè)區(qū)每個(gè)價(jià)格區(qū)間有多少頁房?jī)r(jià)信息。
    parse函數(shù)得到每個(gè)區(qū)每個(gè)價(jià)格區(qū)間的房?jī)r(jià)信息最大頁面數(shù)之后,通過字符串拼接得到每一頁的url。
    每一頁的url用yield Request(url,callback=self.parse1)發(fā)起請(qǐng)求,并調(diào)用parse1函數(shù)進(jìn)行解析。
    parse1函數(shù)用于獲取每一頁30個(gè)房?jī)r(jià)詳情頁面的url鏈接,通過yield Request(detailUrl,callback=self.parse2)發(fā)起請(qǐng)求,并調(diào)用parse2函數(shù)進(jìn)行解析。
    parse2的難點(diǎn)在于xpath的書寫,需要懂xpath基本語法,書寫時(shí)可以在瀏覽器的調(diào)試器中檢查是否正確。
    確定xpath書寫正確,成功獲取到字段后,將字段存入item,最后通過yield item交給管道處理。
    python3可以把變量名設(shè)置為中文,但必須全部是中文,不能為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個(gè),parse函數(shù)的作用是找出這48個(gè)分類中每個(gè)分類的最大頁數(shù)
    def parse(self, response):
        pageNum = response.xpath("http://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("http://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("http://h1[@class='title floatl']/text()").strip()
        item['price'] = find("http://div[@class='trl-item_top']/div[1]/i/text()") + "萬"
        item['downPayment'] = find("http://div[@class='trl-item']/text()").strip().strip("首付約 ")
        item['sizeType'] = find("http://div[@class='tab-cont-right']/div[2]/div[1]/div[1]/text()").strip()
        item['size'] = find("http://div[@class='tab-cont-right']/div[2]/div[2]/div[1]/text()")
        item['unitPrice'] = find("http://div[@class='tab-cont-right']/div[2]/div[3]/div[1]/text()")
        item['orientation'] = find("http://div[@class='tab-cont-right']/div[3]/div[1]/div[1]/text()")
        item['floor'] = find("http://div[@class='tab-cont-right']/div[3]/div[2]/div[1]/text()") + ' ' + \
                        find("http://div[@class='tab-cont-right']/div[3]/div[2]/div[2]/text()")
        item['decoration'] = find("http://div[@class='tab-cont-right']/div[3]/div[3]/div[1]/text()")
        item['community'] = find("http://div[@class='tab-cont-right']/div[4]/div[1]/div[2]/a/text()")
        item['region'] = find("http://div[@class='tab-cont-right']/div[4]/div[2]/div[2]/a[1]/text()").strip() + \
                         '-' + find("http://div[@class='tab-cont-right']/div[4]/div[2]/div[2]/a[2]/text()").strip()
        item['school'] = find("http://div[@class='tab-cont-right']/div[4]/div[3]/div[2]/a[1]/text()")
        detail_list = response.xpath("http://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("http://div[text()='核心賣點(diǎn)']/../div[2]/div/text()").extract()).strip()
        item['equipment'] = '\n'.join(response.xpath("http://div[text()='小區(qū)配套']/../div[2]/text()").extract()).strip()
        yield item
  1. 編寫工程文件夾中的pipelines.py文件
    house_list用于收集每次傳遞進(jìn)來的item
    close_spider函數(shù)用于指明爬蟲結(jié)束時(shí)進(jìn)行的操作,函數(shù)中把house_list先轉(zhuǎn)化為pandas的DataFrame,然后DataFrame轉(zhuǎn)化為excel,最后通過time.process_time() 函數(shù)打印程序運(yùn)行的總時(shí)間。
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("廈門房?jī)r(jià)數(shù)據(jù)(房天下版).xlsx",columns=[k for k in self.house_list[0].keys()])
        print("爬蟲程序共運(yùn)行{}秒".format(time.process_time()))
  1. 編寫工程文件夾中settings.py文件
    刪除掉了文件中自帶的注釋內(nèi)容,真正起作用的是下面這些代碼。
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.在工程文件夾下的任意一級(jí)目錄在cmd或PowerShell中運(yùn)行命令scrapy crawl house啟動(dòng)爬蟲程序,運(yùn)行程序產(chǎn)生的excel截圖如下。

產(chǎn)生的excel截圖.png

提示:

  1. 按照上述步驟正確進(jìn)行,能夠獲取房天下網(wǎng)站廈門房產(chǎn)的全部信息,本文作者在2018年6月17日的測(cè)試結(jié)果是共爬取26332條房?jī)r(jià)信息,總共用時(shí)1363秒,即22分43秒。平均爬取速度為19.32條/秒,1159條/分。
  2. 確保程序能夠正確運(yùn)行,只需要完全復(fù)制上述4個(gè)文件即可,整個(gè)工程已經(jīng)上傳github,鏈接:
    https://github.com/StevenLei2017/XiamenHouse
  3. 自己編寫代碼,進(jìn)行測(cè)試的時(shí)候,可以修改下面代碼減少運(yùn)行時(shí)間。
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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容