Scrapy实战篇(一)之爬取链家网成交房源数据(上)
今天,我們就以鏈家網南京地區為例,來學習爬取鏈家網的成交房源數據。
這里推薦使用火狐瀏覽器,并且安裝firebug和firepath兩款插件,你會發現,這兩款插件會給我們后續的數據提取帶來很大的方便。
首先創建一個名稱為lianjia的項目。
需求分析
爬取數據的第一步當然是確定我們的需求,大方向就是我們想拿到南京地區的房源成交信息,但是具體的細節信息,我們需要從網頁來看,,我們直接在瀏覽器中輸入以下的網址https://nj.lianjia.com/chengjiao/,會顯示南京地區的成交的房源信息,包括名稱,房屋簡介,地理位置,成交日期,成交價格,成交單價等詳細信息,這樣我們就確定了我們想要的信息,我們在items.py文件中定義如下的一些字段。
#items.py from scrapy import Item,Fieldclass LianjiaItem(Item):region = Field() #行政區域href = Field() #房源鏈接name = Field() #房源名稱style = Field() #房源結構area = Field() #小區orientation = Field() #朝向decoration = Field() #裝修elevator = Field() #電梯floor = Field() #樓層高度build_year = Field() #建造時間sign_time = Field() #簽約時間unit_price = Field() #每平米單價total_price = Field() #總價fangchan_class = Field() #房產類型school = Field() #周邊學校subway = Field() #周邊地鐵請注意,以上的信息,并不是每一套房源都有的,比如下面的地鐵,學校,很多房源都是沒有的。
問題
你會發現一個問題,每一個頁面會呈現30條的房源信息,最下面一共可以顯示100頁,總計最多也就是3000條信息,南京地區的成交房源信息肯定不止這區區的3000條,那么如果直接從這個頁面通過翻頁來獲取數據,最多只能獲取到3000條信息,所以我們這里需要轉思路。
還是這個頁面,可以看到頁面上部列出了南京地區的行政區,我們隨意選擇一個,會發現,新的頁面依然是每一頁30條,共計100頁,但是我們有11個行政區,那么其數量也是翻了好幾倍了。
這個時候,你可能還是不滿足,我們想辦法看一下是不是還可以進一步向下劃分,沒錯那就是小區,我們把房源從11個行政區劃分到小區上,以小區為單位,每一個小區上面還有房源數據,這樣的話,我們的信息可以說比較全面了,當然了,我們需要做的工作也是要翻倍的。
總結
這里我們通過分析,總結出了如下的思路:
- 以行政區為單位,先獲取南京地區所有的小區信息
- 以小區為單位,獲取每一個小區里面的房源數據
- 最后就是獲取具體的每一個房源的信息。
具體實施
現在明確了我們的思路,下面就開始具體的實施。
編寫spider.py文件
from scrapy import Spider,Request import re from lxml import etree import json from urllib.parse import quote from lianjia.items import LianjiaItemclass Lianjia_spider(Spider):name = 'lianjia'allowed_domains = ['nj.lianjia.com']regions = {'gulou':'鼓樓','jianye':'建鄴','qinhuai':'秦淮','xuanwu':'玄武','yuhuatai':'雨花臺','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("//div[@class='page-box house-lst-page-box']/@page-data")[0] # 返回的是字符串字典sel = json.loads(sel) # 轉化為字典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("//div[@class='page-box house-lst-page-box']") #有可能為空total_pages = 0if len(content):page_data = json.loads(content[0].xpath('./@page-data')[0])total_pages = page_data.get("totalPage") # 獲取總的頁面數量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("//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):continueitem['href'] = href[0]content = cj.xpath('.//div[@class="title"]/a/text()') if len(content):content = content[0].split() # 按照空格分割成一個列表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得數,找不到的返回的是-1item['fangchan_class'] = ielif i.find("號線") != -1:item['subway'] = ielif i.find("學") != -1:item['school'] = iyield item我們對上面關鍵的地方進行解釋:
- start_requests
這個就是我們以行政區為單位,目的是爬取每一個行政區的小區列表。 - parse
對行政區返回的response進行解析,我們目的是拿到這個大的行政區,包含多少個頁面,其中的
total_pages就是具體的頁面數,接下來就是按照頁碼請求每一個頁面。 - parse_xiaoqu
上面返回了每一個頁面的信息,這個時候我們就把當前頁面的小區列表拿到,而后,在針對小區列表,每一個小區進行一次請求。 - parse_chengjiao
解析小區的頁面數,上面說到了,我們請求了每一個小區數據,這個小區肯定不止包含一頁的數據,那么我們這個方法就是將這個小區包含的頁面數抽取出來,而后針對每一個頁面進行請求 - parse_content
這個方法就是解析具體的頁面了,可以看到,這個方法里面包含了非常多的條件判斷,這是因為,我們之前定義的item字段里面的信息,并不是每一個小區都有的,就是說,我們要的信息他不是一個規規矩矩的信息,很多的房源沒有提供相關的信息,比如地鐵,周邊學校等等的信息,我們這里就是如果有這個信息,我們就把它提取出來,如果沒有的話,我們就給他自定義一個內容
。最后將item提交給item pipeline進行后續的處理。
由于這一節的信息比較多,我們就把它分為兩個小節,在下一節中,我們對拿到的數據進行后續的處理。
總結
以上是生活随笔為你收集整理的Scrapy实战篇(一)之爬取链家网成交房源数据(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EM算法(Expectation Max
- 下一篇: Scrapy实战篇(二)之爬取链家网成交