赞
踩
代码链接
一、要爬取什么数据?
1、打开链家网,将目标城市设为上海。进入”二手房“板块。
2、获取二手房明细信息
每页30条二手房信息,共计100页,也就意味着本次信息抓取的数据条数最大值为3000条。
点击标题链接,需要抓取的信息如下图所示,包括标题、房屋总价、单价、所在区域、小区名称、房屋其他基本信息等。
二、获取每条二手房数据对应的url链接
1、二手房板块每页的url
上文有提到,链家二手房板块有100页,我们需要获取每个页面对应的url。
以第二页url为例:https://sh.lianjia.com/ershoufang/pg2/
发现每页的url构造有规律可循,第几页就对应着pg几。
import re
import requests
##生成需要爬取的页数的url
def generate_allurl(user_in_number):#参数user_in_number可由用户自行输入
urls_outer=[]
for url_next in range(1,user_in_number+1):
url='https://sh.lianjia.com/ershoufang/pg{}/'
url=url.format(url_next)
urls_outer.append(url)
return(urls_outer)
2、每页二手房信息明细链接
上文有提到,每页二手房信息条数为30条,对应着30个明细url链接。这个链接是我们在爬取数据时真正需要用到的最内层明细链接。
该url明细链接可以在上一步获取的每页url的框架源代码中获取。
首先使用requests.get()获取网页的源代码,然后通过正则表达式模糊匹配可以筛选出该html页面中所有的二手房url明细链接。关于requests库更多的明细信息,可以参考这篇文章requests库用法大全
#获取每一页的房源url链接汇总
def get_allurl():
urls_inner=[]
user_in_number=int(input('请输入生成页数:'))
for url in generate_allurl(user_in_number):
get_url = requests.get(url)#requests.get()方法用于获取网页源代码,返回的格式时JSON
if get_url.status_code == 200:#判断requests.get()返回的状态码是否是200,200意味着连接成功。
re_set = re.compile('https://sh.lianjia.com/ershoufang/[0-9]+\.html')#设置正则表达式的pattern
re_get = re.findall(re_set,get_url.text)#返回列表值
re_get=list(set(re_get))#去除重复项目
urls_inner.append(re_get)#返回嵌套列表
allurls=[i for k in urls_inner for i in k]#将嵌套列表展开
return(allurls)
三、使用BeautifulSoup解析html信息
接下来,就可以对每页的html进行解析,获取文章开头提到的需要获取的各类信息。
import re import requests from bs4 import BeautifulSoup import get_allurls#import自定义模块 from get_allurls import * import pandas as pd from pandas import Series,DataFrame import numpy as np from numpy import random def parse_url(): info_db=DataFrame() all_urls=get_allurls.get_allurl() for url in all_urls: info={} res=requests.get(url)# if res.status_code==200: html=res.content#响应内容 soup=BeautifulSoup(html,'html.parser') #该网站的html代码中有的标识为空,字典赋值会出现报错IndexError: list index out of range #加上try.....except 错误机制跳过空值 try: #获取标题 title=soup.select('.main')[0].text #获取房屋总价(单位万) total_price=soup.select('.total')[0].text#输出如:'775' #获取房屋单价(单位元) price=soup.select('.unitPriceValue')[0].text#输出如:'85147元/平米' #获取房屋名称及所属区 s=soup.find_all('div',attrs={'class':'fl l-txt'})[0] ##返回的s格式如下 ##<div class="fl l-txt"><a href="/">上海房产网</a><span class="stp"> > </span><a href="/ershoufang/">上海二手房</a><span class="stp"> > </span><a href="/ershoufang/jingan/">静安二手房</a><span class="stp"> > </span><a href="/ershoufang/daning/">大宁二手房</a><span class="stp"> > </span><a href="/ershoufang/c5011000010670/">上海滩大宁城(公寓)二手房</a><span class="stp"> > </span> <span>当前房源</span> </div> s=re.findall('<a.*?</a',str(s))#规整s ##返回的s格式如下 ##['<a href="/">上海房产网</a', ##'<a href="/ershoufang/">上海二手房</a', ##'<a href="/ershoufang/jingan/">静安二手房</a', ##'<a href="/ershoufang/daning/">大宁二手房</a', ##'<a href="/ershoufang/c5011000010670/">上海滩大宁城(公寓)二手房</a'] name=re.findall('>(.*?)<',s[-1])[0]#将s继续规整,得到name,'上海滩大宁城(公寓)二手房' district=re.findall('>(.*?)<',s[2])[0]#'静安二手房' info['title']=title#标题 info['name']=name#小区名称 info['district']=district#所在区 info['total_price']=total_price#房屋总价 info['price']=total_price#单价 #获取房屋基本信息 basic_info=re.findall("""房屋户型</span>.*?</li>| 所在楼层</span>.*?</li>| 建筑面积</span>.*?</li>| 户型结构</span>.*?</li>| 套内面积</span>.*?</li>| 建筑类型</span>.*?</li>| 房屋朝向</span>.*?</li>| 建筑结构</span>.*?</li>| 装修情况</span>.*?</li>| 梯户比例</span>.*?</li>| 配备电梯</span>.*?</li> """,str(soup('li')),re.VERBOSE)#re.VERBOSE参数可以将正则表达式写成多行,并且自动忽略空格,此处一定要加上该参数。 #输出的basic_info列表格式如下: #['房屋户型</span>3室2厅1厨1卫</li>', #'所在楼层</span>中楼层 (共23层)</li>', #'建筑面积</span>91.02㎡</li>', #'户型结构</span>暂无数据</li>', #'套内面积</span>暂无数据</li>', #'建筑类型</span>板楼</li>', #'房屋朝向</span>南</li>', #'建筑结构</span>钢混结构</li>', #'装修情况</span>精装</li>', #'梯户比例</span>两梯三户</li>', #'配备电梯</span>有</li>'] basic_info_list=[] for i in basic_info: i=re.findall('>(.*?)<',i)[0]#对basic_info列表中的值进行规整 basic_info_list.append(i) #规整后的basic_info_list格式如下: #['3室2厅1厨1卫', #'中楼层 (共23层)', #'91.02㎡', #'暂无数据', #'暂无数据', #'板楼', #'南', #'钢混结构', #'精装', #'两梯三户', #'有'] info['house_type']=basic_info_list[0]#房屋户型 info['floor_loc']=basic_info_list[1]#所在楼层 info['house_area']=basic_info_list[2]#建筑面积 info['house_struc']=basic_info_list[3]#户型结构 info['real_area']=basic_info_list[4]#套内面积 info['archi_cate']=basic_info_list[5]#建筑类型 info['house_toward']=basic_info_list[6]#房屋朝向 info['archi_struc']=basic_info_list[7]#建筑结构 info['deco_condition']=basic_info_list[8]#装修情况 info['elevator_rate']=basic_info_list[9]#梯户比例 info['elevator_equip']=basic_info_list[10]#配备电梯 except IndexError: continue info_db=pd.concat([info_db,DataFrame(info,index=[1])],axis=0,ignore_index=True)#将字典转为DataFrame,循环concat return(info_db)#返回DataFrame格式的数据
在爬取数据的这一步遇到了一些困难,就是每次爬取的数据条数达不到预期,我预期的数据应该是3000条左右(100页,每页30条数据),但是每次执行上述代码,爬取的数据有的时候是几百条有的时候是一千多条,都不尽人意。查阅了一些文档,意识到可能遭到了网站的反爬机制,前前后后也试了不少方法,例如使用了代理ip,在头文件中设置user-agent,增加重连接次数、关闭多余的url链接等等等,但是都没有真正解决问题。最后舍弃了上述种种应付网站反爬机制的方法,只是换了一个网速快点的网络,竟然成功了。所以,爬取数据的时候请一定要首先确保网络通畅。
虽然最终上述应付反爬的方法没有用上,不过这里也可以记录下代码,以后说不定也能用上。
#应对网站反爬
requests.adapters.DEFAULT_RETRIES=5#增加重连次数
s = requests.session()
s.keep_alive=False#keep_alive = False关闭多余连接
proxy ={'http':'211.144.213.145'}#代理ip,这个网站的免费ip亲测可用,应付练习足够了。https://www.zdaye.com/shanghai_ip.html#Free
headers = {
'Host': "sh.lianjia.com",
'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
'Accept-Encoding': "gzip, deflate, sdch",
'Accept-Language': "zh-CN,zh;q=0.8",
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36",
'Connection': "keep-alive"
}#关于为什么需要使用头文件,这篇文章可以参考https://blog.csdn.net/lujuntong/article/details/81952519?ops_request_misc=&request_id=&biz_id=102&utm_term=python%E5%8F%8D%E7%88%AC%E8%99%AB&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-81952519
s.get(url,headers=headers,proxies=proxy,timeout=10)#默认timeout为None,不设置超时时间,这里通过设置超时时间来控制访问效率,遇到超时访问直接跳过。
四、数据展示
最终获得了2978条上海地区二手房数据,可以利用这些数据进行接下来的数据分析操作。具体的数据清洗、数据规整、数据分析、数据可视化展现等后续操作,会在后续文章中接着细写。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。