CoderCastrov logo
CoderCastrov
Парсер

Как найти автомобиль по цене ниже рыночной - с помощью Python, BeautifulSoup и вашего собственного Telegram-бота.

Как найти автомобиль по цене ниже рыночной - с помощью Python, BeautifulSoup и вашего собственного Telegram-бота.
просмотров
9 мин чтение
#Парсер

Покупка автомобиля - это инвестиция, которую следует продумать. Но как найти хорошее предложение, когда вы конкурируете с другими покупателями и автодилерами, у которых может быть больше опыта и времени, чем у вас? Действительно хорошие предложения обычно размещаются онлайн очень короткое время, потому что продавец получает слишком много звонков - поэтому важно быть одним из первых звонящих. Я хотел бы показать вам, как решить эту проблему с помощью Python, BeautifulSoup и вашего собственного Telegram-бота. В конце этого проекта у нас будет работающая программа, которая:

1. Парсит результаты одного из крупнейших порталов по продаже подержанных автомобилей в Германии

2. Автоматически уведомляет вас о хорошем предложении

Этот проект разработан для обучения основам парсинга веб-данных с помощью Python и BeautifulSoup с использованием практического примера и показа того, как Data Science может быть использован для решения индивидуальных повседневных проблем.

Если вы хотите создать этот проект, убедитесь, что веб-сайт, с которым вы работаете, разрешает парсинг! Вы можете узнать, разрешено ли это, добавив "/robots.txt" к домену страницы. Данный проект предназначен только для демонстрационных целей.

Давайте начнем!

Для этого проекта я буду парсить информацию с немецкого сайта с подержанными автомобилями. На сайте есть большая база данных и много пользователей. Чтобы уточнить мой поиск, я сначала выберу модель автомобиля, которую я хочу отслеживать, чтобы сузить поиск. Для этого я выбрал Bentley Continental GT с первой регистрацией с 2010 года и максимальным пробегом 100 000 км.

Как работает парсинг веб-страниц и с чего начать?

Парсинг означает получение всей информации с определенного веб-сайта. Мы можем получить весь HTML-код страницы, отфильтровать его с помощью нашего кода, чтобы получить чистые данные и делать выводы проще и быстрее.

Прежде всего, нам нужно найти целевой URL, который в данном случае уже содержит предопределенный поиск. Для этого мы выполняем один поиск вручную. Это означает, что нам нужно заполнить форму поиска на сайте, выполнить поиск, установить сортировку по "возрастанию цены" и затем скопировать URL из браузера. Функция фильтрации предлагает большое количество настроек, но теперь нам нужно выбрать с умом, какие из них полезны для быстрого принятия решения: является ли это хорошим или плохим предложением по автомобилю? Мы не хотим отфильтровать хорошее предложение. Я решил использовать только три настройки фильтрации для этого поиска: название компании и модель автомобиля, год первой регистрации и пробег. С помощью этого поиска мы получаем обзор последних опубликованных автомобилей.

Теперь наступает самая интересная часть: как мы можем найти наилучшее предложение полностью автоматически? Для этого мы сначала определяем, что делает предложение хорошим. После краткого исследования рыночной цены такого автомобиля я выяснил, что самые дешевые предложения в Германии составляют около 60 000-70 000 евро и могут считаться относительно дешевыми. Я решил придерживаться предела в 65 000 евро. Поэтому мы хотим, чтобы наша программа информировала нас, когда автомобиль рекламируется на этом URL с ценой ниже 65 000 евро. Прежде всего, мы реализуем URL, который мы получили через ручной поиск, в нашем коде, чтобы получить автоматическую фильтрацию при запуске кода.

Одна из наших основных задач - выяснить, где именно в HTML-коде этого сайта находится информация о цене для каждого автомобиля. И для этого мы будем использовать BeautifulSoup. Чтобы начать, нам сначала нужно вставить необходимые библиотеки для нашего проекта и определить заголовок. Заголовок позволяет нам сделать вид, что мы получаем доступ к этой странице с помощью браузера. Частое использование веб-страниц может привести к временной блокировке. Поэтому вам следует быть осторожными.

import requests
from bs4 import BeautifulSoup
from requests import get
import pandas as pd
import itertools
import schedule
import time
import random
from time import sleep
from datetime import datetime
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

headers = ({'User-Agent':
                'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36'})

Обработка HTML-кода

Прежде чем мы приступим к основной части кода, нам следует выяснить, где именно в HTML-коде хранится информация о цене. Здесь начинается работа детектива, и инструментом, который я использую для этого, является браузер Chrome (Щелкните правой кнопкой мыши -> "Просмотреть код страницы"). Чтобы упростить поиск, я щелкаю правой кнопкой мыши на поле, где указана цена автомобиля, и нажимаю "Инспектировать".

Right-click on the price — Inspect (Turns yellow)

Теперь открывается окно с HTML-кодом, в котором уже отмечен выбранный мной элемент. Как видим, текстовая информация 61.899,- находится прямо здесь с помощью тега span:

Finding the target information in HTML-jungle

Мы видим, что нам все еще нужно очистить цену, чтобы продолжить работать с ней как с числом. Весь текст ("€", " —" и так далее) должен быть удален из кода. Мы скоро позаботимся об этой части, но сначала мы займемся правильным извлечением информации о цене.

Теперь мы знаем, что цена находится где-то с помощью тега span. Поскольку мы хотим получить цену каждого автомобиля, нам все еще нужно выяснить, под каким тегом мы можем найти главный контейнер для каждого автомобиля. Поднявшись по дереву HTML, мы можем узнать контейнер для автомобиля. Для этого шага полезно разместить веб-страницу и HTML-код рядом и перемещать указатель мыши по коду. Браузер затем - в зависимости от того, в какой строке кода находится указатель мыши - отображает соответствующее поле в вашем браузере синим цветом. Это позволяет легко определить, где находится главный контейнер. В данном случае это отмеченный контейнер div:

Finding the Main-Container for a single car

Если мы просмотрим контейнер div, мы увидим, что вся информация, которую мы видим на веб-сайте, хранится прямо здесь. Все подзаголовки также помечены тегами div и span. Наша следующая задача теперь состоит в том, чтобы вызвать правильный главный контейнер в нашем коде и правильно указать, где именно и под каким тегом находится информация о цене.

Основная часть кода

Теперь перейдем к основной части кода, где мы определяем целевой URL и запускаем BeautifulSoup.

cars_url = 'https://www.ЗДЕСЬ-ВАШ-ЦЕЛЕВОЙ-URL!!'
r = get(cars_url, headers=headers)
page_html = BeautifulSoup(r.text, 'html.parser')

Следующая задача - найти точное местоположение цены. Для этого мы возьмем только первую машину на странице, позволим программе найти все записи с тегом 'span' и покажем результаты:

Поиск правильного местоположения

Запуск кода дает нам следующую информацию:

Результаты запуска кода

Как видно, искомая информация находится в третьем контейнере с тегом span. Важно отметить, что в Python нумерация начинается с 0. Таким образом, первый результат можно вызвать с помощью [0], второй с помощью [1] и так далее. Это означает, что мы можем вызвать цену с помощью [2]. Теперь, когда мы знаем точное положение информации, мы можем приступить к очистке данных.

Очистка!

Очистку можно выполнить разными способами. Чтобы сделать проект максимально простым для начинающих, я предложил два варианта. В первом варианте мы рассматриваем, какие символы нужно удалить, чтобы получить нужное нам числовое значение. В данном случае это знак евро, дефис, точка, запятая и пробел. Мы можем удалить их вручную следующим образом (каждый символ записан в отдельной строке):

price = firstcar.find_all('span')[2].text
print("цена до замены символов:" + price)
price = price.replace('.', '')
price = price.replace('€', '')
price = price.replace(',', '')
price = price.replace(' ', '')
price = price.replace('-', '')
print("цена после замены символов:" + price)

Второй вариант более сложный, но элегантный: объединение функций "join()", "isdigit()" и "filter()" в одной строке кода:

price = ''.join(filter(lambda i: i.isdigit(), price))

Оба варианта дадут нам одинаковый результат:

цена до замены символов:
€ 61.899,-цена после замены символов: 
61899

Хотя у нас теперь в "цене" есть только цифры, этот объект все еще является строкой. С помощью (print(type(price)) мы можем проверить это в любое время. Нам все еще нужно преобразовать этот объект в целое число, чтобы выполнить математическое уравнение: цена `< предельная цена.

price = int(price)
print(type(price))

Для завершения кода нам все еще не хватает следующих элементов:

1. Подключение и настройка Telegram-бота

2. "if-запрос", если полученная цена ниже установленного нами предела

3. Настройка программы для повторения через определенный интервал времени



Настройка вашего Telegram-бота

Для создания бота нам нужно установить Telegram (если его еще нет на вашем телефоне или компьютере) и использовать BotFather: https://t.me/botfather

С помощью команд /start и /newbot мы можем создать бота и присвоить ему имя. В ответ мы получим ссылку на нашего бота и токен бота, который нам нужно внедрить в наш код. Кроме того, нам нужен наш личный идентификатор чата. Чтобы получить его, используйте бота по ссылке, сгенерированной выше, запустите его и отправьте сообщение. Затем откройте следующую ссылку в браузере:

https://api.telegram.org/bot<yourtoken>/getUpdates

Замените <your token> на ваш токен бота, который вы только что получили, и скопируйте число после "id": это ваш идентификатор чата. Чтобы внедрить Telegram Chat-Bot в Python, вы можете использовать следующий код:

def telegram_bot_sendtext(bot_message):
    bot_token = 'Введите ваш токен здесь!!'
    bot_chatID = 'Введите ваш идентификатор чата здесь!!'
    send_text = 'https://api.telegram.org/bot' + bot_token + '/sendMessage?chat_id=' + bot_chatID + '&parse_mode=Markdown&text=' + bot_message
    response = requests.get(send_text)
    return response.json()message = telegram_bot_sendtext('Найдена новая машина!')

Не забудьте вставить ваш токен и идентификатор чата. Также вы можете настроить ваше сообщение в последней строке.


Фильтрация цен выше нашего лимита может быть выполнена просто запуском этого условного запроса перед отправкой сообщения.

limit = 65000
if price<limit:

Чтобы обработать все автомобили на первой странице поиска, а не только первый, нам нужно добавить цикл в начало нашего кода:

if car_containers != []:

    for container in car_containers:

Поскольку мы хотим повторить весь процесс поиска автоматически, нам нужно определить наш основной код как функцию с

def crawlcar():

и настроить расписание для функции:

schedule.every(X).seconds.do(crawcar)

Таким образом, код будет запускаться снова через X секунд.


Одна проблема, которую мы еще не решили: Как обрабатывать сигнал, когда та же машина "находится снова" в следующем запуске?

Поскольку наша программа находит одну и ту же машину снова и снова, она будет отправлять вам сигнал каждый раз. Чтобы избежать этой проблемы, мы можем установить список с определенными характеристиками. Поскольку мы уже работаем с ценой, мы можем использовать этот атрибут сразу. Нам нужно только две строки кода, которые делают следующее: Сохраняем текущую цену для каждой полученной машины в списке. Спрашиваем перед запуском функции Telegram: Эта цена уже есть в списке? Если да, не отправляйте ее. Таким образом, мы избегаем ошибки срабатывания сигнала для одного и того же автомобиля.

И с этим мы закончили и можем запустить нашу программу! Результат теста показывает, что веб-скрепер работает: мы получаем следующее сообщение в Telegram:

Первое сообщение!

Теперь по крайней мере мы знаем, что машина с нашими критериями только что была размещена. Однако, чтобы увидеть машину или любую другую информацию, нам нужно перейти на страницу вручную и найти ее (что в основном не является проблемой). Тем не менее, мы можем настроить наш код и сократить этот шаг, просто добавив ссылку на машину. Для этого, аналогично поиску цены, мы ищем HTML-код контейнера, который содержит ссылку на описание машины, сохраняем его (как и делали с ценой) и добавляем его прямо в наше уведомление бота. Таким образом, мы можем извлечь любую информацию, которая нас интересует, например, дату первой регистрации и пробег. Извлечение этих данных еще проще, так как нам не нужно их редактировать или использовать математические уравнения, а можно взять их прямо как строку. Результат выглядит так:

Теперь выглядит лучше

Теперь все, что нам нужно сделать, это накопить 61 899 евро! Спасибо за чтение! Дайте мне знать, если вам интересны подобные проекты.