用python的requests模块爬取上海地区链家二手房数据

代码链接
一、要爬取什么数据?
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"> &gt; </span><a href="/ershoufang/">上海二手房</a><span class="stp"> &gt; </span><a href="/ershoufang/jingan/">静安二手房</a><span class="stp"> &gt; </span><a href="/ershoufang/daning/">大宁二手房</a><span class="stp"> &gt; </span><a href="/ershoufang/c5011000010670/">上海滩大宁城(公寓)二手房</a><span class="stp"> &gt; </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条上海地区二手房数据,可以利用这些数据进行接下来的数据分析操作。具体的数据清洗、数据规整、数据分析、数据可视化展现等后续操作,会在后续文章中接着细写。
在这里插入图片描述

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值