python爬取链家北京二手房信息(BeautifulSoup)
由于我是AI语言模型,在编写时链家网页中的属性名称与参考博客存在差异,并且由于结构的变化可能会导致某些功能失效。因此直接套用本博客的代码可能会出现无法获取所需数据的情况,请各位读者在使用时结合自身需求以及当前网站的具体情况来调整代码
一、准备
使用的包有:urllib.request、bs4、pandas、numpy、re、time
urllib.request:用来打开和浏览url中内容
bs4:爬取网页
pandas:生成数据表,并保存为csv文件
numpy:循环的时候用了一下,个人感觉好像可以不用,但是没试过
re:使用正则表达式提取需要的内容
time:为了防止访问网站过于频繁报错,使用time.sleep()暂停一段时间
首先引入包
from urllib.request import urlopen
from bs4 import BeautifulSoup
import pandas as pd
import numpy as np
import re
import time
python
二、抓取数据
本博客抓取的信息有:
Direction (房屋朝向)、District (所在商业区)、Floor (楼层)、Garden (房屋所在小区)、Layout (户型)、Price (总价)、Renovation (房屋装修)、Size (面积)、Year (年份)以及Id (房屋编号)
ps:该程序获取的数据因采用科学记数法,在CSV文件中以特殊格式显示故无法直接使用其中的唯一标识字段(仅此字段存在问题)。如需解决此问题的朋友可自行修改代码进行处理最终结果将在末尾展示

房屋编号并不在页面中显示,而是在代码里

请先熟悉目标网站的地址框架,并举个例子说明其具体的访问路径,请访问[北京东城第2页二手房房源_北京东城第2页二手房出售|买卖|交易信息(北京链家)](https://bj.lianjia.com/ershoufang/dongcheng/pg2/ "北京东城第2页二手房房源_北京东城第2页二手房出售|买卖|交易信息北京链家)
bj代表城市中的具体区域名称,在此语境中特指北京市。
ershoufang标识的是一个房地产信息平台下的二级频道,在线提供二手房交易服务。
dongcheng用作区级行政区域内的街道代称,在此系统中被定义为东城区。
pg2赋值为当前页面的唯一编码标识符,在数据处理流程中对应记录着第二页的内容信息。

我们决定获取北京地区的二手房信息,并已确定好这一部分内容(http://bj.lianjia.com/ershoufang/)。同时还需要遍历各个城区以及不同的页面以完成数据采集工作。为了实现这一目标,在外层循环中依次访问各个城区,在内层循环中则逐步探索每个页面的数据。
chengqu = {'dongcheng': '东城区', 'xicheng': '西城区', 'chaoyang': '朝阳区', 'haidian': '海淀区', 'fengtai': '丰台区',
'shijingshan': '石景山区','tongzhou': '通州区', 'changping': '昌平区', 'daxing': '大兴区', 'shunyi': '顺义区',
'fangshan': '房山区'}
for cq in chengqu.keys():
url = 'https://bj.lianjia.com/ershoufang/' + cq + '/' # 组成所选城区的URL
...
for j in np.arange(1, int(total_page) + 1):
page_url = url + 'pg' + str(j) # 组成所选城区页面的URL
....
python

其中步骤要求统计选定区域内的总页面数量,并从div标签所属类为page-box且子类名为house-lst-page-box的标签中解析出第三个子标签中的page-data属性值

total_page = re.sub('\D', '', bsObj.find('div', 'page-box house-lst-page-box').contents[0].attrs['page-data'])[:-1] # 获取所选城区总页数
python
从需要的信息中提取一些内容时,在页面代码中查找所需部分,并注意观察这些提取内容的格式,并将其转换为我们所需存储的形式

识别class为houseInfo、positionInfo、totalPrice的div元素以及class为noresultRecommend img LOGCLICKDATA的a标签,并调用get_text()方法获取文本信息;接着将获取到的内容进行拆分,并将其存储为列表形式。
识别class为houseInfo、positionInfo、totalPrice的div元素以及class为noresultRecommend img LOGCLICKDATA的a标签,并调用get_text()方法获取文本信息;接着将获取到的内容进行拆分,并将其存储为列表形式。
page_html = urlopen(page_url)
page_bsObj = BeautifulSoup(page_html)
info = page_bsObj.findAll("div", {"class": "houseInfo"})
position_info = page_bsObj.findAll("div", {"class": "positionInfo"})
totalprice = page_bsObj.findAll("div", {"class": "totalPrice"})
idinfo = page_bsObj.findAll("a", {"class": "noresultRecommend img LOGCLICKDATA"})
for i_info, i_pinfo, i_tp, i_up, i_id in zip(info, position_info, totalprice, unitprice, idinfo):
i_info=i_info.get_text().split('|') #['马甸南村','2室1厅','51.1平米','西','简装']
i_pinfo=i_pinfo.get_text().split('-')#['中楼层(共16层)1986年建塔楼','马甸']
i_pinfo[0] = re.findall(r"\d+\.?\d*", i_pinfo[0])#[['16','1986'],'马甸']
i_info[2] = re.findall(r"\d+\.?\d*",i_info[2].replace(' ',''))#['马甸南村','2室1厅',51.1,'西','简装']
python

统计导入list中,再生成数据表,保存为csv文件
house_direction = [] # 房屋朝向Direction
house_districe = [] # 房屋所在商业区Districe
house_floor = [] # 房屋楼层Floor
house_garden = [] # 房屋所在小区Garden
house_id = [] # 房屋编号Id
house_layout = [] # 房屋户型Layout
t_price = [] # 房屋总价Price
house_renovation = [] # 房屋装修Renovation
house_size = [] # 房屋面积Size
house_year = [] # 建造年份Year
if len(i_info) == 5 and len(i_pinfo) == 2 and len(i_pinfo[0])==2 and ('data-housecode'in i_id.attrs):#到了后面有的没有楼层或年份,或<a>中没有data-housecode属性
# 从houseinfo中获取房屋所在小区、户型、面积、朝向、装修、有无电梯各字段
house_garden.append(i_info[0].replace(' ',''))
house_layout.append(i_info[1].replace(' ',''))
house_size.append(i_info[2])
house_direction.append(i_info[3].replace(' ', ''))
house_renovation.append(i_info[4].replace(' ',''))
# 从positioninfo中获房屋楼层、建造年份、位置各字段
house_floor.append(i_pinfo[0][0])
house_year.append(i_pinfo[0][1])
house_districe.append(i_pinfo[1])
# 获取房屋总价和单价
t_price.append(i_tp.span.string)
#获取房屋id
house_id.append(str(i_id.attrs['data-housecode']))
# 将数据导入pandas之中生成数据表
file2=open('lianjia.csv','a+',newline='')
house_data = pd.DataFrame()
house_data['Id'] = house_id
house_data['Region'] = [chengqu[cq]] * len(house_garden)
house_data['Gargen'] = house_garden
house_data['District'] = house_districe
house_data['Layout'] = house_layout
house_data['Size'] = house_size
house_data['Direction'] = house_direction
house_data['Renocation'] = house_renovation
house_data['Floor'] = house_floor
house_data['Year'] = house_year
house_data['Price'] = t_price
# 将数据存入到csv中,便于后续分析
house_data.to_csv(file2, header=False,encoding='gb2312',index=None)
file2.close()
time.sleep(60)
python

三、最后加上全部代码~
from urllib.request import urlopen
from bs4 import BeautifulSoup
import pandas as pd
import numpy as np
import re
import time
chengqu = {'dongcheng': '东城区', 'xicheng': '西城区', 'chaoyang': '朝阳区', 'haidian': '海淀区', 'fengtai': '丰台区',
'shijingshan': '石景山区','tongzhou': '通州区', 'changping': '昌平区', 'daxing': '大兴区', 'shunyi': '顺义区',
'fangshan': '房山区'}
for cq in chengqu.keys():
url = 'https://bj.lianjia.com/ershoufang/' + cq + '/' # 组成所选城区的URL
html = urlopen(url)
bsObj = BeautifulSoup(html)
total_page = re.sub('\D', '', bsObj.find('div', 'page-box fr').contents[0].attrs['page-data'])[:-1] # 获取所选城区总页数
#print('total_page', total_page)
house_direction = [] # 房屋朝向Direction
house_districe = [] # 房屋所在商业区Districe
# house_elevator = [] # 有无电梯Elevator
house_floor = [] # 房屋楼层Floor
house_garden = [] # 房屋所在小区Garden
house_id = [] # 房屋编号Id
house_layout = [] # 房屋户型Layout
t_price = [] # 房屋总价Price
house_renovation = [] # 房屋装修Renovation
house_size = [] # 房屋面积Size
house_year = [] # 建造年份Year
for j in np.arange(1, int(total_page) + 1):
print("at the ",cq," page ",j,"/",total_page)
page_url = url + 'pg' + str(j) # 组成所选城区页面的URL
# print (page_url)
page_html = urlopen(page_url)
page_bsObj = BeautifulSoup(page_html)
info = page_bsObj.findAll("div", {"class": "houseInfo"})
position_info = page_bsObj.findAll("div", {"class": "positionInfo"})
totalprice = page_bsObj.findAll("div", {"class": "totalPrice"})
unitprice = page_bsObj.findAll("div", {"class": "unitPrice"})
idinfo = page_bsObj.findAll("a", {"class": "noresultRecommend img LOGCLICKDATA"})
for i_info, i_pinfo, i_tp, i_up, i_id in zip(info, position_info, totalprice, unitprice, idinfo):
i_info=i_info.get_text().split('|')
i_pinfo=i_pinfo.get_text().split('-')
i_pinfo[0] = re.findall(r"\d+\.?\d*", i_pinfo[0])
i_info[2] = re.findall(r"\d+\.?\d*",i_info[2].replace(' ',''))
if len(i_info) == 5 and len(i_pinfo) == 2 and len(i_pinfo[0])==2 and ('data-housecode'in i_id.attrs):
# 分列houseinfo并依次获取房屋所在小区、户型、面积、朝向、装修、有无电梯各字段
house_garden.append(i_info[0].replace(' ',''))
house_layout.append(i_info[1].replace(' ',''))
house_size.append(i_info[2])
house_direction.append(i_info[3].replace(' ', ''))
house_renovation.append(i_info[4].replace(' ',''))
#house_elevator.append(i_info[5])
# 分列positioninfo并依次获房屋楼层、建造年份、位置各字段
house_floor.append(i_pinfo[0][0])
house_year.append(i_pinfo[0][1])
house_districe.append(i_pinfo[1])
# 获取房屋总价和单价
t_price.append(i_tp.span.string)
#获取房屋id
house_id.append(str(i_id.attrs['data-housecode']))
# 将数据导入pandas之中生成数据表
file2=open('lianjia.csv','a+',newline='')
house_data = pd.DataFrame()
house_data['Id'] = house_id
house_data['Region'] = [chengqu[cq]] * len(house_garden)
house_data['Gargen'] = house_garden
house_data['District'] = house_districe
house_data['Layout'] = house_layout
house_data['Size'] = house_size
house_data['Direction'] = house_direction
house_data['Renocation'] = house_renovation
# house_data[u'有无电梯'] = house_elevator
house_data['Floor'] = house_floor
house_data['Year'] = house_year
house_data['Price'] = t_price
# print (house_data)
# 将数据存入到csv中,便于后续分析
house_data.to_csv(file2, header=False,encoding='gb2312',index=None)
#house_data.to_csv(file, header=True, encoding='gb2312', index=True)
file2.close()
time.sleep(60)
python

四、结果

由于是使用office显示的,可以改变数据格式,改变后id变为:

该字段的设置已无法满足使用需求;目前后续的操作步骤中未涉及该字段;若有实际需要获取该字段数据的情形可考虑先建立一个CSV文件;将第一列的数据格式进行调整或许这样就足够了;也可以考虑采用更为复杂的解决方案但个人经验有限在此仅作参考
五、参考博客:
就酱啦~
