Скрипт написан на 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 минуты, чтобы не забанили