【需求】
爬取青岛市二手房源信息,要求包括房源基本信息、小区名称、总价、单价以及房源的经纬度信息。
根据以上需求,进入青岛市二手房首页,如图1所示,进行页面分析:
【页面分析】
首先分析图1所示页面。可以看到青岛市二手房源共有30266条,按区分为市南、市北、李沧等10个区。同时房源列表中包含部分我们所需要的信息,但是并不完整。我们点击该房源进行详情页,如图2、图3所示。
这里有一个小技巧:首先百度一下青岛市的经纬度范围(东经119°30′~121°00′,北纬35°35′~37°09′),然后我们就可以在控制台源码中Ctrl+F搜索该范围内的经度、纬度,比如依次搜索119、120、121或35、36、37,如果该页面中包含经纬度信息则一定可以搜索到。这里我搜索到“120.”时就找到了貌似经纬度的信息,如图4所示,通过在线经纬度信息查询与该网站上所示的地理位置对比,确认了该信息就是房源对应的经纬度,然后我们可以利用正则表达式进行提取。
【亟须解决的3个问题】
上面找到了我们所需信息的页面,那每个房源详情页都对应一个URL,
- 【问题1】如何获取所有房源的URL?
回到青岛市二手房首页。爬虫必备【F12】一下,打开控制台。在控制台中,点击【选择元素】,选择标题时发现<a>标签中指向一个地址,如图5所示:
但是当我在图1所示的青岛市二手房页面中往下拉时发现房源信息以分页的形式展示,最多分为100页,每页最多30条,如图6所示:
- 【问题2】怎么解决分页的问题获取所有房源的URL?
通过观察网页二手房首页可以看到,所有二手房源可以按位置、按售价、按面积、按房型等进行分类,如图7所示:
市南:https://qd.lianjia.com/ershoufang/shinan/
市北:https://qd.lianjia.com/ershoufang/shibei/
李沧:https://qd.lianjia.com/ershoufang/licang/
…
然后发现不同区的URL是有规律的,仅仅是后面拼音不一样。
这样就可以通过不同区进行爬取。为了最大化的获取数据,同时要求每个区的房源数也不能超过3000条,如果超过3000条,则可以在按区分类的同时,再按售价(或面积、房型等)进行更细的分类,以此类推。这里由于市南、市北等几个区的二手房源超过3000,所以在按区的同时还需要再按售价进行分类爬取(因此总共应分为80类,即10个区,每个区对应8类售价)。
我们再点击不同的售价类型:
市南&80万以下 https://qd.lianjia.com/ershoufang/shinan/p1/
市南&80-100万万以下:https://qd.lianjia.com/ershoufang/shinan/p2/
市南&100-150万万以下:https://qd.lianjia.com/ershoufang/shinan/p3/
…
市北&80万以下:https://qd.lianjia.com/ershoufang/shibei/p1/
市北&80-100万万以下:https://qd.lianjia.com/ershoufang/shibei /p2/
市北&100-150万万以下:https://qd.lianjia.com/ershoufang/shibei /p3/
…
这里发现同时按区和售价分类的URL也存在一定规律,即在按区分类的基础上添加页码p1表示第1也,p2表示第2页…….,因此同时按区和售价爬取是一个不错的方法。那么【问题2】也就得到了解决。
有了获取某一页某一房源详情页URL的方法和获取同时按区和售价分类后的URL,就可以从每个分类后的URL中的每一页中依次获取所有房源详情页URL,但是这里需要解决每个分类后的URL中的分页问题。
- 【问题3】怎么解决分类后的分页的问题而获取所有房源的URL?
下面我们同时选择市南区和100-150万,页面列出了339条数据,被分为12页。
然后我们一直单击下一页,观察URL的变化:
第1页:https://qd.lianjia.com/ershoufang/shinan/p3/
第2页:https://qd.lianjia.com/ershoufang/shinan/pg2p3/
第3页:https://qd.lianjia.com/ershoufang/shinan/pg3p3/
…
这里我们发现同样存在规律,URL中p3表示第3类售价,即100-150万,除了第一页, pg2、pg3表示页码,通过这个规律我们就可以通过代码构造不同区不同售价不同页码的URL。那么【问题3】也就得到了解决。
综上分析我们可以构造不同区不同售价不同页码上的所有房源详情页的URL,然后通过请求并解析该URL获取我们所需爬取的信息。
【撸起袖子开始干】
爬虫框架:这里我采用自己比较熟悉的Selenium进行爬取,使用lxml库进行Html解析,另外需要chromedriver.exe自动化加载页面。
需要的第三方python库:selenium、lxml、xlrd、xlwt
另外下载chromedriver.exe时要求chromedriver与所使用的谷歌浏览器版本对应,其对应关系及下载地址参考:Chrome与ChromeDriver版本对应参照表及下载地址
【爬取五部曲】
下面直接贴代码,代码不难,具体自己看
【一步曲:获取不同区的URL】
def get_range_url():
#声明list用于存储不同区的url
range_urls={
}
driver=get_chromedriver()
driver.get(city_url)
html = driver.page_source
selector = etree.HTML(html)
#获取青岛不同区对应的URL
for i in range(0,10):#青岛市分为10个区
a_href=str(selector.xpath('//*[@class="position"]/dl[2]/dd/div[1]/div/a['+str(i+1)+']/@href')[0])#标签索引从1开始
rangename=str(selector.xpath('//*[@class="position"]/dl[2]/dd/div[1]/div/a['+str(i+1)+']/text()')[0])
range_url='https://qd.lianjia.com'+a_href
range_urls[rangename]=range_url
return range_urls
【二步曲:获取不同区不同售价的URL】
#根据已获取的不同区的URL,获取不同区不同售价的URL
def get_range_price_url():
#获取不同区的URL
range_urls=get_range_url()
#声明字典用于存储不同区不同售价的URL,key为每个区名,value为该区对应的8个售价的url
range_price_urls={
}
for key in range_urls:
range_url=range_urls[key]
part_range_price_url=[]
for i in range(1,9):#按售价分为8个区间,依次在按区的URL后增加p1/,p2/,...p8/
range_price_url=range_url+'p'+str(i)+'/'
part_range_price_url.append(range_price_url)
range_price_urls[key]=part_range_price_url
return range_price_urls
【三步曲:获取不同区不同售价不同页的URL】
#根据已获取的不同区不同售价的URL,获取不同区不同售价不同页的URL,并将url写入excel中
def get_range_price_page_url():
#获取不同区不同售价的URL列表集合
range_price_urls=get_range_price_url()
#声明字典用于存储不同区不同售价不同页的url,key为区名, value为每个区对应的不同售价不同页的url的list集合
range_price_page_urls={
}
#创建workbook(即excel文件)
workbook = xlwt.Workbook(encoding='utf-8')
#创建g工作簿(即表)
worksheet = workbook.add_sheet('Sheet1')
count=0
for key in range_price_urls:
try:
driver = get_chromedriver()
except Exception:
driver = get_chromedriver()
part_range_price_urls=range_price_urls[key]
for part_range_price_url in part_range_price_urls:
#请求URL获取该页面中房源的个数,从而计算有多少页
try:
driver.get(part_range_price_url)
except Exception:
driver.get(part_range_price_url)
#等待2秒,以便浏览器加载所有资源,受网速影响如果网站需要加载资源较多可能加载不完全
time.sleep(2)
html = driver.page_source
selector = etree.HTML(html)
#根据不同区不同售价url页面中的总房源数(每页30天,最后一页不足30条)计算有多少页
house_count=selector.xpath('//*[@class="resultDes clear"]/h2/span/text()')
yushu = int(house_count[0]) % 30
page_count = 0
if (yushu is 0):
page_count = int(int(house_count[0]) / 30)
else:
page_count = int(int(house_count[0]) / 30) + 1
#part_range_price_page_urls=[]
#构造不同区不同售价不同页码的URL
for i in range(0,page_count):
range_price_page_url=''
if (i is 0):
range_price_page_url=part_range_price_url
else:
list_ss = part_range_price_url.split('/')
url_priceId = list_ss[5]
range_price_page_url = part_range_price_url.replace(url_priceId, 'pg' + str(i + 1) + url_priceId)
#part_range_price_page_urls.append(range_price_page_url)
#将每页的URL写如excel文件中
worksheet.write(count