YandexDirect

21 января 2019

Python. Парсинг сайта на примере krisha.kz, часть вторая

Добрый день, добавил в код парсера функции для сохранения фотографий, теперь рядом с csv файлом сохраняется папка с фотографиями из объявлений. Для каждого объявления своя папка. В планах добавить изменений прокси(proxy) сервера и юзер агента(useragent), для того чтобы не нарваться на бан.




import requests
from bs4 import BeautifulSoup
from time import sleep
from selenium import webdriver
import urllib.request
import csv
import os

#функция для получения html
def get_html(url):
    r = requests.get(url)
    return r.text

#функция подсчета количества страниц
#используем библиотеку BeautifulSoup для поиска html тегов на странице
#
def get_total_pages(html):
    soup = BeautifulSoup(html, 'lxml') # определяем объект soup
    divs = soup.find('nav', class_='paginator-public') # находим на странице первый объект nav с именем класса paginator-public
    pages = divs.find_all('a', class_='paginator-page-btn')[-2].get('href') # находим на странице все теги "a" с именем класса paginator-page-btn, из них выбираем элемент с индексом [-2] это будет последняя страница
    print(pages)
    total_pages = pages.split('page=')[1].split('&')[0] # из полученной ссылки делаем сплит и выбираем значение между знаком "=" и "&"
    return int(total_pages) #возвращаем полученное значение
    # return 2

# находим количество  фотографий и формируем ссылки на фотографии
def find_all_photo_urls(html):
    soup = BeautifulSoup(html, 'lxml') # определяем объект soup
    divs = soup.find('div', class_='gallary__container')
    # проверяем наличие фотографий, если в объявлении нет фотографий то возвращаем 100
    if (len(divs.text))==1:
        photo_kol=100
        return photo_kol

    photo_kol = len(divs.find_all('li')) # считаем количество фотографий
    if photo_kol == 0:
        photo_kol = 1
        return photo_kol
    
    print(photo_kol)
    # получаем url фотографий
    photo_url = divs.find_all('div', class_='gallary__small-item')

    all_photo = [] # создаем список и передаем ему все ссылки на фотографии
    for i in range(photo_kol):
        all_photo.append(photo_url[i].get('data-photo-url'))
    return all_photo

def get_file(url):
    r = requests.get(url, stream=True)
    return r
 
# задаем имя файла и создаем папку на компьютере 
def get_name(url, folder):
    name = url.split('/')[-1]
    if not os.path.exists(folder):
        os.makedirs(folder)
    path = os.path.abspath(folder)
    # print('get_name'+path)
    return path + '\\' + name

# сохраняем фотографии
def save_image2(url, name):
    urllib.request.urlretrieve(url, name)

#функция для записи csv файла 
def write_csv(data):
    with open('krisha.csv', 'a', encoding='utf-8') as f:
        writer = csv.writer(f)
        writer.writerow((data['title'],
                         data['price'],
                         data['etaj'],
                         data['square'],
                         data['address'],
                         data['description'],
                         data['url'],
                         data['telephone']))
#функция для получения телефонов
#так как на сайте скрыты телефоны, необходимо использовать дополнительный пакет selenium
#для имитации нажатия на ссылку с телефоном, для этого запускается браузер chrome в режиме
#"headless" т.е. без отображения окна
def get_phone(url):
    options = webdriver.ChromeOptions()
    options.add_argument('headless') 
    options.add_argument('window-size=1200x600')
    #driver = webdriver.Chrome('C:\\python37\\chromedriver_win32\\chromedriver.exe') # or Firefox() or smth else
    driver = webdriver.Chrome(options=options) # определяем объект типа webdriver
    driver.get(url) # передаем ему на вход url
    link = driver.find_element_by_id('tm-telephone-body') # переменной link присваиваем ссылку, которая нам покажет телефон
    link.click() # кликаем на ссылку
    sleep(1) # делаем паузу в 1 секунду
    tel = driver.find_element_by_class_name('offer__contacts-phones').text.strip() # присваиваем переменной tel показанные номера телефонов
    driver.quit() # не забываем закрывать невидимый браузер
    return tel # возвращаем телефон

# функция получения данных с сайта, используем библиотеку BeautifulSoup, она позволяет выполнять поиск по html тегам
# 
def get_page_data(html):
    soup = BeautifulSoup(html, 'lxml') # определяем объект soup
    divs = soup.find('section', class_='a-list') # находим тег section с именем класса a-list
    ads = divs.find_all('div', class_='a-card__inc') # в найденном объекте ищем все div с классом a-card__inc
    # запускаем цикл по всем найденным полям, которые нас интересуют
    # т.к. ссылка состоит 3-комнатная квартира, 79.4 м², 5/5 эт. я решил ее разделить на поля квартира, площадь, этаж
    # далее выбираем цену, адрес и описание
    #
    for ad in ads:
        try:
            div = ad.find('a', class_='a-card__title').text
            kv = div.split(",")[0] 
        except:
            kv = ''
        try:
            div = ad.find('a', class_='a-card__title').text
            square = div.split(",")[1]
        except:
            square  = ''
        try:
            div = ad.find('a', class_='a-card__title').text
            etaj = div.split(",")[2]
        except:
            etaj  = ''

        try:
            price = ad.find('div', class_='a-card__price').text.strip()
        except:
            price = ''
        try:
            address = ad.find('div', class_='a-card__subtitle').text.strip()
        except:
            address = ''
        try:
            descr = ad.find('div', class_='a-card__text-preview').text.strip()
        except:
            descr = ''
        try:
            div = ad.find('div', class_='a-card__header-left')
            url = "https://krisha.kz" + div.find('a').get('href')
            #tel = ''
        except:
            url = ''
            #tel = ''

        # парсим урл и присваиваем значение переменной folder , это и будет именем папки с фото
        folder = url.split('/')[-1]
        urls = find_all_photo_urls(get_html(url)) # вызываем функцию поиска урлов всех фотографий
        # в функции выше мы возвращали значение 100, тут проверяем, если 100 значит фотографий в объявлении нет, иначе фотки есть - сохраняем их
        if urls == 100:
            print ("Фотографий в объявлении нет!")
        else:
            for url2 in urls:
                save_image2(url2, get_name(url2, folder))

        tel1 = get_phone(url) # тут вызываем функцию выборки телефонов
        # определяем кортеж из наших полей и передаем его функции write_csv    
        data = {'title':kv,
                'price':price,
                'etaj':etaj,
                'square':square,
                'address':address,
                'description':descr,
                'url':url,
                'telephone':tel1}
        write_csv(data)
# основная функция 
# базовый url для Астаны имеет вид https://krisha.kz/prodazha/kvartiry/astana/
# к нему добавляется запрос query_part и еще дальше идет блок со страницами page_part
# 
def main():
    #url = 'https://krisha.kz/prodazha/kvartiry/astana/?das[live.rooms]=1'
    # тут передаем основной урл для парсинга
    url_gen = ('https://krisha.kz/prodazha/kvartiry/astana-almatinskij/?das[flat.floor][from]=1&das[flat.floor][to]=1&das[live.rooms]=1&sort_by=price-asc')
    total_pages = get_total_pages(get_html(url_gen))
    

    for i in range(1, total_pages):
        # url_gen = base_url + query_part + page_part + str(i)
        html = get_html(url_gen)
        get_page_data(html)

#блок main
if __name__ == '__main__':
    main()

Комментариев нет:

Отправить комментарий

Общее·количество·просмотров·страницы