Парсинг веб-сайта недвижимости для сбора набора данных
Table Of Content
Поиск подходящих данных для обучения моделей машинного обучения часто является настоящей проблемой. Несмотря на то, что существует множество веб-сайтов, пытающихся решить эту проблему, таких как Kaggle и Google Dataset Search, для большинства моих собственных идей по машинному обучению требуются дополнительные данные. В этом руководстве я покажу, как парсить веб-сайт недвижимости с использованием Ruby для сбора исторических данных о проданных объектах недвижимости.
Конечная цель - иметь достаточное количество данных, чтобы иметь возможность отвечать на интересующие вопросы о шведском рынке недвижимости. Поскольку крупнейший сайт недвижимости в Швеции, hemnet.se, не предлагает общедоступного API, мы собираемся парсить HTML напрямую. Все проданные объекты на веб-сайте доступны в списках, где элементы списка указывают на страницу с подробной информацией.
Используя библиотеки Ruby Rest-Client и Nokogiri, мы можем получить и выбрать нужные данные со страниц веб-сайта. Каждая веб-страница, содержащая списки проданных объектов недвижимости, доступна по следующей структуре URL:
https://www.hemnet.se/salda/bostader?page=#{index}&sold_age=all
Поскольку у Hemet в настоящее время 122 239 страниц с списками проданных объектов недвижимости, индекс в URL представляет собой диапазон от 1 до 122 239.
number_of_pages = (1..12239)number_of_pages.each do |index| link = “https://www.hemnet.se/salda/bostader?page=#{index}&sold_age=all" scrape_page(link)end
Приведенный выше код перебирает диапазон страниц, чтобы создать все необходимые ссылки и передает их в метод #scrape_page.
def scrape_page(link) css = "li.sold-results__normal-hit div.sold-property-listing a.item-link-container" webpage_data = data_from_link(link, css) webpage_data.each do |data| sold_propety_link = data.attr("href").split("/maklare/salda/")[0] parse_sold_property_data(sold_propety_link) end enddef data_from_link(link, css_input) response_string = RestClient.get(link) webpage = Nokogiri::HTML(response_string) webpage_data = webpage.css(css_input)end
Давайте пройдемся по этому коду.
-
Метод #data_from_link использует Rest-Client для получения HTML и Nokogiri для выбора нужного тега a.
-
Мы можем получить URL страницы с подробным просмотром значений проданных объектов, перебирая данные веб-страницы. Чтобы удалить добавленное по умолчанию окончание "/maklare/salda/XXXX", мы используем метод #split и оставляем только первый элемент массива.
Наконец, мы добавляем правильную ссылку в наш метод #parse_sold_property_data, показанный ниже.
def parse_sold_property_data(link) proprety_details_css = "div.sold-property__details dl dd.sold- property__attribute-value"
sold_price_css = "span.sold-property__price-value"
title_css = "h1.sold-property__address"
sold_date_css = "p.sold-property__metadata time" details_data = data_from_link(link, proprety_details_css)
sold_price_data = data_from_link(link, sold_price_css)
title_data = data_from_link(link, title_css)
sold_date = data_from_link(link, sold_date_css) titel = title_data[0].text.split("\n").last
sold_price = sold_price_data[0].text.split(" kr")[0]
sold_date = sold_date[0].values[0] organize_and_store_data(details_data, titel, sold_price, sold_date)end
Приведенный выше метод #parse_sold_property_data использует ту же технику парсинга CSS, но здесь мы извлекаем данные, заголовок, цену и дату. Метод #organize_and_store_data затем использует эти значения для создания хэша для этой недвижимости, как показано ниже.
[@data](http://twitter.com/data) = []
[@values](http://twitter.com/values) = {starting_price: "kr", price_per_square_meters: "kr/m²", number_of_rooms: "rum", living_are_square_meters: "m²", cost_per_month: "kr/mån"}def organize_and_store_data(data, title, sold_price, sold_date)current_hash = {title => {}}current_hash[title][:sold_price] = sold_price
current_hash[title][:sold_date] = sold_datedata.each do |row|
[@values](http://twitter.com/values).each do |key, unit|
if !row.text.include?("%")
if row.text.split(" ").include?(unit)
current_hash[title][key] = row.text
elsif (1700..2019).include?(row.text.to_i)
current_hash[title][:construction_year] = row.text
end
end
end
end[@data](http://twitter.com/data) << current_hashend
Это момент, когда дела становятся немного сложными. Hemnet не использует уникальный класс или идентификатор для тегов в подробном просмотре элементов, которые мы хотим получить. Но у каждого из них есть конкретная единица измерения в конце их значений, поэтому мы можем различать значения, используя единицы после их числового значения.
Используя хэш с названием values, мы можем проверить, содержат ли какие-либо теги подробного просмотра эти конкретные единицы измерения. Обратите внимание, что для года постройки, который не имеет конкретной единицы измерения, мы проверяем, включен ли он в диапазон от 1700 до 2019. Также мы убеждаемся, что значения не содержат знак процента, поскольку мы не хотим сохранять изменение от начальной цены по сравнению с конечной ценой в нашем JSON-файле, так как его легко можно рассчитать по другим значениям. Наконец, мы создаем JSON-файл и сохраняем его в локальной директории с помощью метода #save_json_to_file ниже.
def save_json_to_file(data)
json_file = File.new('hemnet_json.json', 'w')
json_file.write(data.to_json)
json_file.close
end
Et voilà! Теперь у нас есть данные о более чем 600 000 проданных объектах недвижимости в нашем JSON-файле.
Простой способ создать модель машинного обучения из этих данных можно выполнить с помощью фреймворка Create ML от Apple в Swift playground. Для надежного руководства по Create ML прочтите этот учебник от Appcoda.
Весь код проекта доступен на GitHub в этом репозитории.
(Обратите внимание, что целью этой статьи является исключительно академическая.)