Парсер товаров Авито написан с учетом рефакторинга кода, то есть код разбит на части, каждая из которых отвечает за определенную функцию (что такое рефакторинг и как его реализовать, можно почитать тут)
Код проекта на Github
1. Импорт библиотек.
И так, начнем написание нашего парсера с импорта всех нужных нам библиотек:
import random
import re
import time
import pandas
import requests
from bs4 import BeautifulSoup
from pandas import ExcelWriter
Библиотеки random и time нам пригодятся для выставления задержки парсера между страницами, что бы не нагружать сайт c большим количеством запросов за единицу времени
re — библиотека регулярных выражений, пригодится для поиска по частичному совпадению текста, т.к. код периодически изменяется
2. Функция get_html(url, params=None)
Итак первая функция, это функция получения кода страницы get_html в нее будем передавать нашу ссылку url и параметры params (по умолчанию None)
def get_html(url, params=None):
""" получение кода страницы """
headers = {
"Accept": "*/*",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0)"
}
html = requests.get(url, headers=headers, params=params)
return html
Добавим свои headers — наши заголовки (где брать headers?) что бы сайт не заподозрил нас, как вредоносный код.
3. Функция get_pages(html)
Вторая функция get_pages, которая возвращает нам количество страниц выдачи поиска.
Для этого найдем крайнюю ссылку «След. →» по тегу span и по значению параметра data—marker, воспользуемся функцией beautifulsoup‘а privious_element , который находит предыдущий элемент найденного тэга, который является искомым. (Рисунок 1)
Принцип такой: находим «След. →» и берем предыдущий элемент, если «След. →» не найден, то присваиваем значение 1
def get_pages(html):
""" получаем количество страниц """
soup = BeautifulSoup(html.text, 'html.parser')
# находим кол-во страниц, иначе количество страниц равно 1
try:
pages = soup.find('span', {'data-marker': 'pagination-button/next'}).previous_element
except:
pages = 1
print('Количество найденных страниц: ', pages)
return pages
4. Функция get_content(html)
Теперь пишем функцию сбора данных со страницы get_content, во всех поисках нужных тэгов я применил библиотеку re, т.к. классы тэгов периодически изменяются на сайте, что приводит к выводу из строя нашего парсера. (Рисунок 2)
Находим все блоки карточек с помощью функции BeautifulSoup’а find_all с тэгом div и классом iva-item-content. Заметьте, я беру только часть класса, не включив в нее UnQQ4 т.к. эта часть периодически изменяется (Рисунок 3):
blocks = soup.find_all('div', class_=re.compile('iva-item-content'))
Далее пробегаемся по всем карточкам товаров (blocks) и собираем с них нужные нам данные по тому же принципу, занося полученные данные в список (data). Функция полностью:
def get_content(html):
""" сбор контента со страницы """
soup = BeautifulSoup(html.text, 'lxml')
blocks = soup.find_all('div', class_=re.compile('iva-item-content'))
# сбор данных с страницы
data = []
for block in blocks:
data.append({
"Наименование": block.find('h3', class_=re.compile('title-root')).get_text(strip=True),
'Цена': block.find('span', class_=re.compile('price-text')).get_text(strip=True).replace('₽', '').replace('\xa0', ''),
'Город': block.find('a', class_=re.compile('link-link')).get('href').split('/')[1],
'Район': block.find('div', class_=re.compile('geo-root')).get_text(strip=True),
'Ссылка': url + block.find('a', class_=re.compile('link-link')).get('href'),
})
return data
5. Функция save_excel(data, file_name)
Функция save_excel получает собранные данные data и file_name это название под которым будем сохранять наш файл excel. Сохранение реализовано, через библиотеку pandas.
# Создали фрейм собранных данных:
df_data = pandas.DataFrame(data)
# Далее чистим данные от дубликатов
data_clear = df_data.drop_duplicates('Ссылка')
# и само сохранение в файл .xlsx
writer = ExcelWriter(f'{file_name}.xlsx')
data_clear.to_excel(writer, f'{file_name}')
writer.save()
Функция полностью:
def save_excel(data, file_name):
# сохраняем полученные данные в эксель через pandas
df_data = pandas.DataFrame(data)
print(f'До удаления дубликатов: {len(df_data)} записей')
# чистим дубликаты записей (проплаченные посты дублируются на разных страницах)
data_clear = df_data.drop_duplicates('Ссылка')
print(f'После удаления дубликатов: {len(data_clear)} записей')
writer = ExcelWriter(f'{file_name}.xlsx')
data_clear.to_excel(writer, f'{file_name}')
writer.save()
print(f'Данные сохранены в файл "{file_name}.xlsx"')
6. Функция parse(url) (основная функция Авито парсера)
Основная функция выполняющая все предыдущие + пользовательский ввод.
От пользователя принимаем запрос поиска search, минимальную/максимальную цену товара, для уменьшения выдачи поиска:
search = input('Введите запрос поиска: ')
min_price = input('Введите минимальную стоимость: ')
max_price = input('Введите максимальную стоимость: ')
Отправляем запрос с заданными параметрами:
html = get_html(url, params={'bt': 1, 'pmax': max_price, 'pmin': min_price, 'q': search, 's': '2', 'view': 'gallery'})
создаем на его основе объект BeautifulSoup и вытаскиваем заголовок
soup = BeautifulSoup(html.text, 'lxml')
print(soup.h1.get_text())
Далее сделаем проверку на ответ от сайта, если доступ к данным есть, т.е. ответ от сервера равен 200, то выполняем блок кода, если сервер нас ограничил в данных, или данные по нашему запросу не найдены, выведем ошибку и завершим программу.
Если статус код с сайта равен 200, то определяем количество страниц выдачи и спрашиваем у пользователя сколько страниц спарсить:
get_pages(html)
pagination = int(input('Сколько страниц спарсить? '))
Далее пробегаемся по заданным пользователем страницам собирая с них данные и расширяя ими список data
for page in range(1, pagination + 1):
html = get_html(url, params={'bt': 1, 'p': page, 'pmax': max_price, 'pmin': min_price, 'q': search, 's': '2', 'view': 'gallery'})
print(f'Парсинг страницы {page} из {pagination}...')
data.extend((get_content(html)))
Функция полностью:
def parse(url):
search = input('Введите запрос поиска: ')
min_price = input('Введите минимальную стоимость: ')
max_price = input('Введите максимальную стоимость: ')
html = get_html(url, params={'bt': 1, 'pmax': max_price, 'pmin': min_price, 'q': search, 's': '2', 'view': 'gallery'})
soup = BeautifulSoup(html.text, 'lxml')
print(soup.h1.get_text())
print('Ссылка со всеми параметрами:\n', html.url)
print('Статус код сайта: ', html.status_code)
data = []
# проверка сайта на доступ
if html.status_code == 200:
# вызов функции для вывода количества найденных страниц
get_pages(html)
pagination = int(input('Сколько страниц спарсить? '))
for page in range(1, pagination + 1):
html = get_html(url, params={'bt': 1, 'p': page, 'pmax': max_price, 'pmin': min_price, 'q': search, 's': '2', 'view': 'gallery'})
print(f'Парсинг страницы {page} из {pagination}...')
data.extend((get_content(html)))
time.sleep(random.randint(1, 3))
print(f'Получили {len(data)} позиций')
# сохраняем в эксель, передав наши собранные данные и запрос
save_excel(data, search)
else:
print('Ошибка доступа к сайту')
7. Запуск основной функции Авито парсера:
if __name__ == "__main__":
parse(url)
Спасибо, что прочли основную часть статьи, жду от вас обратной связи, пишите что нужно рассмотреть, что подробнее объяснить, рассказать, показать, под опросом расположенная доп. информация о примененных запросах.
Читать далее: Авито. Cбор данных товаров используя Python