CoderCastrov logo
CoderCastrov
Парсер

Когда ученый-исследователь данных ищет место для жизни

Когда ученый-исследователь данных ищет место для жизни
просмотров
3 мин чтение
#Парсер

Жил-был парень, который уехал из Северо-Востока Бразилии (Форталеза, штат Сеара) и отправился на поиски приключений в Сан-Паулу. У этого парня была серьезная проблема с принятием решений, и он, как и любой новичок, не знал, насколько дорого стоит снимать жилье в новом городе. Однако ему нужно было снять место для проживания в новом городе (Кампинас, штат Сан-Паулу), чтобы начать новое путешествие. Он начал с посещения всех сайтов, которые нашел в интернете, чтобы сравнить цены. После того, как он потерялся на нескольких сайтах, где объявления о недвижимости были представлены в разных форматах, он решил изменить подход. Будучи ученым-исследователем данных, он решил использовать свои знания для принятия решения. Вкратце, этот парень был я.

Я решил немного изучить парсинг и, очевидно, нашел много материалов, использующих нашего любимого Python. С помощью этой статьи https://medium.com/@henriquecoura_87435/webscraping-com-python-extraindo-dados-de-um-ecommerce-89c16b622f69 я смог написать довольно простой скрипт для сбора информации о квартирах с двумя спальнями и одним местом на парковке в Кампинасе, штат Сан-Паулу. Поскольку это был мой первый проект по парсингу, я считаю, что его можно существенно улучшить. Поэтому не стесняйтесь улучшать его в своих проектах. Я выбрал сайт OLX для получения данных, но это может быть применено к любому другому сайту.



Оригинальный код

Прямо к делу, ниже приведены использованные библиотеки. Полужирным шрифтом выделены библиотеки, которые я никогда не использовал, но в них нет ничего сложного.

**import lxml.html as parser**
import requests
import csv
import re
**from urllib.parse import urlsplit, urljoin**
import time
import numpy as np
import pandas as pd
import random
import matplotlib.pyplot as plt

Следующие функции я создал, чтобы сделать код более чистым. Они извлекают числа и текст из грязных строк вроде '\n\n\n\n\n\n\Cambui\t\t\t\t\t\t' или 'R$ 1.200 \n\n'.

def get_float(string_):
 return float(''.join(re.findall('\d+',string_)))
def get_cep(string_):
 return ''.join(re.findall('\d{5}-\d{3}',string_))
def sub_string(string_):
 return string_.strip()

Следующая функция была немного сложнее и потребует некоторого времени, чтобы понять структуру HTML-страницы. Вам придется потратить время на просмотр, поиск и минимальное понимание структуры страницы. Не беспокойтесь, если вы ничего не понимаете, это процесс обучения и он стоит того.

def get_links_page(start_url):
 r = requests.get(start_url)
 html = parser.fromstring(r.text)
 links = html.xpath("//a[@class='OLXad-list-link']/@href")
 links = [urljoin(start_url, l) for l in links]
 links = [urlsplit(l)._replace(query="").geturl() for l in links]
 next_page = html.xpath("//div[@class='module_pagination']//ul[@class='list']//li[@class='item next']//a[@class='link'][@rel='next']/@href")[0]
 return links, next_page

Эта функция получает посещенный URL при выполнении базового поиска квартиры с двумя спальнями в Кампинасе (https://sp.olx.com.br/grande-campinas/regiao-de-campinas/campinas/imoveis/aluguel/2-quartos?gsp=1). Функция извлекает все ссылки на объявления на этой странице и ищет на странице небольшие числа с пагинацией сайта. Этот адрес пагинации используется для получения следующей страницы с продолжением результатов поиска. Таким образом, можно переходить на вторую, третью, четвертую страницу и так далее. Последняя функция похожа на предыдущую, но более сложная. Это связано с тем, что она посещает все полученные ссылки и ищет несколько полей, описывающих недвижимость, и добавляет все это в замечательный dataframe (потому что так веселье будет полным).

def get_atributes(links):
 tp_imovel = []
 condominio = []
 iptu = []
 area = []
 quartos = []
 banheiros = []
 garagem = []
 prices = []
 cep = []
 bairro = []
 city = []
 url = links
 for i in links:
 time.sleep(random.randint(1,3))
 r = requests.get(i)
 product_html = parser.fromstring(r.text) 
 des_dict = dict(zip(
 product_html.xpath("//ul[@class='list square-gray']/li[@class='item']/p[@class='text']/span[@class='term']/text()"),
 product_html.xpath("//ul[@class='list square-gray']/li[@class='item']/p[@class='text']/strong[@class='description']/text()")))try:
 tp_imovel.append(sub_string(des_dict['Tipo:']))
 except:
 tp_imovel.append('')
 try:
 condominio.append(get_float(des_dict['Condomínio:']))
 except:
 condominio.append(None)
 try:
 iptu.append(get_float(des_dict['IPTU:']))
 except:
 iptu.append(None)
 try:
 area.append(get_float(des_dict['Área útil:']))
 except:
 area.append(None)
 try:
 quartos.append(get_float(des_dict['Número de quartos:']))
 except:
 quartos.append(None)
 try:
 banheiros.append(get_float(des_dict['Número de banheiros:']))
 except:
 banheiros.append(None)
 try:
 garagem.append(get_float(des_dict['Vagas na garagem:']))
 except:
 garagem.append(None)
 try:
 city.append(sub_string(des_dict['Município:']))
 except:
 city.append('')
 try:
 cep.append(get_cep(des_dict['CEP do imóvel:']))
 except:
 cep.append('')
 try:
 bairro.append(sub_string(des_dict['Bairro:']))
 except:
 bairro.append('')
 try:
 prices.append(get_float(product_html.xpath("//h3[@class='price']/span[@class='actual-price']/text()")[0]))
 except:
 prices.append(None)
 df = pd.DataFrame({'tp_imovel':tp_imovel,'price':prices,'cond':condominio,
 'iptu':iptu,'area':area,'quartos':quartos,'banheiros':banheiros,
 'garagem':garagem,'cep':cep,'City':city,'Bairro':bairro,'url':links}) 
 return df

Теперь просто создайте цикл с созданными функциями.

df_t = pd.DataFrame()
next_page = '[https://sp.olx.com.br/grande-campinas/regiao-de-campinas/campinas/imoveis/aluguel/2-quartos?gsp=1'](https://sp.olx.com.br/grande-campinas/regiao-de-campinas/campinas/imoveis/aluguel/2-quartos?gsp=1%27)
for i in range(200):
 print('Страница =>',i)
 try:
 n_links, next_page = get_links_page(next_page)
 df = get_atributes(n_links)
 df_t = pd.concat([df_t,df])
 except:
 print('Ошибка при поиске новых страниц.')
 break

Первичный анализ данных после небольшой очистки, удаляющей некоторые выбросы с помощью Kmeans.

Вот и все, ребята. Если у вас есть вопросы, вот ссылка (https://github.com/samuelmorais1891/scraping_olx.git) на мой jupyter-notebook, не стесняйтесь отправлять вопросы или предложения. Спасибо.