<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 ЦАО
Step1:  Собираем ссылки на все квартиры первых тридцати страниц выдачи
Step2:  Cтандартный блок, в котором мы получаем по ссылке текст страницы в удобном формате
Step3:  Prices
Функция для извлечения цены квартиры из странички, взято из лекции
Step4:  Функция для сбора всех цен с сайта в выбранном диапазоне ссылок ( l < r )
Step5:  Теперь используем эти функции и составим колонку для всех цен
Step6:  Dist
Извлечем координаты так же, как это было продемонстировано на лекции
Step7:  Определим функцию получения координат из текста страницы
Step8:  И функцию для получения всех координат в выбранном диапазоне ссылок (такая схема из двух функций будет повторяться везде)
Step9:  Теперь нам предстоит использовать полученные координаты для вычисления расстояния до центра города. Воспользуемся для этого формулой для вычисления расстояния между двумя точками, расположенными на сфере. Запишем отдельную функцию
Step10:  Rooms
Перейдем к числу комнат, тоже сделано по аналогии с лекционным ноутбуком
Step11:  Страницы о многокомнатных квартирах (  > 5 ) не содержат информации о количестве комнат в явном виде, такие квартиры фигурируют как "многокомнатные". Так что соответствующие объекты у меня получат в столбец Rooms значение 'mult'. Это более достоверная информация, чем, например, 6.
Step12:  Metrdist и Walking
Теперь начнем разбираться с расстоянием до метро,  заодно и со способом до него добраться (пешком или на машине)
Step13:  Тут у нас появляется отличие от того, что было раньше
Step14:  Walking
Аналогично поступим с колонкой walking
Step15:  Теперь перейдем к работе с данными, которые мы будем извлекать из таблицы. Это будущие колонки
Step16:  Информация по Brick и New содержится между следующими двумя обязательными полями
Step17:  Ситуация следующая
Step18:  New
Теперь перейдем к New - тут либо один вариант из двух (1 - новостройка, 0 - вторичка), либо nan.
Step19:  Теперь переходим дальше
Floor, Nfloors
Step20:  Информация по Floor и Nfloors тоже содержится между обязательными полями "Этаж" и "Тип продажи".
Step21:  Floor указан обязательно, и он лежит во второй ячейке, Nfloors может не быть, но если есть - то он в третьей.
Floor
Step22:  Nfloors
Step23:  Перейдем дальше
Totsp, Livesp
Step24:  Всех полей, что находятся справа от "Общая площадь", может не быть. Так что ограничим нашу область поиска только слева.
Step25:  Totsp и Livesp будут обязательно. Тут мы столкнемся с проблемой
Step26:  Totsp
Step27:  Livesp
Step28:  Переходим к опциональным полям
Kitsp, Bal, Tel
Step29:  Нужный нам подблок информации мы снова можем ограничить только слева названием поля "Общая площадь"
Step30:  Kitsp
Если площадь кухни не указана, то она фигурирует в таблице в виде прочерка. При этом она может быть float с запятой в качестве разделителя, как и предыдущие площади. Учтем это
Step31:  Bal
Балкона может не быть, балкон может быть, а еще балконов может быть больше одного. Информации о балконе может не быть.
Так что будем готовы к значениям > 1. Прочерк будет говорить о том, что информации нет, слово "нет" - о том, что есть информация о том, что нет балкона.
Step32:  Tel
Телефон может быть, может не быть, и о нем может не быть информации. О том, что он есть, нам сообщает слово "да", о том, что его нет - слово "нет".
Step33:  Последние штрихи перед сборкой DataFrame
N
Step34:  В этом ноутбуке квартиры только из ЦАО, укажу это
District
Step35:  Собираем все воедино
DataFrame
<ASSISTANT_TASK:>
Python Code:
district = 'http://www.cian.ru/cat.php?deal_type=sale&district%5B0%5D=13&district%5B1%5D=14&district%5B2%5D=15&district%5B3%5D=16&district%5B4%5D=17&district%5B5%5D=18&district%5B6%5D=19&district%5B7%5D=20&district%5B8%5D=21&district%5B9%5D=22&engine_version=2&offer_type=flat&p={}&room1=1&room2=1&room3=1&room4=1&room5=1&room6=1'
Explanation: ЦАО
End of explanation
links = []
for page in range(1, 30):
    page_url =  district.format(page)
    search_page = requests.get(page_url)
    search_page = search_page.content
    search_page = BeautifulSoup(search_page, 'lxml')
    flat_urls = search_page.findAll('div', attrs = {'ng-class':"{'serp-item_removed': offer.remove.state, 'serp-item_popup-opened': isPopupOpen}"})
    flat_urls = re.split('http://www.cian.ru/sale/flat/|/" ng-class="', str(flat_urls))
    for link in flat_urls:
        if link.isdigit():
            links.append(link)
Explanation: Собираем ссылки на все квартиры первых тридцати страниц выдачи
End of explanation
flat_url = 'http://www.cian.ru/sale/flat/' + str(links[0]) + '/'
#flat_url = 'http://www.cian.ru/sale/flat/150531912/' 
flat_page = requests.get(flat_url)
flat_page = flat_page.content
flat_page = BeautifulSoup(flat_page, 'lxml')
Explanation: Cтандартный блок, в котором мы получаем по ссылке текст страницы в удобном формате
End of explanation
def getPrice(flat_page):
    price = flat_page.find('div', attrs={'class':'object_descr_price'})
    price = re.split('<div>|руб|\W', str(price))
    price = "".join([i for i in price if i.isdigit()][-4:])
    return int(price)
Explanation: Prices
Функция для извлечения цены квартиры из странички, взято из лекции
End of explanation
def getAllPrices(l, r):
    prices = []
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        prices.append(getPrice(flat_page))
    return prices
Explanation: Функция для сбора всех цен с сайта в выбранном диапазоне ссылок ( l < r )
End of explanation
prices = getAllPrices(0, len(links))
Explanation: Теперь используем эти функции и составим колонку для всех цен
End of explanation
flat_url = 'http://www.cian.ru/sale/flat/' + str(links[0]) + '/'
#flat_url = 'http://www.cian.ru/sale/flat/150531912/' 
flat_page = requests.get(flat_url)
flat_page = flat_page.content
flat_page = BeautifulSoup(flat_page, 'lxml')
coords = flat_page.find('div', attrs={'class':'map_info_button_extend'}).contents[1]
coords = re.split('&amp|center=|%2C', str(coords))
coords
coords_list = []
for item in coords:
    if item[0].isdigit():
        coords_list.append(item)
lat = float(coords_list[0])
lon = float(coords_list[1])
lat
lon
Explanation: Dist
Извлечем координаты так же, как это было продемонстировано на лекции
End of explanation
def getCoords_at(flat_page):
    coords = flat_page.find('div', attrs={'class':'map_info_button_extend'}).contents[1]
    coords = re.split('&amp|center=|%2C', str(coords))
    coords_list = []
    for item in coords:
        if item[0].isdigit():
            coords_list.append(item)
    lat = float(coords_list[0])
    lon = float(coords_list[1])
    return lat, lon
Explanation: Определим функцию получения координат из текста страницы
End of explanation
def getAllCoordinates(l, r):
    coordinates = []
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        coordinates.append(getCoords(flat_page))
        
    return coordinates
coordinates = getAllCoordinates(0, len(links))
Explanation: И функцию для получения всех координат в выбранном диапазоне ссылок (такая схема из двух функций будет повторяться везде)
End of explanation
from math import radians, cos, sin, asin, sqrt
AVG_EARTH_RADIUS = 6371
def haversine(point1, point2):
    # извлекаем долготу и широту
    lat1, lng1 = point1
    lat2, lng2 = point2
    # переводим все эти значения в радианы
    lat1, lng1, lat2, lng2 = map(radians, (lat1, lng1, lat2, lng2))
    # вычисляем расстояние по формуле
    lat = lat2 - lat1
    lng = lng2 - lng1
    d = sin(lat * 0.5) ** 2 + cos(lat1) * cos(lat2) * sin(lng * 0.5) ** 2
    h = 2 * AVG_EARTH_RADIUS * asin(sqrt(d))
    return h 
MSC_POINT_ZERO = (55.755831, 37.617673)
distance = []
for i in range(0, len(coordinates)):
    distance.append(haversine(MSC_POINT_ZERO, coordinates[i]))
Explanation: Теперь нам предстоит использовать полученные координаты для вычисления расстояния до центра города. Воспользуемся для этого формулой для вычисления расстояния между двумя точками, расположенными на сфере. Запишем отдельную функцию
End of explanation
flat_url = 'http://www.cian.ru/sale/flat/' + str(links[2]) + '/'
#flat_url = 'http://www.cian.ru/sale/flat/150844464/' 
flat_page = requests.get(flat_url)
flat_page = flat_page.content
flat_page = BeautifulSoup(flat_page, 'lxml')
rooms_n = flat_page.find('div', attrs={'class':'object_descr_title'})
rooms_n = html_stripper(rooms_n)
rooms_n
re.split('-|\n', rooms_n)
Explanation: Rooms
Перейдем к числу комнат, тоже сделано по аналогии с лекционным ноутбуком
End of explanation
def getRoom(flat_page):
    rooms_n = flat_page.find('div', attrs={'class':'object_descr_title'})
    rooms_n = html_stripper(rooms_n)
    room_number = ''
    flag = 0
    for i in re.split('-|\n', rooms_n):
        if 'много' in i:
            flag = 1
            break
        elif 'комн' in i:
            break
        else:
            room_number += i
    
    if (flag):
        room_number = 'mult'
    room_number = "".join(room_number.split())
    return room_number
def getAllRooms(l, r):
    rooms = []
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        rooms.append(getRoom(flat_page))
        
    return rooms
rooms = getAllRooms(0, len(links))
Explanation: Страницы о многокомнатных квартирах (  > 5 ) не содержат информации о количестве комнат в явном виде, такие квартиры фигурируют как "многокомнатные". Так что соответствующие объекты у меня получат в столбец Rooms значение 'mult'. Это более достоверная информация, чем, например, 6.
End of explanation
#flat_url = 'http://www.cian.ru/sale/flat/' + str(links[2]) + '/'
flat_url = 'http://www.cian.ru/sale/flat/150387502/' 
flat_page = requests.get(flat_url)
flat_page = flat_page.content
flat_page = BeautifulSoup(flat_page, 'lxml')
metro = flat_page.find('div', attrs={'class':'object_descr_metro'})
metro = re.split('metro_name|мин', str(metro))
metro
re.split('metro_name|мин', str(metro))
Explanation: Metrdist и Walking
Теперь начнем разбираться с расстоянием до метро,  заодно и со способом до него добраться (пешком или на машине)
End of explanation
def getMetroDistance(flat_page):
    metro = flat_page.find('div', attrs={'class':'object_descr_metro'})
    metro = re.split('metro_name|мин', str(metro))
    if (len(metro) > 2): # если оба поля не были заполнены, то предыдущий сплит даст размерность 2
        metro_dist = 0
        power = 0
        
        # кусок metro[1] после сплита будет содержать в конце кучу хлама, потом количество минут (если есть)
        flag = 0
        for i in range(0, len(metro[1])):
            if metro[1][-i-1].isdigit():
                flag = 1
                metro_dist += int(metro[1][-i-1]) * 10 ** power
                power += 1
            elif (flag == 1):
                break
    else:
        metro_dist = np.nan
    return metro_dist
def getAllMetroDistances(l, r):
    metro_distance = []
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        metro_distance.append(getMetroDistance(flat_page))
        
    return metro_distance
metro_distances = getAllMetroDistances(0, len(links))
Explanation: Тут у нас появляется отличие от того, что было раньше: некоторые поля на Циане являются обязательными для заполнения, а некоторые нет, и вот эти поля обязательными не являются. Класть в данные по квартире, по которой нет этой информации, дефолтный 0 не будет правильным, потому что люди могли не указать как 3 минуты пешком (ну мало ли, лень было вписывать что-то в ячейки, квартира и так продастся), а могли не указать 30 минут на машине. Если уравнивать такие вещи (и тем более все их забивать нулями), то позже это плохо скажется на модели. Так что будем класть в такие ячейки nan'ы.
Metrdist
Функция для извлечения расстояния до метро в минутах
End of explanation
def getMetroWalking(flat_page):
    metro = flat_page.find('div', attrs={'class':'object_descr_metro'})
    metro = re.split('metro_name|мин', str(metro))
    if (len(metro) > 2): # если оба поля не были заполнены, то предыдущий сплит даст размерность 2
        if 'пешк' in metro[2]:
            walking = 1
        elif 'машин' in metro[2]:
            walking = 0
        else:
            walking = np.nan # да, проверка на то, отсутствовали ли оба поля была. мне лично не попадались ситуации, где бы не 
                             # было заполнено только значение поля "пешком/на машине", но вдруг они есть? на такой случай проверка
    else:
        walking = np.nan
    return walking
def getAllMetroWalking(l, r):
    metro_walking = []
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        metro_walking.append(getMetroWalking(flat_page))
        
    return metro_walking
walking = getAllMetroWalking(0, len(links))
Explanation: Walking
Аналогично поступим с колонкой walking
End of explanation
#flat_url = 'http://www.cian.ru/sale/flat/' + str(links[2]) + '/'
flat_url = 'http://www.cian.ru/sale/flat/150387502/' 
flat_page = requests.get(flat_url)
flat_page = flat_page.content
flat_page = BeautifulSoup(flat_page, 'lxml')
table = flat_page.find('table', attrs = {'class':'object_descr_props'})
table = html_stripper(table)
table
Explanation: Теперь перейдем к работе с данными, которые мы будем извлекать из таблицы. Это будущие колонки:
Bricks - кирпичный ли дом, монолитный (варианта жб на Циане нет)/иное
New - новостройка/вторичка
Totsp, Livsp, Kitsp - общая, жилая и площадь кухни соответственно
Tel - наличие телефона
Bal - наличие балкона
Floor - этаж
Nfloors - всего этажей в доме.
Среди вышеперечисленного обязательны к заполнению только поля Totsp, Livsp и Floor. В остальных местах могут быть nan'ы.
Bricks
End of explanation
building_block = re.split('Этаж|Тип продажи', table)[1]
building_block
Explanation: Информация по Brick и New содержится между следующими двумя обязательными полями: "Этаж" и "Тип продажи" (то есть на наличие этих слов в этом месте можно положиться)
End of explanation
def getBrick(flat_page):
    table = flat_page.find('table', attrs = {'class':'object_descr_props'})
    table = html_stripper(table)
    
    brick = np.nan
    
    building_block = re.split('Этаж|Тип продажи', table)[1]
    if 'Тип дом' in building_block:
        if (('кирпич' in building_block) | ('монолит' in building_block)):
            brick = 1
        elif (('панельн' in building_block) | ('деревян' in building_block) | ('сталин' in building_block) | 
              ('блочн' in building_block)):
            brick = 0
            
    return brick
def getAllBricks(l, r):
    
    bricks = []
    
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        bricks.append(getBrick(flat_page))
        
    return bricks
bricks = getAllBricks(0, len(links))
Explanation: Ситуация следующая: поле "Тип дома" может быть, его может не быть (тогда brick = nan). Если оно есть, то оно может содержать информацию по полю Brick, а может и не содержать. Поле Brick может задавать два значения: 1- если это кирпичный или монолитный (или кирпично-монолитный), и 0 - если это что-то другое (и мы заранее можем посмотреть на сайте на то, какие еще бывают варианты: панельный, блочный, деревянный и сталинский). Запишем эти размышления в функцию
End of explanation
def getNew(flat_page):
    table = flat_page.find('table', attrs = {'class':'object_descr_props'})
    table = html_stripper(table)
    
    new = np.nan
    
    building_block = re.split('Этаж|Тип продажи', table)[1]
    if 'Тип дом' in building_block:
        if 'новостр' in building_block:
            new = 1
        elif 'втор' in building_block:
            new = 0
            
    return new
def getAllNew(l, r):
    
    new = []
    
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        new.append(getNew(flat_page))
        
    return new
new = getAllNew(0, len(links))
Explanation: New
Теперь перейдем к New - тут либо один вариант из двух (1 - новостройка, 0 - вторичка), либо nan.
End of explanation
#flat_url = 'http://www.cian.ru/sale/flat/' + str(links[2]) + '/'
flat_url = 'http://www.cian.ru/sale/flat/150387502/' 
flat_page = requests.get(flat_url)
flat_page = flat_page.content
flat_page = BeautifulSoup(flat_page, 'lxml')
table = flat_page.find('table', attrs = {'class':'object_descr_props'})
table = html_stripper(table)
table
Explanation: Теперь переходим дальше
Floor, Nfloors
End of explanation
building_block = re.split('Этаж|Тип продажи', table)[1]
building_block
floor_block = re.split('\xa0/\xa0|\n|\xa0', building_block)
floor_block
Explanation: Информация по Floor и Nfloors тоже содержится между обязательными полями "Этаж" и "Тип продажи".
End of explanation
def getFloor(flat_page):
    table = flat_page.find('table', attrs = {'class':'object_descr_props'})
    table = html_stripper(table)
    
    floor_is = 0
    
    building_block = re.split('Этаж|Тип продажи', table)[1]
    floor_block = re.split('\xa0/\xa0|\n|\xa0', building_block)
    
    for i in range(1, len(floor_block[2]) + 1):
        if(floor_block[2][-i].isdigit()):
            floor_is += int(floor_block[2][-i]) * 10**(i - 1)
            
    return floor_is
def getAllFloors(l, r):
    
    floors = []
    
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        floors.append(getFloor(flat_page))
        
    return floors
floors = getAllFloors(0, len(links))
Explanation: Floor указан обязательно, и он лежит во второй ячейке, Nfloors может не быть, но если есть - то он в третьей.
Floor
End of explanation
def getNFloor(flat_page):
    table = flat_page.find('table', attrs = {'class':'object_descr_props'})
    table = html_stripper(table)
    
    floors_count = np.nan
    
    building_block = re.split('Этаж|Тип продажи', table)[1]
    floor_block = re.split('\xa0/\xa0|\n|\xa0', building_block)
    
    if floor_block[3].isdigit():
        floors_count = int(floor_block[3])
            
    return floors_count
def getAllNFloors(l, r):
    
    nfloors = []
    
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        nfloors.append(getNFloor(flat_page))
        
    return nfloors
nfloors = getAllNFloors(0, 20)
Explanation: Nfloors
End of explanation
#flat_url = 'http://www.cian.ru/sale/flat/' + str(links[2]) + '/'
flat_url = 'http://www.cian.ru/sale/flat/150387502/' 
flat_page = requests.get(flat_url)
flat_page = flat_page.content
flat_page = BeautifulSoup(flat_page, 'lxml')
table = flat_page.find('table', attrs = {'class':'object_descr_props'})
table = html_stripper(table)
table
Explanation: Перейдем дальше
Totsp, Livesp
End of explanation
space_block = re.split('Общая площадь', table)[1]
space_block
Explanation: Всех полей, что находятся справа от "Общая площадь", может не быть. Так что ограничим нашу область поиска только слева.
End of explanation
def myStrToFloat(string):
    delimiter = 0
    value = 0
    for i in range(0, len(string)):
        if string[i] == ',':
            delimiter = i
    for i in range(0, delimiter):
        value += int(string[delimiter - i - 1]) * 10 ** i
    for i in range(1, len(string) - delimiter):
        value += (int(string[delimiter + i]) * (10 ** (i - 2)))
    return value
Explanation: Totsp и Livesp будут обязательно. Тут мы столкнемся с проблемой: Циан записывает float через запятую, Python - через точку, поэтому дефолтная float(str) не сработает. Набросаем свой конвертор.
End of explanation
def getTotsp(flat_page):
    table = flat_page.find('table', attrs = {'class':'object_descr_props'})
    table = html_stripper(table)
    
    space_block = re.split('Общая площадь', table)[1]
   
    total = re.split('Площадь комнат', space_block)[0]
    total_space = re.split('\n|\xa0', total)[2]
    if total_space.isdigit():
        total_space = int(total_space)
    else:
        total_space = myStrToFloat(total_space)
            
    return total_space
def getAllTotsp(l, r):
    
    totsp = []
    
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        totsp.append(getTotsp(flat_page))
        
    return totsp
totsp = getAllTotsp(0, len(links))
Explanation: Totsp
End of explanation
def getLivesp(flat_page):
    table = flat_page.find('table', attrs = {'class':'object_descr_props'})
    table = html_stripper(table)
    
    space_block = re.split('Общая площадь', table)[1]
   
    living = re.split('Жилая площадь', space_block)[1]
    living_space = re.split('\n|\xa0', living)[2]
    if living_space.isdigit():
        living_space = int(living_space)
    else:
        living_space = myStrToFloat(living_space)
            
    return living_space
def getAllLivesp(l, r):
    
    livesp = []
    
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        livesp.append(getLivesp(flat_page))
        
    return livesp
livesp = getAllLivesp(0, len(links))
Explanation: Livesp
End of explanation
#flat_url = 'http://www.cian.ru/sale/flat/' + str(links[2]) + '/'
flat_url = 'http://www.cian.ru/sale/flat/150387502/' 
flat_page = requests.get(flat_url)
flat_page = flat_page.content
flat_page = BeautifulSoup(flat_page, 'lxml')
table = flat_page.find('table', attrs = {'class':'object_descr_props'})
table = html_stripper(table)
table
Explanation: Переходим к опциональным полям
Kitsp, Bal, Tel
End of explanation
space_block = re.split('Общая площадь', table)[1]
space_block
optional_block = re.split('Жилая площадь', space_block)[1]
optional_block
Explanation: Нужный нам подблок информации мы снова можем ограничить только слева названием поля "Общая площадь"
End of explanation
def getKitsp(flat_page):
    table = flat_page.find('table', attrs = {'class':'object_descr_props'})
    table = html_stripper(table)
    
    space_block = re.split('Общая площадь', table)[1]
    optional_block = re.split('Жилая площадь', space_block)[1]
    
    kitchen_space = np.nan
    
    if 'Площадь кухни' in optional_block:
        kitchen_block = re.split('Площадь кухни', optional_block)[1]
        if re.split('\n|\xa0', kitchen_block)[2] != '–':
            if re.split('\n|\xa0', kitchen_block)[2].isdigit():
                kitchen_space = int(re.split('\n|\xa0', kitchen_block)[2])
            else:
                kitchen_space = myStrToFloat(re.split('\n|\xa0', kitchen_block)[2])
            
    return kitchen_space
def getAllKitsp(l, r):
    
    kitsp = []
    
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        kitsp.append(getKitsp(flat_page))
        
    return kitsp
kitsp = getAllKitsp(0, len(links))
Explanation: Kitsp
Если площадь кухни не указана, то она фигурирует в таблице в виде прочерка. При этом она может быть float с запятой в качестве разделителя, как и предыдущие площади. Учтем это
End of explanation
def getBal(flat_page):
    table = flat_page.find('table', attrs = {'class':'object_descr_props'})
    table = html_stripper(table)
    
    space_block = re.split('Общая площадь', table)[1]
    optional_block = re.split('Жилая площадь', space_block)[1]
    
    balcony = np.nan
    if 'Балкон' in optional_block:
        balcony_block = re.split('Балкон', optional_block)[1]
        if re.split('\n', balcony_block)[1] != 'нет':
            if re.split('\n', balcony_block)[1] != '–':
                balcony = int(re.split('\n', balcony_block)[1][0])
        else:
            balcony = 0
            
    return balcony
def getAllBal(l, r):
    
    bal = []
    
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        bal.append(getBal(flat_page))
        
    return bal
bal = getAllBal(0, len(links))
Explanation: Bal
Балкона может не быть, балкон может быть, а еще балконов может быть больше одного. Информации о балконе может не быть.
Так что будем готовы к значениям > 1. Прочерк будет говорить о том, что информации нет, слово "нет" - о том, что есть информация о том, что нет балкона.
End of explanation
def getTel(flat_page):
    table = flat_page.find('table', attrs = {'class':'object_descr_props'})
    table = html_stripper(table)
    
    space_block = re.split('Общая площадь', table)[1]
    optional_block = re.split('Жилая площадь', space_block)[1]
    
    telephone = np.nan
    if 'Телефон' in optional_block:
        telephone_block = re.split('Телефон', optional_block)[1]
        if re.split('\n', telephone_block)[1] == 'да':
            telephone = 1
        elif re.split('\n', telephone_block)[1] == 'нет':
            telephone = 0
            
    return telephone
def getAllTel(l, r):
    
    tel = []
    
    for i in range(l, r):
        flat_url = 'http://www.cian.ru/sale/flat/' + str(links[i]) + '/'
        flat_page = requests.get(flat_url)
        flat_page = flat_page.content
        flat_page = BeautifulSoup(flat_page, 'lxml')
        tel.append(getTel(flat_page))
        
    return tel
tel = getAllTel(0, len(links))
Explanation: Tel
Телефон может быть, может не быть, и о нем может не быть информации. О том, что он есть, нам сообщает слово "да", о том, что его нет - слово "нет".
End of explanation
N = []
for i in range(0, len(links)):
    N.append(i)
Explanation: Последние штрихи перед сборкой DataFrame
N
End of explanation
district = []
for i in range(0, len(links)):
    district.append('CAD')
Explanation: В этом ноутбуке квартиры только из ЦАО, укажу это
District
End of explanation
data = dict([('New', new), ('Bal', bal), ('Tel', tel), ('Walk', walk), ('Metrdist', metrdist), ('Nfloors', nfloors), ('Floor', floor), ('Totsp', totsp), ('Livesp', livesp), ('Kitsp', kitsp), ('N', N), ('Price', prices), ('Rooms', rooms), ('Distance', distance), ('Brick', bricks), ('District', district)])
df = pd.DataFrame(data)
df.T
df.to_csv('cian.csv', index=False)
Explanation: Собираем все воедино
DataFrame
End of explanation
<END_TASK>