基于python多线程和Scrapy爬取链家网房价成交信息


本次教程以深圳市为例,介绍利用python多线程爬取链家网的房价成交信息。其中涉及的知识包括爬虫框架Scrapy-spider、轻量数据库SQLite等。

知识背景

Scrapy- spider 爬虫框架

简单的说,Scrapy爬虫框架会利用异步机制帮助提高网络爬虫的爬取速度。
最重要的,爬虫记载爬取失败的url,进行重复爬取,直到爬取失败的url列表为空时结束爬取,这显然提高数据的爬取质量。

SQLite数据库

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它占用资源非常的低,操作简单,适合用于存储爬虫结果数据。

python多线程

多线程就像同时执行多个程序,多线程运行有如下优点:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 运行速度更快
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等情况下我们可以释放一些珍贵的资源如内存占用等等。

爬取流程详解

爬取房价信息
封装数据库类,方便多线程操作
class SQLiteWraper(object):
    """
    数据库的一个小封装,更好的处理多线程写入
    """
    def __init__(self,path,command='',*args,**kwargs):  
        self.lock = threading.RLock() #锁  
        self.path = path #数据库连接参数  
        
        if command!='':
            conn=self.get_conn()
            cu=conn.cursor()
            cu.execute(command)
    
    def get_conn(self):  
        conn = sqlite3.connect(self.path)#,check_same_thread=False)  
        conn.text_factory=str
        return conn   
      
    def conn_close(self,conn=None):  
        conn.close()  
    
    def conn_trans(func):  
        def connection(self,*args,**kwargs):  
            self.lock.acquire()  
            conn = self.get_conn()  
            kwargs['conn'] = conn  
            rs = func(self,*args,**kwargs)  
            self.conn_close(conn)  
            self.lock.release()  
            return rs  
        return connection  
    
    @conn_trans    
    def execute(self,command,method_flag=0,conn=None):  
        cu = conn.cursor()
        try:
            if not method_flag:
                cu.execute(command)
            else:
                cu.execute(command[0],command[1])
            conn.commit()
        except sqlite3.IntegrityError as e:
            #print(e)
            return -1
        except Exception as e:
            print(e)
            return -2
        return 0
    
    @conn_trans
    def fetchall(self,command="select name from xiaoqu",conn=None):
        cu=conn.cursor()
        lists=[]
        try:
            cu.execute(command)
            lists=cu.fetchall()
        except Exception as e:
            print(e)
            pass
        return lists
数据库插入操作

利用数组生成数据库插入命令

def gen_xiaoqu_insert_command(info_dict):
    """
    生成小区数据库插入命令
    """
    info_list=[u'小区ID',u'小区名称',u'参考均价',u'大区域',u'小区域',u'建筑年代',u'建筑类型',u'物业费用',u'物业公司',u'开发商',u'楼栋总数',u'房屋总数',u'附近门店']
    t=[]
    for il in info_list:
        if il in info_dict:
            t.append(info_dict[il])
        else:
            t.append('')
    t=tuple(t)
    command=(r"insert into xiaoqu values(?,?,?,?,?,?,?,?,?,?,?,?,?)",t)
    return command
构建爬虫爬取数据
def xiaoqu_spider(db_xq,url_page=u"http://sz.lianjia.com/xiaoqu/pg1rs%E6%98%8C%E5%B9%B3/"):
    """
    爬取页面链接中的小区信息
    """
    try:
        res=requests.get(url_page,headers=hds[random.randint(0,len(hds)-1)])
        plain_text=res.text
        soup = BeautifulSoup(plain_text,"html.parser")
    except (urllib.request.HTTPError, urllib.request.URLError) as e:
        print(e)
        exit(-1)
    except Exception as e:
        print(e)
        exit(-1)
        
    xiaoqu_list=soup.findAll('li',{'class':'clear xiaoquListItem'})
    for xq in xiaoqu_list:
        info_dict={}
        info_dict.update({u'小区ID':xq.get('data-id')})
        info_dict.update({u'小区名称':xq.find('div',{'class':'title'}).get_text()})
        info_dict.update({u'参考均价':xq.find('div',{'class':'totalPrice'}).get_text()})
        info_dict.update({u'大区域':xq.find('a',{'class':'district'}).get_text()})
        info_dict.update({u'小区域':xq.find('a',{'class':'bizcircle'}).get_text()})
        url_detail="https://sz.lianjia.com/xiaoqu/"+xq.get('data-id')+"/"
        info=xiaoqu_spider_detail(url_detail)
        try:
            info_dict.update({u'建筑年代':info[0]})
            info_dict.update({u'建筑类型':info[1]})
            info_dict.update({u'物业费用':info[2]})
            info_dict.update({u'物业公司':info[3]})
            info_dict.update({u'开发商':info[4]})
            info_dict.update({u'楼栋总数':info[5]})
            info_dict.update({u'房屋总数':info[6]})
            info_dict.update({u'附近门店':info[7]})
        except Exception as e:
            print(url_detail)
            print(e)
        command=gen_xiaoqu_insert_command(info_dict)
        db_xq.execute(command,1)
        
def do_xiaoqu_spider(db_xq,region=u"futianqu"):
    """
    爬取大区域中的所有小区信息
    """
    url=u"http://sz.lianjia.com/xiaoqu/"+quote(region_pinyin)+"/"
    try:
        res=requests.get(url,headers=hds[random.randint(0,len(hds)-1)])
        plain_text=res.text
        soup = BeautifulSoup(plain_text,"html.parser")
    except (urllib.request.HTTPError, urllib.request.URLError) as e:
        print(e)
        return
    except Exception as e:
        print(e)
        return
    d=json.loads(soup.find('div',{'class':'page-box house-lst-page-box'}).get('page-data'))
    total_pages=d['totalPage']
    threads=[]
    for i in range(total_pages):
        url_page=u"http://sz.lianjia.com/xiaoqu/"+quote(region_pinyin)+"/"+"pg%s/" % (i+1)
        t=threading.Thread(target=xiaoqu_spider,args=(db_xq,url_page))
        threads.append(t)
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    print(u"爬下了 %s 区全部的小区信息" % region)
基于百度地图获取小区地理坐标

构建函数调用百度地图API,查询poi点位置信息,获取位置标记

def getlocation(name):#调用百度API查询位置
    bdurl='http://api.map.baidu.com/place/v2/search?query='
    output='json'
    ak='yPNB0qKB6sIVVupOWyKnzoxabwsSuK9M'#输入你刚才申请的密匙
    region='深圳'
    tag='房地产'
    uri=bdurl+name+'&tag='+tag+'&region='+region+'&output='+output+'&ak='+ak
    res=requests.get(uri)
    s=json.loads(res.text)['results']
    if s==[]:
        loc={'lng':'NULL','lat':'NULL'}
    else:
        s=json.loads(res.text)['results'][0]
        loc=s.get('location', 'not exist')
        if loc=="not exist":
            loc={'lng':'NULL','lat':'NULL'}
    return(loc)

完整代码

完整代码请参考个人博客-资源下载。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Taro.js是一种支持多个小程序平台开发的框架,它提供了一种简洁、高效的方式来创建微信小程序。在Taro.js中,我们可以使用城市选择列表组件来方便地实现城市选择功能。 微信小程序城市选择列表是指用户可以在小程序中选择所在城市的功能。使用Taro.js开发微信小程序时,我们可以通过调用微信小程序提供的定位功能,获取用户所在位置的经纬度信息。然后,我们可以将经纬度信息传递给城市选择列表组件,让用户选择所在城市。 城市选择列表通常包括省份和城市两级下拉菜单。当用户选择省份时,城市列表会动态更新为该省份对应的城市列表。用户可以通过滚动或搜索的方式,在城市列表中选择目标城市。用户选择完成后,我们可以将所选择的城市信息传递给后端服务器,进行进一步的处理。 在Taro.js中,我们可以使用类似以下的代码来实现城市选择列表: ``` import { View, Picker } from '@tarojs/components' const cities = { 北京: ['北京市'], 上海: ['上海市'], 广东: ['广州市', '深圳市', '珠海市', '东莞市'], ... } export default class CitySelector extends Component { state = { province: '', city: '', } handleProvinceChange = e => { const province = e.detail.value const city = cities[province][0] this.setState({ province, city }) } handleCityChange = e => { const city = e.detail.value this.setState({ city }) } render() { const { province, city } = this.state return ( <View> <Picker mode='selector' range={Object.keys(cities)} onChange={this.handleProvinceChange}> <View>{province || '请选择省份'}</View> </Picker> <Picker mode='selector' range={cities[province]} onChange={this.handleCityChange}> <View>{city || '请选择城市'}</View> </Picker> </View> ) } } ``` 以上代码中,我们定义了一个城市选择列表组件`CitySelector`,其中使用了`Picker`组件来实现下拉选择功能。`cities`对象存储了各省份对应的城市列表。当用户选择省份时,触发`handleProvinceChange`函数,在城市列表中显示对应省份的城市。当用户选择城市时,触发`handleCityChange`函数,更新城市信息。最终,用户选择的省份和城市会保存在`state`中,方便后续的数据处理和传递。 通过这样的方式,我们可以在Taro.js微信小程序中实现城市选择列表的功能。希望这个回答能对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值