• (项目)爬取安居客二手房房屋信息


    目标

    1 打开安居客二手房页面,如 https://nanning.anjuke.com/sale/?from=navigation 。得到如下页面。

       通过分析发现,每个主页有60个二手房信息。一共有50个主页(一般类似网站都只提供50个主页)。

    2 打开其中一个二手房的信息后,跳转到如下页面。我们的目标是要得到下图所示框起来的“房屋信息”的内容。

      也就是我们需要爬取 50 * 60 = 3000 个“房屋信息”

    思路

    1 获取50个主页源码。

      如果使用本机ip进行reques请求主页源码后,安居客的反爬机制会检测出我们的请求,提示如下页面。为了解决这个问题,应该使用代理IP代替本机ip。

      本次使用蘑菇隧道代理IP,并且对官方接口进行了部分修改。在定义的接口函数中,使用while循环和 try : ... except : ... 的目的是为了能够在请求url失败的时候重新进行请求。使用 if 语句来判断请求的次数是否超过8次,如果超过8次那么就退出请求。请求安居客的页面时,有时候虽然请求失败了(即请求到的内容不是我们需要的内容),但是程序也不会报错,而是返回一个无效的页面,这个无效的页面的字符串长度小于1000。因此可以使用 if 语句来判断请求到的内容的字符串长度是否大于1000,如果大于1000就说明获取到的内容是我们所需要的。注意:当蘑菇隧道代理的代理IP每秒请求(并发)为1时,如果请求失败,需要重新请求,需要在重新请求前延时等待一秒。即添加 time.sleep(1) 

    2 获取主页源码后,使用xpath抓取每个主页的60个二手房的跳转链接。如下图所示。

    3 使用requests请求获取到的跳转链接。 

    4 使用xpath对跳转链接的源码获取房屋信息的内容。

       获取到详细页的源码后,通过观察发现,房屋信息的每一个小信息(包括字段和对应内容)都存放在一个<li>标签里。这个小信息的字段是<li>标签里的第一个<div>标签的文本,字段对应的内容是<li>标签里的第二个<div>标签的文本。

       

       我们需要成对地将一个小信息的字段和内容爬取出来,做法是:

       ①将小信息的标签用xpath定位。

       我第一次在对应代码位置通过点击右键的Copy的Cope XPath定位的路径,得到定位的路径 " //*[@id="content"]/div[3]/div[1]/div[3]/div/div[1]/ul/li[1] ",这个路径是通过id来定位的,但是使用xpath查找这个路径时,获取不到结果。因此后面我改为使用class定位就能获取到结果,即" //*//li[@class="houseInfo-detail-item"] "。总结:xpath有时候如果使用id定位不到,建议换其他属性定位。

       因为每一个小信息的class属性都是一样的,所以获取到一个列表,这个列表的元素是每一个小信息。

       ②对获取到的列表进行遍历,每次遍历的结果是一个小信息。对这个小信息用xpath方法获取第一个div文本(字段)和第二个div文本(对应的内容)。

       ③对得到的字段和对应的内容进行数据的清洗。然后以键值对的形式添加到一个空字典。

    5 通过多个详情页观察发现,存在一些详情页的房屋信息的小信息个数不同。详情页的小信息最多有18个。有的详情页小信息个数少于18个。针对这个问题,解决的方法是:

       创建一个列表,这个列表有18个字段。对这个列表进行遍历,使用 not in 方法判断每次遍历出的字段是否在字典的键里,如果不在,那么就以键值对的方式给字典添加这个不在的字段和对应的内容 '暂无'

     1 import requests
     2 from lxml import etree
     3 import re
     4 
     5 # 该函数请求网页时使用的代理IP是蘑菇隧道代理,该函数用于请求网页源代码。
     6 def mogu_suidaodaili(url,appKey):
     7     # 蘑菇隧道代理服务器地址
     8     ip_port = 'secondtransfer.moguproxy.com:9001'
     9     proxy = {"http": "http://" + ip_port, "https": "https://" + ip_port}
    10     headers = {
    11         "Proxy-Authorization": 'Basic ' + appKey,
    12         "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0",
    13         "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4"}
    14     num = 0
    15     while 1:
    16         if num != 8:
    17             try:
    18                 r = requests.get(url=url, headers=headers, proxies=proxy, verify=False, allow_redirects=False)
    19                 if r.status_code == 302 or r.status_code == 301:
    20                     loc = r.headers['Location']
    21                     print(loc)
    22                     url_f = loc
    23                     r = requests.get(url_f, headers=headers, proxies=proxy, verify=False, allow_redirects=False)
    24                     return r.content.decode('utf-8')
    25                 if len(r.content.decode('utf-8')) > 1000 :
    26                     return r.content.decode('utf-8')
    27                 else:
    28                     num = num + 1
    29                     print('当前代理ip请求失败,正在进行第' + str(num) + '次重新请求')
    30             except:
    31                 num = num + 1
    32                 print('当前代理ip请求失败,正在进行第'+str(num)+'次重新请求')
    33         else:
    34             return '请求8次失败,退出请求'
    35 
    36 def get_jump_url(home_page):
    37     etree_object = etree.HTML(home_page)
    38     jump_url = etree_object.xpath('//*[@id="houselist-mod-new"]/li/div[2]/div[1]/a/@href')
    39     return jump_url
    40 
    41 def get_information(detail_page):
    42     etree_object = etree.HTML(detail_page)
    43     # 因为我使用在对应代码位置通过点击右键的Copy的Cope XPath定位的路径,即//*[@id="content"]/div[3]/div[1]/div[3]/div/div[1]/ul/li[1]。这个路径是通过id来定位的,但是这个路径获取不到内容,所以改为使用class属性定位。有时候如果使用id定位不到,建议换其他属性定位。
    44     informations = etree_object.xpath('//*//li[@class="houseInfo-detail-item"]')
    45 
    46     dict = {}
    47     for information in informations:
    48         ziduan = information.xpath('div[1]//text()')[0]
    49         content = information.xpath('div[2]//text()')
    50         # str.strip(str) 这个方法移除字符串头尾指定的字符或字符序列
    51         ziduan = ziduan.strip('')
    52 
    53         # '指定分隔符'.join(seq) 这个方法将序列中的元素以指定分隔符连接生成一个新的字符串
    54         content = ''.join(content)
    55 
    56         # re.sub(pattern, repl, string, count) 替换函数,将规则表达式pattern匹配到的字符串替换为repl指定的字符串,参数count用于指定最大替换次数。正则表达式中 s 用于匹配任意空白字符(包括	
    
    fv)
    57         pattern = 's'
    58         content = re.sub(pattern,'',content)
    59 
    60         # 因为遍历出的一个content值的尾部有ue003,所以需要用到strip方法去掉。
    61         content = content.strip('ue003')
    62 
    63         #dict.update(dict2) 这个方法把字典dict2的键/值对更新到dict里。
    64         dict.update({ziduan:content})
    65 
    66     list = ['所属小区','房屋户型','房屋单价','所在位置','建筑面积','参考首付','建造年代','房屋朝向','房屋类型','所在楼层','装修程度','产权年限','配套电梯','房本年限','产权性质','唯一住房','一手房源','测试']
    67     for i in list:
    68         # dict.keys() 这个方法以列表返回一个字典所有的键。
    69         if i not in dict.keys():
    70             dict[i] = '暂无'
    71     return dict
    72 
    73 if __name__ == '__main__':
    74     appKey = ******
    75     for page in range(50):
    76         page = page + 1
    77         url = 'https://nanning.anjuke.com/sale/p'+str(page)+'/#filtersort'
    78         print('=============================现在请求的是第'+str(page)+'页================================')
    79         home_page = mogu_suidaodaili(url=url,appKey=appKey)
    80         jump_urls = get_jump_url(home_page)
    81         for jump_url in jump_urls:
    82             detail_page = mogu_suidaodaili(url=jump_url,appKey=appKey)
    83             print(get_information(detail_page))

     补充

         当我们后期如果想做数据分析的话,就会需要大量的信息。我们发现标签为 区域:全部;售价:全部;面积:全部;房型:全部   的50个主页提供的信息并不能满足数据分析的数量。

         要想获取更多信息,可以依次抓取标签顺序如下的的50个主页提供的信息:   

      区域:青秀;售价:50万以下;面积:50²以下;房型:一室  

      区域:青秀;售价:50万以下;面积:50²以下;房型:二室 

      。。。

      区域:青秀;售价:50万以下;面积:50m²以下;房型:五室以上

      区域:青秀;售价:50万以下;面积:50-70m²;房型:一室

      。。。

         区域:青秀;售价:50万以下;面积:50-70m²;房型:五室以上

      。。。

      区域:其他;售价:300万以上;面积:300m²以上;房型:五室以上

     

  • 相关阅读:
    WEB上传大文件解决方案
    上传大文件的解决方案
    网页文件断点上传
    超大文件上传方案(B/S)
    asp.net选择文件夹上传
    java文件断点上传
    超大文件上传方案(网页)
    web选择文件夹上传
    jsp选择文件夹上传
    jsp文件断点上传
  • 原文地址:https://www.cnblogs.com/weifeng1998/p/13236137.html
Copyright © 2020-2023  润新知