使用python抓取并分析数据—链家网(requests+BeautifulSoup)

azkb6080 7年前
   <p>本篇文章是使用python抓取数据的第一篇,使用request+BeautifulSoup的方法对页面进行抓取和数据提取。通过使用requests库对链家网二手房列表页进行抓取,通过BeautifulSoup对页面进行解析,并从中获取房源价格,面积,户型和关注度的数据。</p>    <p><img src="https://simg.open-open.com/show/33bc735cf445176e14328eaf8f8f4955.jpg"></p>    <h2>准备工作</h2>    <p>首先是开始抓取前准备工作,导入需要使用的库文件,这里主要使用的是requests和BeautifulSoup两个。Time库负责设置每次抓取的休息时间。这里并非全部,后续还会在过程中导入新的库。</p>    <pre>  <code class="language-python">import requests  import time  from bs4 import BeautifulSoup</code></pre>    <h2>抓取列表页</h2>    <p>开始抓取前先观察下目标页面或网站的结构,其中比较重要的是URL的结构。链家网的二手房列表页面共有100个,URL结构为http://bj.lianjia.com/ershoufang/pg9/,其中bj表示城市,/ershoufang/是频道名称,pg9是页面码。我们要抓取的是北京的二手房频道,所以前面的部分不会变,属于固定部分,后面的页面码需要在1-100间变化,属于可变部分。将URL分为两部分,前面的固定部分赋值给url,后面的可变部分使用for循环。</p>    <pre>  <code class="language-python">#设置列表页URL的固定部分  url='http://bj.lianjia.com/ershoufang/'  #设置页面页的可变部分  page=('pg')</code></pre>    <p>此外,还需要在很http请求中设置一个头部信息,否则很容易被封。头部信息网上有很多现成的,也可以使用httpwatch等工具来查看。具体细节按照具体情况进行调整。</p>    <pre>  <code class="language-python">#设置请求头部信息  headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',  'Accept':'text/html;q=0.9,*/*;q=0.8',  'Accept-Charset':'ISO-8859-1,utf-8;q=0.7,*;q=0.3',  'Accept-Encoding':'gzip',  'Connection':'close',  'Referer':'http://www.baidu.com/link?url=_andhfsjjjKRgEWkj7i9cFmYYGsisrnm2A-TN3XZDQXxvGsM9k9ZZSnikW2Yds4s&amp;wd=&amp;eqid=c3435a7d00006bd600000003582bfd1f'  }</code></pre>    <p>使用for循环生成1-100的数字,转化格式后与前面的URL固定部分拼成要抓取的URL。这里我们设置每两个页面间隔0.5秒。抓取到的页面保存在html中。</p>    <pre>  <code class="language-python">#循环抓取列表页信息  for i in range(1,100):       if i == 1:            i=str(i)            a=(url+page+i+'/')            r=requests.get(url=a,headers=headers)            html=r.content       else:            i=str(i)            a=(url+page+i+'/')            r=requests.get(url=a,headers=headers)            html2=r.content            html = html + html2       #每次间隔0.5秒       time.sleep(0.5)</code></pre>    <h2>解析页面并提取信息</h2>    <p>页面抓取完成后无法直接阅读和进行数据提取,还需要进行页面解析。我们使用BeautifulSoup对页面进行解析。变成我们在浏览器查看源代码中看到的样子。</p>    <pre>  <code class="language-python">#解析抓取的页面内容  lj=BeautifulSoup(html,'html.parser')</code></pre>    <p>完成页面解析后就可以对页面中的关键信息进行提取了。下面我们分别对房源的总价,房源信息和关注度三部分进行提取。</p>    <p>把页面div标签中class=priceInfo的部分提取出来,并使用for循环将其中每个房源的总价数据存在tp中。</p>    <pre>  <code class="language-python">#提取房源总价  price=lj.find_all('div',attrs={'class':'priceInfo'})  tp=[]  for a in price:      totalPrice=a.span.string      tp.append(totalPrice)</code></pre>    <p>提取房源信息和关注度的方法与提取房源价格的方法类似,下面是具体的代码,房源信息存储在hi中,关注度存储在fi中。</p>    <pre>  <code class="language-python">#提取房源信息  houseInfo=lj.find_all('div',attrs={'class':'houseInfo'})    hi=[]  for b in houseInfo:      house=b.get_text()      hi.append(house)</code></pre>    <pre>  <code class="language-python">#提取房源关注度  followInfo=lj.find_all('div',attrs={'class':'followInfo'})    fi=[]  for c in followInfo:      follow=c.get_text()      fi.append(follow)</code></pre>    <h2>创建数据表并清洗数据</h2>    <p>导入pandas库将前面提取的房源总价,和关注度等信息进行汇总生成数据表。便于后面的分析。</p>    <pre>  <code class="language-python">#导入pandas库  import pandas as pd  #创建数据表  house=pd.DataFrame({'totalprice':tp,'houseinfo':hi,'followinfo':fi})  #查看数据表的内容  house.head()</code></pre>    <p><img src="https://simg.open-open.com/show/6e3f4a6588383285565f5a1178760287.jpg"></p>    <p>前面提取的都只是信息,还不能直接使用,在分析前要对这些信息进行数据提取和清洗等工作。如房源信息,在表中每个房源的小区名称,户型,面积,朝向等信息都在一个字段中,无法直接使用。需要先进行分列操作。这里的规则比较明显,每个信息间都是以竖线分割的,因此我们只需要以竖线进行分列即可。</p>    <pre>  <code class="language-python">#对房源信息进行分列  houseinfo_split = pd.DataFrame((x.split('|') for x in house.houseinfo),index=house.index,columns=['xiaoqu','huxing','mianji','chaoxiang','zhuangxiu','dianti'])</code></pre>    <p>这是完成分列后的新数据表,房源的各种信息以及成为单独的字段。</p>    <pre>  <code class="language-python">#查看分列结果  houseinfo_split.head()</code></pre>    <p><img src="https://simg.open-open.com/show/de6a0a5df1acb8542fb9302adc327d36.jpg"></p>    <p>将分列后的新数据表在重新拼接回原有的数据表中,这样在后面的分析过程中可以与其他字段的信息配合使用。</p>    <pre>  <code class="language-python">#将分列结果拼接回原数据表  house=pd.merge(house,houseinfo_split,right_index=True, left_index=True)</code></pre>    <p>完成拼接后的数据表中既包含了原有字段,也包含了分列后的新增字段。</p>    <pre>  <code class="language-python">#查看拼接后的数据表  house.head()</code></pre>    <p><img src="https://simg.open-open.com/show/63db754664aa37549eea10a46f669e89.jpg"></p>    <p>使用相同的方法对房源关注度字段进行分列和拼接操作。这里的分列规则是斜杠。</p>    <pre>  <code class="language-python">#对房源关注度进行分列  followinfo_split = pd.DataFrame((x.split('/') for x in house.followinfo),index=house.index,columns=['guanzhu','daikan','fabu'])  #将分列后的关注度信息拼接回原数据表  house=pd.merge(house,followinfo_split,right_index=True, left_index=True)</code></pre>    <h2>房源户型分布情况</h2>    <p>前面我们经过对房源信息的分列获取了房源的朝向,户型等信息,这里我们对房源的户型情况进行汇总,看看北京在售二手房的户型分布情况。</p>    <p>首先按房源的户型对房源数量进行汇总,下面是具体的代码和结果。</p>    <pre>  <code class="language-python">#按房源户型类别进行汇总  huxing=house.groupby('huxing')['huxing'].agg(len)  #查看户型汇总结果  huxing</code></pre>    <p><img src="https://simg.open-open.com/show/0f65f6a44c292a8395dc732bbe343e93.jpg"></p>    <p>导入数值计算库mumpy对数据进行处理,并使用matplotlib绘制房源户型分布条形图。</p>    <pre>  <code class="language-python">#导入图表库  import matplotlib.pyplot as plt  #导入数值计算库  import numpy as np</code></pre>    <pre>  <code class="language-python">#绘制房源户型分布条形图  plt.rc('font', family='STXihei', size=11)  a=np.array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])  plt.barh([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],huxing,color='#052B6C',alpha=0.8,align='center',edgecolor='white')  plt.ylabel('户型')  plt.xlabel('数量')  plt.xlim(0,1300)  plt.ylim(0,20)  plt.title('房源户型分布情况')  plt.legend(['数量'], loc='upper right')  plt.grid(color='#95a5a6',linestyle='--', linewidth=1,axis='y',alpha=0.4)  plt.yticks(a,('1室0厅','1室1厅','1室2厅','2室0厅','2室1厅','2室2厅','3室0厅','3室1厅','3室2厅','3室3厅','4室1厅','4室2厅','4室3厅','5室2厅','5室3厅','6室1厅','6室2厅','7室2厅','7室3厅'))  plt.show()</code></pre>    <p>北京在售二手房中户型从1室0厅到7室3厅近20种分布广泛。在所有的户型中数量最多的是2室1厅,其次为3室1厅和3室2厅,以及2室2厅。较小的1室1厅数量也较多。较大的户型数量较少。另外,从在售户型的分布中我们也可以推测出售房人的一些情况。</p>    <h2> </h2>    <p><img src="https://simg.open-open.com/show/64b32f9e4dde510c830f1dd68f49281b.png"></p>    <p>房源面积分布情况</p>    <p>在数据表中,房源面积通过分列以及单独提取出来,但数字与中文的格式并不能直接使用。我们还需要对房源面积字段进行二次分列处理,提取出面积的数值。方法与前面的分列方法类似,我们使用“平”作为分列规则对房源面积进行二次分列。并将分列后的结果拼接回原数据表中。</p>    <pre>  <code class="language-python">#对房源面积进行二次分列  mianji_num_split = pd.DataFrame((x.split('平') for x in house.mianji),index=house.index,columns=['mianji_num','mi'])  #将分列后的房源面积拼接回原数据表  house=pd.merge(house,mianji_num_split,right_index=True, left_index=True)</code></pre>    <p>分列后的数据在使用前还需要进行清洗,通常的操作包括去除空格和格式转换。下面我们先对房源面积的值去除两端的空格,然后更改数值的格式以方便后面的计算。</p>    <pre>  <code class="language-python">#去除mianji_num字段两端的空格  house['mianji_num']=house['mianji_num'].map(str.strip)  #更改mianji_num字段格式为float  house['mianji_num']=house['mianji_num'].astype(float)</code></pre>    <p>清洗后的房源面积字段可以开始分析了。首先查看所有北京在售二手房的面积范围,下面是代码和结果。房源面积从18.85到332.63。</p>    <pre>  <code class="language-python">#查看所有房源面积的范围值  house['mianji_num'].min(),house['mianji_num'].max()  (18.850000000000001, 332.63)</code></pre>    <p>有了房源面积的范围后,就可以对面积进行分组了,我们以50为区间将房源面积分为7组。并统计所有房源在这7组中的分布情况。</p>    <pre>  <code class="language-python">#对房源面积进行分组  bins = [0, 50, 100, 150, 200, 250, 300, 350]  group_mianji = ['小于50', '50-100', '100-150', '150-200','200-250','250-300','300-350']  house['group_mianji'] = pd.cut(house['mianji_num'], bins, labels=group_mianji)  #按房源面积分组对房源数量进行汇总  group_mianji=house.groupby('group_mianji')['group_mianji'].agg(len)</code></pre>    <p>使用房源面积分组字段对房源数量进行分组并绘制条形图。</p>    <pre>  <code class="language-python">#绘制房源面积分布图  plt.rc('font', family='STXihei', size=15)  a=np.array([1,2,3,4,5,6,7])  plt.barh([1,2,3,4,5,6,7],group_mianji,color='#052B6C',alpha=0.8,align='center',edgecolor='white')  plt.ylabel('面积分组')  plt.xlabel('数量')  plt.title('房源面积分布')  plt.legend(['数量'], loc='upper right')  plt.grid(color='#95a5a6',linestyle='--', linewidth=1,axis='y',alpha=0.4)  plt.yticks(a,('小于50', '50-100', '100-150', '150-200','200-250','250-300','300-350'))  plt.show()</code></pre>    <p>在所有房源中,数量最多的是50-100,其次为100-150。随着面积增加数量减少。小于50的小面积房源也有一定数量的房源。</p>    <h2> </h2>    <p><img src="https://simg.open-open.com/show/092c579c6c3082d79aad393240ae810d.png"></p>    <p>房源关注度分布情况</p>    <p>房源关注度的情况与房源面积类似,第一次分列处理后得到的数据包含数字和中文,无法直接使用,需要再次通过分列处理提取关注度的数值,并对数值进行清洗和格式转换。下面是具体的代码。</p>    <pre>  <code class="language-python">#对房源关注度进行二次分列  guanzhu_num_split = pd.DataFrame((x.split('人') for x in house.guanzhu),index=house.index,columns=['guanzhu_num','ren'])  #将分列后的关注度数据拼接回原数据表  house=pd.merge(house,guanzhu_num_split,right_index=True, left_index=True)  #去除房源关注度字段两端的空格  house['guanzhu_num']=house['guanzhu_num'].map(str.strip)  #更改房源关注度及总价字段的格式  house[['guanzhu_num','totalprice']]=house[['guanzhu_num','totalprice']].astype(float)</code></pre>    <p>清洗完后查看所有房源关注度的区间,关注度从0到725。也就是说有些房子很热门,而有些房子没有人关注。这可能和房源上线和更新的情况有关,此外还要考虑房源的销售速度,热门房源可能很抢手,刚上线就成交了。因此我们对情况进行简化,暂时忽略掉这些复杂的情况。仅对关注度的分布情况进行统计。</p>    <pre>  <code class="language-python">#查看房源关注度的区间  house['guanzhu_num'].min(),house['guanzhu_num'].max()  (0.0, 725.0)</code></pre>    <p>将关注度以100为区间分为8组,并按关注度区间进行汇总统计房源数量。查看在售房源的关注度分布情况。</p>    <pre>  <code class="language-python">#对房源关注度进行分组  bins = [0, 100, 200, 300, 400, 500, 600, 700,800]  group_guanzhu = ['小于100', '100-200', '200-300', '300-400','400-500','500-600','600-700','700-800']  house['group_guanzhu'] = pd.cut(house['guanzhu_num'], bins, labels=group_guanzhu)  group_guanzhu=house.groupby('group_guanzhu')['group_guanzhu'].agg(len)</code></pre>    <p>绘制房源关注度分布条形图。</p>    <pre>  <code class="language-python">#绘制房源关注度分布图  plt.rc('font', family='STXihei', size=15)  a=np.array([1,2,3,4,5,6,7,8])  plt.barh([1,2,3,4,5,6,7,8],group_guanzhu,color='#052B6C',alpha=0.8,align='center',edgecolor='white')  plt.ylabel('关注度分组')  plt.xlabel('数量')  plt.xlim(0,3000)  plt.title('房源关注度分布')  plt.legend(['数量'], loc='upper right')  plt.grid(color='#95a5a6',linestyle='--', linewidth=1,axis='y',alpha=0.4)  plt.yticks(a,('小于100', '100-200', '200-300', '300-400','400-500','500-600','600-700','700-800'))  plt.show()</code></pre>    <p>在3000个房源中,近2500个房源的关注度小于100,关注度大于400的房源则较少。这里需要再次说明的是关注度数据无法准确的表示房源的热门程度。热门房源可能由于出售速度快而关注度较少。因此关注度数据仅供参考。</p>    <p><img src="https://simg.open-open.com/show/20be513650c59d49afd3b73018be23e4.png"></p>    <h2>房源聚类分析</h2>    <p>最后,我们对所有在售房源按总价,面积和关注度进行聚类分析。将在售房源按总价,面积和关注度的相似性分在不同的类别中。</p>    <pre>  <code class="language-python">#导入sklearn中的KMeans进行聚类分析  from sklearn.cluster import KMeans  #使用房源总价,面积和关注度三个字段进行聚类  house_type = np.array(house[['totalprice','mianji_num','guanzhu_num']])  #设置质心数量为3  clf=KMeans(n_clusters=3)  #计算聚类结果  clf=clf.fit(house_type)</code></pre>    <p>通过计算我们将在售房源分为三个类别,下面是每个类别的中心点坐标。</p>    <pre>  <code class="language-python">#查看分类结果的中心坐标  clf.cluster_centers_  array([[ 772.97477064, 112.02389908, 58.96330275],  [ 434.51073861, 84.92950236, 61.20115244],  [ 1473.26719577, 170.65402116, 43.32275132]])</code></pre>    <pre>  <code class="language-python">#在原数据表中标注所属类别  house['label']= clf.labels_</code></pre>    <p><img src="https://simg.open-open.com/show/623637e113c2ef553d0f55fba3c6be5e.jpg"></p>    <p>根据三个类别在总价,面积和关注度三个点的中心坐标,我们将在售房源分为三个类别,第一个类别是总价低,面积低,关注度高的房源。第二个类别是总价居中,面积居中,关注度居中的类别。第三个类别是总价高,面积高,关注度低的类别。</p>    <p>从营销和用户体验的角度来看,在广告和列表页的默认排序中应该给予总价400万,面积80属性的房源更高的权重。这个类别的房源可以吸引最多的用户关注。</p>    <p>—【所有文章及图片版权归 蓝鲸(王彦平)所有。欢迎转载,但请注明转自“蓝鲸网站分析博客”。】—</p>    <p> </p>    <p>来自:http://bluewhale.cc/2016-12-04/use-python-crawl-and-analysis-data-lianjia-requests-and-beautifulsoup.html</p>    <p> </p>