Парсер книг на Литрес с публикацией во ВКонтакте

Скрипт написан на Python. Общий принцип работы — парсятся книги по готовому набору категорий. Сразу в цикле публикуются на странице ВКонтакте. Формат — заголовок, описание, ссылка на источник с добавлением идентификатора партнерской программы.

Используется вложенный цикл — обход по категориям, обход по полученным ссылкам текущей категории.

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

p = requests.get(f'https://www.litres.ru/pub/c/cover_250/{id}.jpg')

Используются два текстовых файла — набор категорий (их всего более ста) и ID уже опубликованных книг для проверки на дубликаты. Можно доработать, чтобы скрипт сам получал категории, но мне так удобнее — можно выбирать раздел, новые/популярные.
Книги берутся только с первой страницы, в данном случае полученный набор дополнительно ограничивается — остаются только три самых популярных или самых новых. Если обход идет по всем категориям это оптимально — у ВКонтакте лимит публикаций составляет 150 сообщений в сутки.

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

vk_api.exceptions.ApiError: [214] Access to adding post denied: you can only add 150 posts a day

Также к ссылке категории добавляется подготовленный набор параметров, что позволяет указать язык и тип книг (текстовые, аудио, подкасты). Пример ниже — текстовые и аудио книги, только русский язык. Нужный набор можно скопировать из адресной строки, указав предварительно опции.

options = '?art_types=text_book&art_types=audiobook&languages=ru' 

Все нужные данные собираются в отдельные переменные. Далее собирается нужный текст и публикуется.

Для парсинга используется библиотека Beautiful Soup, для публикации — vk_api. Весь код с комментариями ниже.

import requests
from bs4 import BeautifulSoup
from time import sleep
import os
import random
import vk_api
import re

cookies = {}  # важно полностью указать данные параметры, иначе сайт Litres не будет отдавать контент
headers = {}

all_count = 0 # счетчик для вывода в консоль
options = '?art_types=text_book&art_types=audiobook&languages=ru'  # текстовые и аудиокниги, русский язык
ref = '?lfrom=123456'   # идентификатор реферальной ссылки

with open("new.txt", "r", encoding="utf-8") as file_cl:  # файл со списком категорий
    category_list = file_cl.read().split('\n')

with open("id.txt", "r", encoding="utf-8") as file_id:   # файл со списком идентификаторов
    all_id = file_id.read().split('\n')
file_id.close()

vk_session = vk_api.VkApi('телефон', 'пароль', app_id = 'ID_приложения')  # авторизация ВКонтакте
vk_session.auth()
vk = vk_session.get_api()

random.shuffle(category_list)  # перемешивание списка, необязательно
count_cat = len(category_list) + 1  # только для вывода в консоль
for category in category_list:      # начинаем обход категорий
    count_cat -= 1
    sleep(20)
    req = requests.get(f'{category}{options}', headers=headers, cookies = cookies)
    src = req.text

     # для удобства тестирования страница сохраняется в промежуточный файл и сразу извлекается
     # при тестировании этап сохранения можно отключить, чтобы не заблокировали 
    with open(f"temp_html/current_category.html", "w", encoding="utf-8") as file:  
        file.write(src)
    
    with open(f"temp_html/current_category.html", encoding="utf-8") as file:  
        src = file.read()
    soup = BeautifulSoup(src, "lxml")
    try:
        name_category = soup.find("div", class_="Genre-module__title__name_JuQ14").text
    except:
        print('ошибка, пропуск')
        continue
    name_category_clear = name_category.replace('/', '') 
    if not os.path.exists(f'temp_html/{name_category_clear}/'):
        os.makedirs(f'temp_html/{name_category_clear}/')
    list_href = soup.find_all('a', {'data-testid': ['art__title']})  # извлекаем ссылки в список
    list_href = list_href[:3]  # оставляем три новых и запускаем обход по ссылкам
    for href in list_href:
        
        title, author, isbn, description, id, count_author = '', '', '', '', '', ''
        
        link = (f"{href.get('href')}")  # получаем чистую ссылку, извлекаем ID и проверяем на дубль
        id = ((link.split('-'))[-1])[:-1]
        if id in all_id:
            print(f'дубль - {id} -- осталось категорий: {count_cat}-- добавлено: {all_count}')
            continue
        sleep(30)
        all_id.append(id)
        all_count += 1
        print(f'новый - {id} -- осталось категорий: {count_cat}-- добавлено: {all_count}')
        with open("id.txt", "a", encoding="utf-8") as file_id:
            file_id.write(f'{id}\n')

        # переходим на страницу книги и извлекаем данные
        req = requests.get(f"https://www.litres.ru{link}", headers=headers, cookies = cookies)
        src = req.text

        with open(f"temp_html/{name_category_clear}/{id}.html", "w", encoding="utf-8") as file_book:
            file_book.write(src)
    
        with open(f"temp_html/{name_category_clear}/{id}.html", "r", encoding="utf-8") as file_book:
            src = file_book.read()
        soup = BeautifulSoup(src, "lxml")
        title = soup.find("h1").text
        if soup.find(class_="Authors-module__authors_2_blq"):  # данные могут отсутствовать, поэтому проверяем
            count_author = soup.find(class_="Authors-module__authors_2_blq").find("h4").text

        isbn = soup.find("span", itemprop="isbn")
        if isbn:
            isbn = isbn.text
        if soup.find(class_="Authors-module__authors_2_blq"):
            author = soup.find("div", class_="Authors-module__authors__wrapper_1rZey").find_all("span", itemprop="name")
        for i in range(len(author)):  # авторов может быть несколько, правильно оформляем
            if author[i]:
                author[i] = author[i].text
        author = ', '.join(map(str, author))
        try:
            description = soup.find(class_="BookCard-module__book__annotation_2ZIhf").find_all('p')
        except:
            print('ошибка - нет текста, пропуск')  # если описание не получено, пропускаем итерацию полностью
            continue
        
        # собираем нужный текст и публикуем. Описание обрезается до одного абзаца, можно изменить

        ch_n = '\n'
        vk_message = f'{title}{ch_n}{author}{ch_n}{ch_n}{("".join(map(str, description))).split("</p>")[0]}{ch_n}{ch_n}Скачать:{ch_n}www.litres.ru{link}{ref}'
        vk_message = vk_message.replace("</p>", "\n")
        vk_message = re.sub('<[^<]+?>', '', vk_message)

        print(vk.wall.post(message=vk_message))
        sleep(180) # пауза 3 минуты, чтобы не забанили




Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: