当前位置:   article > 正文

爬取贝壳和房天下——北京二手房在售和已成交百万级别数据(附详细思路和部分源码)_贝壳最新成交数据需要登陆

贝壳最新成交数据需要登陆

前言

在港校做RA期间,本周老师安排了爬取国内二手房的交易网站,我先调研对比了各平台,之后选取了房天下和贝壳的数据进行爬取

贝壳

房天下

贝壳需要注册登录才能访问,房天下不需要

因课题保密的要求,数据和完整代码无法分享,如果有需要的朋友或老师可以联系我个人微信(liguopingHNU)进行定制化爬取数据


提示:以下是本篇文章正文内容,思路和代码可供参考

一:前期调研

1.1 各二手房平台详细对比

1.1.1链家

网址:

贝壳

在售数据

在这里插入图片描述

成交:

在这里插入图片描述

列表页:

在这里插入图片描述

详情页:

例如:https://bj.lianjia.com/ershoufang/101108567289.html

分为:三个部分

  • 图片展示

在这里插入图片描述

  • 基本信息

在这里插入图片描述

  • 房源特色

在这里插入图片描述

1.1.2安居客

网址:

安居客

列表页:

在这里插入图片描述

缺点:

  • 没有在售和已成交的区分
  • 没有直观显示有多少房源
  • 网站维护有问题,我在登陆时都没法登陆,难以保障数据的实时更新
详情页:

以此为例:

https://beijing.anjuke.com/prop/view/A5183579374?from=filter&spread=commsearch_p&uniqid=pc5f27c655bde998.84281277&position=1&kwtype=filter&now_time=1596442197

分为:两个部分

  • 图片展示

在这里插入图片描述

  • 房屋信息

在这里插入图片描述

1.1.3 房天下

网址:

[房天下])https://esf.fang.com/)

列表页:

上面显示有16万套待售,但真实性存疑,我查阅此软件评价较差,存在数据造假的问题

本网站也没有已成交的房源信息,不够全面

在这里插入图片描述

详情页:

感觉对房源的介绍极为敷衍随意,不如其他平台的仔细介绍,真实感较差

在这里插入图片描述

1.1.4贝壳网

网址:

贝壳网

在售:

在这里插入图片描述

已成交:

在这里插入图片描述

详情页:

贝壳的二手房详情更为丰富,网页主体分为四个部分,:

  • 图片展示区

在这里插入图片描述

  • 房源基本信息

在这里插入图片描述

  • 房源特色:

在这里插入图片描述

  • 户型空间

在这里插入图片描述

1.2 综合评价

贝壳网和链家是多名网友较为推崇的两个平台,经过查询,两个平台的数据相差不大,且都有在售和已成交的数据

而其他平台,网友评价较低,主要原因为数据造假,在本次查询中也出现此问题

链家和贝壳在房源数据方面,展示极为清晰,方便我们获知总成交数量,在详情页的介绍也明显比其他两个平台更加翔实,真实性,可靠性更高

1.3 最后需求确定

和老师进一步讨论之后,我们最后选择了贝壳和房天下,爬取北京市 近两年的二手房的在售和已成交

本次需求是先将新闻的概览信息保存到EXCEL文件,最后格式如下

   |标题|时间|报社|版面号|版面信息|文章描述|正文url|
  • 1

在这里插入图片描述
因课题保密的要求,数据和完整代码无法分享,如果有需要的朋友或老师可以联系我个人微信(liguopingHNU)进行定制化爬取数据

在保存上述简要信息的时候,我便将正文的url保存进txt文件,方便后续直接读写txt进行正文爬取

这种设计模式我觉得是做到了不同模块的解耦,如果有朋友有更好的想法也可以和我交流

最后正文保存格式如下:
按照 关键词-报道报社 - 新闻标题-正文 保存
在这里插入图片描述
因课题保密的要求,数据和完整代码无法分享,如果有需要的朋友或老师可以联系我个人微信(liguopingHNU)进行定制化爬取数据

二:网页结构的分析

2.1 网站首页

目标的url 为 :

http://www.apabi.com/pku/?pid=usp.advsearch&db=newspaper&cult=CN

除了pku 是不同机构或高校会改变这个字段,在检索查看的其他过程中,本url不变,完全使用Ajax的异步加载

给我们的自动化翻页,匹配信息,查询都带来了极大的挑战

在这里插入图片描述

我使用了request进行测试,发现没有办法获取到返回的信息,页面总是不相符,测试了好久,一直没有得到正确的内容

一筹莫展之际,我只能使用selenium就行匹配(但这也给后续的爬去埋下了一个巨大的坑)

查看网页源代码发现,请求之后,返回的是iframe,直接更新下面的搜索结果,我们需要的信息是在iframe里面去进行匹配,如果直接匹配页面的话,是匹配不到的

Selenium的好处是所见即所得,但是因为是模仿人的点击,速度极慢,而且书写代码时非常繁杂,点击选项时可能就是网页滑动了一下,便导致定位错误,无法匹配

2.2 使用selenium访问

以下是模拟点击页面的代码,可以看到匹配元素时非常麻烦

driver_path = r"G:\Daily_Life\Google_driver\chromedriver.exe"

def config(head_keyword,con_keyword,office_key):
    url = "http://www.apabi.com/pku/?pid=usp.advsearch&db=newspaper&cult=CN"
    
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    
    try:
        driver = webdriver.Chrome(executable_path=driver_path,chrome_options=chrome_options)

        driver.get(url)
        time.sleep(1)
        
        webdriver.common.action_chains.ActionChains(driver)
        
        #保存详细信息结果
        rows=[]
        #保存文章
        arts = []
        
        #报纸标题输入框
        head_key = driver.find_element_by_xpath('.//div[@class="adh1"]/input')
        head_key.send_keys(head_keyword)
        
        #报纸内容关键词输入 con_keyword
        con_key = driver.find_element_by_xpath('.//div[@class="adh1"][2]/input')
        con_key.send_keys(con_keyword)
        
        time.sleep(1)
        
        #报社关键词
        paper_office = driver.find_element_by_xpath('.//div[@class="adh1"][4]/input')
        paper_office.send_keys(office_key)
        
        time.sleep(1)
        
        #选择时间按钮
        time_bt = driver.find_element_by_xpath('.//ul[@class="rq_one"][2]')
        time_btn =time_bt.find_element_by_tag_name('input')
        time_btn.click()
        
        #时间范围(开始时间和结束时间)
        select_pre_time = driver.find_element_by_xpath('.//ul[@class="rq_one"][2]/li[2]/span')                                           
        pre_time = select_pre_time.find_element_by_tag_name('input')
        pre_time.send_keys(p_time)
        
        time.sleep(1)
        
        select_end_time = driver.find_element_by_xpath('.//ul[@class="rq_one"][2]/li[4]/span')                                           
        end_time = select_end_time.find_element_by_tag_name('input')
        end_time.send_keys(e_time)
        
        #时间顺序排列
        time_sort = driver.find_element_by_id('raddateasc')
        time_sort.click()
        
        #点击检索
        sou = driver.find_element_by_id('btmSch')
        sou.click()
        
        time.sleep(1)
        
        # 切换到iframe中
        # 获取 iframe 对象
        iframe_elemnt = driver.find_element_by_id("ifrResult")
        driver.switch_to.frame(iframe_elemnt)
        
        time.sleep(1)
        
        flag = True;
        IsOne = False
        
        #print('flag1:',flag)
        #点击下一页
        #在这里可以使用class搜索,因为没有上一页干扰,但之后,有了上一页干扰
        try:
            #没有找到,代表搜索结果只有一页
            next_page = driver.find_element_by_xpath('.//a[@class = "pageImp"]')
        except Exception:
            IsOne = True
            print('当前搜索结果只有一页')
            print('\n')
        
        if(IsOne):
            #如果只有一页
            articles =driver.find_elements_by_xpath('.//div[@class="search_list_content m2"]')
            
            for article  in articles:
                row={}
                art={}
                
                #取到a标签
                a = article.find_element_by_class_name('title')
                title = a.text
                row['title'] = title
                #网址                
                c_url = a.get_attribute('href')
                art['title']  = title
                art['url']= c_url
                arts.append(art)
                
                c = article.find_element_by_class_name('comment').text
                
                JianGeFu ='/'
                #结构如   : 常州晚报 / 2016 -08 -15 / A08版 / 版面城事 微交通
                #查找了所有/ 的位置
                list=[i.start() for i in re.finditer('/', c)]
                
                if(len(list)>=3):
                    #找到的每一个/ 的位置   
                    l1 = list[0]
                    l2 = list[1]
                    l3 = list[2]
                    c_comment = c[0:l1-5]
                    c_office = c[l1-5:l1]
                    c_time = c[l1+1:l2]
                    c_ban_num = c[l2+1:l3]
                    c_ban_name = c[l3+1:]
                else:
                    c_comment = c[:-30]
                    c_office = c[-29:25]
                    c_time = c[-24:-8]
                    c_ban_num = c[-7:-5]
                    c_ban_name = c[-3:]
                    print(title,'版面号有误')
                        
                row['comment'] =c_comment
                row['office'] =c_office
                row['time'] =c_time
                row['ban_num'] =c_ban_num
                row['ban_name'] =c_ban_name
                rows.append(row)
            name = con_keyword+office_key
            pyexcel.save_as(records= rows,dest_file_name='%s.xls'%name)
            print('--------------------------------------------')
            print(name,'文章列表页的详细信息全部保存完毕')
            print('--------------------------------------------')
            #保存正文
            for art in arts :
                filename= con_keyword+office_key+'url.txt'
                with open(filename, 'a', encoding='utf-8') as f:
                    f.write(art['url']+'\n')
            f.close()
            
            print('--------------------------------------------')
            print(name,'文章的全文url全部保存完毕')
            print('--------------------------------------------')
            time.sleep(1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150

在获取到返回信息之后,使用selenium就是不断的点击下一页,如果不存在下一页的按钮,那么不再翻页,开始保存。

对每一页的内容,有15篇报道,我发现他都是放在一个块里面的,可以通过遍历整个块来,获取每一篇的文章报道,并将它存到字典里面,最后将字典保存进数组,爬取完毕之后,打开excel将数组的写入

row={}
art={}
 #标题
row['title'] = title
row['comment'] =c_comment
row['office'] =c_office
row['time'] =c_time
row['ban_num'] =c_ban_num
row['ban_name'] =c_ban_name
rows.append(row)

name = "我们赋予的文件名"
pyexcel.save_as(records= rows,dest_file_name='%s.xls'%name)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

对于大规模的爬虫访问,我使用VPN测试了一下,发现差不多访问到2000次就会被block,这和我们要求的爬取量级远远不符,

使用selenium爬取太容易被墙,出现“检测到您的访问太过频繁的警告”

常用的办法是使用代理IP,但是中华书苑会检测访问者的IP,如果不在IP范围之内(不属于高校),是无法正常访问的

解决不了被墙的问题,就先解决爬取速度吧

2.3 多线程加快爬取速度

使用了多线程技术加快爬取速度,加上无头模式速度快了一点,但还是不能达到我们想要的速度

当时对于网络抓包分析这一块还不是很熟练的情况下,使用的又是非常坑爹的d(ŐдŐ๑)selenium,结果可想而知,只能看着它不断的把浏览器打开,然后关闭

为了快速爬取,后期只能找了十几个北大账号,加快爬取,往往是一个掉线之后,另一个立刻挂上

多线程的优点完全没有被体现出来

就这样爬了十多万篇之后,实在忍受不了,开始另谋他法

2.4 注册bug

突然发现这个中华书院有一个注册的功能,在第一次登录之后,可以通过注册他的账号访问,不用特定的IP登录

刚发现这个的我欣喜不已,一晚上爬了四五万篇

但是这个bug很快被对方运维人员发现,将这个规则填补,直接把注册功能取消或无法进行注册

2.5 使用request带cookies访问

又一次山穷水尽之后

项目搁置了一个月

但师姐一直在催促,期间我还去旁边高校使用校园网(校园范围内IP随机变化),这种办法取爬取了一些,但是总是治标不治本

最后认认真真分析了整个网络协议流程和抓包的请求通信之后

我发现他返回的数据虽然庞杂,但是有三条比较核心,

  • 整个iframe的内容是通过一个包来返回的,可以理解为我们通过这个url去访问之后,通过拼接请求字段,重定向到了另一个URL,然后从这里面去获取报刊的数据,放入iframe
  • 使用request带cookies加多线程访问之后,速度提升了很多,至少不用熬夜换vpn了,核心代码如下

headers = {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Cache-Control': 'max-age=0',
            'Connection': 'keep-alive',
            'Content-Length': '0',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Cookie': '__cfduid=dac2c0020a2e072b6ba4fde90f45d91f81602924475; Hm_lvt_07cfb20d5f1d7402d600e55de80e9127=1602935685,1603206348,1603702503,1603727123; Hm_lvt_eaa57ca47dacb4ad4f5a257001a3457c=1602935697,1603206359,1603702506,1603727128; .ASPXANONYMOUS=_q9kIBfb1gEkAAAAMTY5ZDY0N2UtYmNhNi00NmViLTk0NzEtN2M4Yjg1MWFmNTI5IjVThVwuTPrCeki-rIt5Gstg2HE1; ASP.NET_SessionId=dl3ngom53i0mqvkd4fc0bnsw; ups_token=aXA6MTA2LjEyMC4xMjQuNDYsdW46LGVtYWlsOixvcmdpZDpwa3UsZW5kdGltZToyMDIwLTEwLTI3IDAxOjQ1OjE1LHRvcGljczo=; USPLOGINUSERCOOKIENAME=42DCF6A99B346AF43C59627FCE52E595; Hm_lpvt_07cfb20d5f1d7402d600e55de80e9127=1603727123; Hm_lpvt_eaa57ca47dacb4ad4f5a257001a3457c=1603727128',
            'Host': 'www.apabi.com',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': user,
            }
        
        while self.url_queue.empty() == False:
            #print('开始爬取,当前队列是否为空',self.url_queue.empty())
            url = self.url_queue.get()
            response = requests.get(url, headers=headers)
            e = etree.HTML(response.text)
            time.sleep(0.3)
            try: 
                title = e.xpath('.//h2[@class ="bo"]/text()')
                title = str(title)
                title = title.replace("['","").replace("']","").replace("\\r\\n","").replace("\\r","").replace(u'\\u3000',u'').replace(u'?',u'').replace(u' ',u'') 
                #print(title)
                s_time = e.xpath('.//li[@class="daxiao"]/text()')
                s_time=''.join(s_time) 
                
                ban_author = s_time.replace("\r\n","").replace(" ","")
                
                si_time = str(re.findall(r'(\d{4}-\d{1,2}-\d{1,2})',s_time))
                si_time = si_time.replace("['","").replace("']","")
                content = e.xpath('.//li[@id="zoom"]/p/text()')
                content = str(content)
                content = content.replace("['","").replace("']","").replace(u'\\u3000',u'').replace(u'\xa0', u'')
            except Exception:
                si_time = ''
                ban_author='无'
                content = '无'
                title = '无'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

因课题保密的要求,数据和完整代码无法分享,如果有需要的朋友或老师可以联系我个人微信(liguopingHNU)进行定制化爬取数据

三.问题记录

1.iframe
2.性能优化

很久之后老师聊了聊之后,建议可以使用异步的高性能网络请求包和高性能的线程,代替现在的request和Thread,但是本爬虫的ip检测阻止了我提升代码性能的欲望

严禁使用本代码恶意爬取数据库,造成严重后果自负

因课题保密的要求,数据和代码无法分享,如果有需要的朋友或老师可以联系我个人微信(liguopingHNU)进行定制化爬取数据

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/192502
推荐阅读
相关标签
  

闽ICP备14008679号