作者:xiaoyu
微信公眾號:Python資料科學
知乎:https://zhuanlan.zhihu.com/pypcfx
本篇介紹一個scrapy
的實戰爬蟲專案,並對爬取資訊進行簡單的資料分析。目標是北京二手房資訊
,下面開始分析。
網頁結構分析
採用安居客網頁資訊作為二手房的資訊來源。直接點選進入二手房資訊的頁面。
每頁的住房資訊:
點開連結後的詳細資訊:
博主並沒有採用分割槽域進行爬取,博主是直接進行全部爬取,然後迴圈下一頁完成的。步驟很簡單,如下:
- 先把每一頁的所有二手住房詳細連結爬取到
- 請求每一個爬取到的詳細連結,解析住房資訊
- 完成所有解析後,請求下一頁的連結
- 返回步驟一迴圈,直到返回內容為空
Scrapy程式碼實現
資料結構定義
Scrapy
中的後設資料field
其實是繼承了Python中的字典
資料型別,使用起來很方便,博主直接定義了幾個住房的資訊,如下程式碼所示。當然還有高階的用法,配合itemloader
加入processor
,這裡只使用簡單的定義即可。
class AnjukeItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
price = scrapy.Field()
mode = scrapy.Field()
area = scrapy.Field()
floor = scrapy.Field()
age = scrapy.Field()
location = scrapy.Field()
district = scrapy.Field()
pass
複製程式碼
爬蟲解析
- 定義了一個繼承
Scrapy
自帶的爬蟲類Spider
。 - 然後一個必不可少的東西是
name
,它貫穿了整個Scrapy
的始終,後面會看到它的作用。 start_urls
是初始請求的url的列表,也可以有多個初始url,這裡只有一個。- 由於
Scrapy
的Spider
類中預設使用了Request
請求,因此這裡選擇不覆蓋Request
,使用預設請求,且請求中呼叫parse回撥函式。 - 解析部分用
Scrapy
的高階selector
選擇器的xpath
進行解析。
parse
函式請求中有兩個yield
,代表生成器。
- 第一個
yield
返回每一頁的下一頁連結next_pageurl
。 - 第二個
yield
返回每一頁所有的住房詳細連結,並再次Request
請求跟進,然後呼叫下一個回撥函式parse_detail
。
請求的過程中如果速度過快,會要求輸入驗證碼
,這裡放慢了請求速度,暫不處理驗證部分(後續慢慢介紹)。
class AnjukeSpider(scrapy.Spider):
name = 'anjuke'
# custom_settings = {
# 'REDIRECT_ENABLED': False
# }
start_urls = ['https://beijing.anjuke.com/sale/']
def parse(self, response):
# 驗證碼處理部分
pass
# next page link
next_url = response.xpath(
'//*[@id="content"]/div[4]/div[7]/a[7]/@href').extract()[0]
print('*********' + str(next_url) + '**********')
if next_url:
yield scrapy.Request(url=next_url,
callback=self.parse)
# 爬取每一頁的所有房屋連結
num = len(response.xpath(
'//*[@id="houselist-mod-new"]/li').extract())
for i in range(1, num + 1):
url = response.xpath(
'//*[@id="houselist-mod-new"]/li[{}]/div[2]/div[1]/a/@href'
.format(i)).extract()[0]
yield scrapy.Request(url, callback=self.parse_detail)
複製程式碼
parse_detail
回撥函式中使用itemloader
解析items
住房資訊,並返回載有資訊的item
。
def parse_detail(self, response):
houseinfo = response.xpath('//*[@class="houseInfo-wrap"]')
if houseinfo:
l = ItemLoader(AnjukeItem(), houseinfo)
l.add_xpath('mode', '//div/div[2]/dl[1]/dd/text()')
l.add_xpath('area', '//div/div[2]/dl[2]/dd/text()')
l.add_xpath('floor', '//div/div[2]/dl[4]/dd/text()')
l.add_xpath('age', '//div/div[1]/dl[3]/dd/text()')
l.add_xpath('price', '//div/div[3]/dl[2]/dd/text()')
l.add_xpath('location', '//div/div[1]/dl[1]/dd/a/text()')
l.add_xpath('district', '//div/div[1]/dl[2]/dd/p/a[1]/text()')
yield l.load_item()
複製程式碼
資料清洗
由於爬取後的items
資料很亂,有各種\n,\t
等符號,因此在pipelines
中進行簡單的清理工作,使用正規表示式
實現,程式碼如下:
import re
def list2str(value):
new = ''.join(value).strip()
return new
class AnjukePipeline(object):
def process_item(self, item, spider):
area = item['area']
price = item['price']
loc = item['location']
district = item['district']
mode = item['mode']
age = item['age']
floor = item['floor']
modes = list2str(mode)
item['area'] = int(re.findall(r'\d+', list2str(area))[0])
item['age'] = int(re.findall(r'\d+', list2str(age))[0])
item['floor'] = list2str(floor)
item['location'] = list2str(loc)
item['district'] = list2str(district)
item['price'] = int(re.findall(r'\d+', list2str(price))[0])
item['mode'] = modes.replace('\t', '').replace('\n', '')
return item
複製程式碼
別忘記在setting
裡面設定pipeline
引數。
ITEM_PIPELINES = {
'anjuke.pipelines.AnjukePipeline': 300,
}
複製程式碼
命令列執行
我們想要將爬取的資料輸出到一個檔案中,csv
或者json
,我們這裡輸出為csv格式
的檔案。
在Scrapy中只需要一個command
指令即可完成,在專案檔案下的命令列輸入:
scrapy crawl anjuke -o items.csv
複製程式碼
命令列中的anjuke
就是最開始我們定義的name
。
開始進行爬取:
資料視覺化分析
爬取資料後,我們得到了一個csv
檔案,開啟顯示如下:
然後,我們將使用jupyter notebook
進行資料分析,程式碼如下:
簡單分析一下各大區的每平米二手房單價
和各大區二手房數量
,資料僅為部分,博主沒等資料全部爬取完,僅供參考。當然也可根據實際情況進行更復雜的資料分析和機器學習進行房價預測。
效果圖如下:
##總結 本篇只是一個簡單的例子,一個完整的高效的爬蟲還有很多需要完善。
- 加入代理
ip池
scrapd
的部署分散式爬蟲- 增量式的爬蟲考慮
- .... 這些將在後續會慢慢進行介紹,完畢。
關注微信公眾號Python資料科學
,帶你走進資料的世界。