Scrapy實(shí)戰(zhàn)篇(一)之爬取鏈家網(wǎng)成交房源數(shù)據(jù)(上)

今天,我們就以鏈家網(wǎng)南京地區(qū)為例,來學(xué)習(xí)爬取鏈家網(wǎng)的成交房源數(shù)據(jù)。

這里推薦使用火狐瀏覽器,并且安裝firebug和firepath兩款插件,你會(huì)發(fā)現(xiàn),這兩款插件會(huì)給我們后續(xù)的數(shù)據(jù)提取帶來很大的方便。

首先創(chuàng)建一個(gè)名稱為lianjia的項(xiàng)目。

需求分析

爬取數(shù)據(jù)的第一步當(dāng)然是確定我們的需求,大方向就是我們想拿到南京地區(qū)的房源成交信息,但是具體的細(xì)節(jié)信息,我們需要從網(wǎng)頁來看,,我們直接在瀏覽器中輸入以下的網(wǎng)址https://nj.lianjia.com/chengjiao/,會(huì)顯示南京地區(qū)的成交的房源信息,包括名稱,房屋簡介,地理位置,成交日期,成交價(jià)格,成交單價(jià)等詳細(xì)信息,這樣我們就確定了我們想要的信息,我們在items.py文件中定義如下的一些字段。

#items.py
from scrapy import Item,Field

class LianjiaItem(Item):
    region = Field()      #行政區(qū)域
    href = Field()        #房源鏈接
    name = Field()        #房源名稱
    style = Field()       #房源結(jié)構(gòu)
    area = Field()           #小區(qū)
    orientation = Field()    #朝向
    decoration = Field()     #裝修
    elevator = Field()       #電梯
    floor = Field()          #樓層高度
    build_year = Field()     #建造時(shí)間
    sign_time = Field()      #簽約時(shí)間
    unit_price = Field()     #每平米單價(jià)
    total_price = Field()    #總價(jià)
    fangchan_class = Field()   #房產(chǎn)類型
    school = Field()         #周邊學(xué)校
    subway = Field()         #周邊地鐵

請注意,以上的信息,并不是每一套房源都有的,比如下面的地鐵,學(xué)校,很多房源都是沒有的。

問題

  • 你會(huì)發(fā)現(xiàn)一個(gè)問題,每一個(gè)頁面會(huì)呈現(xiàn)30條的房源信息,最下面一共可以顯示100頁,總計(jì)最多也就是3000條信息,南京地區(qū)的成交房源信息肯定不止這區(qū)區(qū)的3000條,那么如果直接從這個(gè)頁面通過翻頁來獲取數(shù)據(jù),最多只能獲取到3000條信息,所以我們這里需要轉(zhuǎn)思路。

  • 還是這個(gè)頁面,可以看到頁面上部列出了南京地區(qū)的行政區(qū),我們隨意選擇一個(gè),會(huì)發(fā)現(xiàn),新的頁面依然是每一頁30條,共計(jì)100頁,但是我們有11個(gè)行政區(qū),那么其數(shù)量也是翻了好幾倍了。

  • 這個(gè)時(shí)候,你可能還是不滿足,我們想辦法看一下是不是還可以進(jìn)一步向下劃分,沒錯(cuò)那就是小區(qū),我們把房源從11個(gè)行政區(qū)劃分到小區(qū)上,以小區(qū)為單位,每一個(gè)小區(qū)上面還有房源數(shù)據(jù),這樣的話,我們的信息可以說比較全面了,當(dāng)然了,我們需要做的工作也是要翻倍的。

總結(jié)

這里我們通過分析,總結(jié)出了如下的思路:

  • 以行政區(qū)為單位,先獲取南京地區(qū)所有的小區(qū)信息
  • 以小區(qū)為單位,獲取每一個(gè)小區(qū)里面的房源數(shù)據(jù)
  • 最后就是獲取具體的每一個(gè)房源的信息。

具體實(shí)施

現(xiàn)在明確了我們的思路,下面就開始具體的實(shí)施。

編寫spider.py文件

from scrapy import Spider,Request
import re
from lxml import etree
import json
from urllib.parse import quote
from lianjia.items import LianjiaItem

class Lianjia_spider(Spider):
    name = 'lianjia'
    allowed_domains = ['nj.lianjia.com']
    regions = {'gulou':'鼓樓',
               'jianye':'建鄴',
               'qinhuai':'秦淮',
               'xuanwu':'玄武',
               'yuhuatai':'雨花臺(tái)',
               'qixia':'棲霞',
               'jiangning':'江寧',
               'liuhe':'六合',
               'pukou':'浦口',
               'lishui':'漣水',
               'gaochun':'高淳'
    }

    def start_requests(self):
        for region in list(self.regions.keys()):
            url = "https://nj.lianjia.com/xiaoqu/" + region + "/"
            yield Request(url=url, callback=self.parse, meta={'region':region}) #用來獲取頁碼

    def parse(self, response):
        region = response.meta['region']
        selector = etree.HTML(response.text)
        sel = selector.xpath("http://div[@class='page-box house-lst-page-box']/@page-data")[0]  # 返回的是字符串字典
        sel = json.loads(sel)  # 轉(zhuǎn)化為字典
        total_pages = sel.get("totalPage")

        for i in range(int(total_pages)):
            url_page = "https://nj.lianjia.com/xiaoqu/{}/pg{}/".format(region, str(i + 1))
            yield Request(url=url_page, callback=self.parse_xiaoqu, meta={'region':region})

    def parse_xiaoqu(self,response):
        selector = etree.HTML(response.text)
        xiaoqu_list = selector.xpath('//ul[@class="listContent"]//li//div[@class="title"]/a/text()')
        for xq_name in xiaoqu_list:
            url = "https://nj.lianjia.com/chengjiao/rs" + quote(xq_name) + "/"
            yield Request(url=url, callback=self.parse_chengjiao, meta={'xq_name':xq_name, 
                                    'region':response.meta['region']})

    def parse_chengjiao(self,response):
        xq_name = response.meta['xq_name']
        selector = etree.HTML(response.text)
        content = selector.xpath("http://div[@class='page-box house-lst-page-box']")  #有可能為空
        total_pages = 0
        if len(content):
            page_data = json.loads(content[0].xpath('./@page-data')[0])
            total_pages = page_data.get("totalPage")  # 獲取總的頁面數(shù)量
        for i in range(int(total_pages)):
            url_page = "https://nj.lianjia.com/chengjiao/pg{}rs{}/".format(str(i+1), quote(xq_name))
            yield Request(url=url_page, callback=self.parse_content, meta={'region': response.meta['region']})

    def parse_content(self,response):
        selector = etree.HTML(response.text)
        cj_list = selector.xpath("http://ul[@class='listContent']/li")


        for cj in cj_list:
            item = LianjiaItem()
            item['region'] = self.regions.get(response.meta['region'])
            href = cj.xpath('./a/@href')  
            if not len(href):
                continue
            item['href'] = href[0]

            content = cj.xpath('.//div[@class="title"]/a/text()') 
            if len(content):
                content = content[0].split()  # 按照空格分割成一個(gè)列表
                item['name'] = content[0]
                item['style'] = content[1]
                item['area'] = content[2]

            content = cj.xpath('.//div[@class="houseInfo"]/text()')
            if len(content):
                content = content[0].split('|')
                item['orientation'] = content[0]
                item['decoration'] = content[1]
                if len(content) == 3:
                    item['elevator'] = content[2]
                else:
                    item['elevator'] = '無'

            content = cj.xpath('.//div[@class="positionInfo"]/text()')
            if len(content):
                content = content[0].split()
                item['floor'] = content[0]
                if len(content) == 2:
                    item['build_year'] = content[1]
                else:
                    item['build_year'] = '無'

            content = cj.xpath('.//div[@class="dealDate"]/text()')
            if len(content):
                item['sign_time'] = content[0]

            content = cj.xpath('.//div[@class="totalPrice"]/span/text()')
            if len(content):
                item['total_price'] = content[0]

            content = cj.xpath('.//div[@class="unitPrice"]/span/text()')
            if len(content):
                item['unit_price'] = content[0]

            content = cj.xpath('.//span[@class="dealHouseTxt"]/span/text()')  
            if len(content):
                for i in content:
                    if i.find("房屋滿") != -1:  # 找到了返回的是非-1得數(shù),找不到的返回的是-1
                        item['fangchan_class'] = i
                    elif i.find("號線") != -1:
                        item['subway'] = i
                    elif i.find("學(xué)") != -1:
                        item['school'] = i
            yield item

我們對上面關(guān)鍵的地方進(jìn)行解釋:

  • start_requests
    這個(gè)就是我們以行政區(qū)為單位,目的是爬取每一個(gè)行政區(qū)的小區(qū)列表。
  • parse
    對行政區(qū)返回的response進(jìn)行解析,我們目的是拿到這個(gè)大的行政區(qū),包含多少個(gè)頁面,其中的
    total_pages就是具體的頁面數(shù),接下來就是按照頁碼請求每一個(gè)頁面。
  • parse_xiaoqu
    上面返回了每一個(gè)頁面的信息,這個(gè)時(shí)候我們就把當(dāng)前頁面的小區(qū)列表拿到,而后,在針對小區(qū)列表,每一個(gè)小區(qū)進(jìn)行一次請求。
  • parse_chengjiao
    解析小區(qū)的頁面數(shù),上面說到了,我們請求了每一個(gè)小區(qū)數(shù)據(jù),這個(gè)小區(qū)肯定不止包含一頁的數(shù)據(jù),那么我們這個(gè)方法就是將這個(gè)小區(qū)包含的頁面數(shù)抽取出來,而后針對每一個(gè)頁面進(jìn)行請求
  • parse_content
    這個(gè)方法就是解析具體的頁面了,可以看到,這個(gè)方法里面包含了非常多的條件判斷,這是因?yàn)?,我們之前定義的item字段里面的信息,并不是每一個(gè)小區(qū)都有的,就是說,我們要的信息他不是一個(gè)規(guī)規(guī)矩矩的信息,很多的房源沒有提供相關(guān)的信息,比如地鐵,周邊學(xué)校等等的信息,我們這里就是如果有這個(gè)信息,我們就把它提取出來,如果沒有的話,我們就給他自定義一個(gè)內(nèi)容
    。最后將item提交給item pipeline進(jìn)行后續(xù)的處理。

由于這一節(jié)的信息比較多,我們就把它分為兩個(gè)小節(jié),在下一節(jié)中,我們對拿到的數(shù)據(jù)進(jìn)行后續(xù)的處理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容