Хочешь скачать все файлы с /b или другого раздела? И без дубликатов? И только файлы больше/меньше X Килобайт? Или только картинки/только видео? Или файлы только с конкретного треда? Или тебе нужен трекер, который будет отбирать треды по ключевым словам? Всё это здесь! И даже больше!
В репозитории представлен готовый набор скриптов для двача, все скрипты можно кастомизировать под свои задачи. При минимальных знаниях питона можно с легкостью написать скрипт под свои нужды. Вся информация ниже.
Установите python
Скачайте zip архив или:
git clone https://github.com/diademoff/2ch
Установите зависимости:
cd 2ch
pip install -r requirements.txt
Запускайте нужный скрипт:
python {название скрипта}.py
- Скачать все файлы и посты треда
thread_saver.py - Уведомления о новых тредах на досках
tracker.py - Самые популярные треды на доске
popular.py - Скачивать все файлы доски
board_media.py
Все скрипты можно редактировать под ваши задачи.
thread_saver.pyFOLDER = 'saver'- Изменить имя папки, в которую будут сохраняться файлыSAVE_MEDIA- Сохранять ли изображения и видеоDELAY- Интервал обновления в секундах
tracker.pytext_limit = 155- Изменить длину строкиboard_names = 'b news sex v hw gg dev soc rf ma psy fet'- Изменить список досок (писать через пробел)KEY_WORDS- Указать ключевые слова
popular.pytext_limit = 164- Длина строкиmax_lines = 55- Максимальное количество строк в выводеboard_name = 'b'- Доска, которая парситсяKEY_WORDS- Выводить треды только с ключевыми словами
board_media.pyBOARD = 'b'- Имя борды, с которой скачивать файлыFOLDER_NAME = 'media'- Имя папки, в которую скачивать файлыKEY_WORDS = []- Отбирать треды по ключевым словам, если ключевые слова не указаны, то будут скачиваться файлы всех тредовEXTENSIONS = []- Файлы с какими расширениями скачиватьMAX_FILE_SIZE- Задать максимальный размер файла в КилобайтахMIN_FILE_SIZE- Задать минимальный размер файла в Килобайтах
- Скрипт не запускается.
Проверьте установлены ли зависимости: pip install -r requirements.txt. Проверьте кодировку файлов. Проверьте, что у вас установлена версия Python > 3.
- Как сравниваются изображения?
Изображения сравниваются по содержимому. Даже если у изображений разное расширение png и jpg, или разный размер они всё равно будут распознаны как одинаковые.
- Ты используешь api двача?
Да. А конкретно:
https://2ch.hk/makaba/mobile.fcgi?task=get_thread&board={board_name}&thread={num}&post=1
http://2ch.hk/{name}/threads.json
- Зачем тебе beautiful soup?
Преимущественно чтобы убирать html теги в постах. Если в посте жирный текст, то получается так:
<strong>текст</strong>. Этот тэг нужно убрать, чтобы остался только текст.
- Как указать ключевые слова?
Откройте нужный скрипт и отредактируйте по образцу. Обратите внимание на форматирование, запятые и кавычки.
KEY_WORDS = [
"цуиь",
"mp4"
]- Скрипты кроссплатформенные?
Да. Скрипты были проверены на Linux и Windows.
Весь api хранится в файле dvach.py. Подключаем:
import dvach- Board
name: str- Имя доскиposts: dict- Список постов, это словарь. Ключ - это номер треда, значение - переменная типаThreadjson_link: str- Ссылка на json тредовfrom_json()- Получить объектBoardиз json'аjson_download()- Скачать json доскиthread_exists()- Есть ли на доске тред с указанным номеромupdate_threads()- Обновить список тредов на доскеsort_threads_by_posts()- Отсортировать список тредов по количеству постов, чем ближе элемент к началу списка, тем больше в нем постовget_new_threads()- Сравнить текущий список тредов с другим и получить словарь новых тредовget_dead_threads()- Сравнить текущий список тредов с другим и получить словарь утонувших тредов
- Thread
comment: str- Текст в ОП постеnum: str- Номер тредаposts_count: int- Количество постовscore: float- Сколько очков у тредаsubject: str- Сокращенныйcommentviews: int- Количество просмотровunique_posters: int- Количество уникальных просмотров (появится после обновления постов)board_name: str- Какой доске принадлежит тредposts = []- Список постовget_link: str- Ссылка на тредget_op_post: Post- Получить ОП-постjson_posts_link: str- Ссылка на json тредаsave(path)- сохранить в html посты треда в указанную папкуIsOk()- Подходит ли тред по заданным ключевым словамupdate_posts()- Скачать json и обновить их список, вызывает функциюget_posts()get_posts()- Спарсить json и обновитьunique_postersиpostsjson_download()- Получить json постов в чистом виде
- Post
comment: str- Текстdate: str- Дата постаemail: strop: intnum: str- Номерfiles: []- Список файлов
- Post_file
displayname: str- Отображаемое имяname: str- Имяdownload_link: str- Ссылка на скачиваниеwidth: int- Ширинаheight: int- Высотаsize: int- Размер файлаIsImage: bool- Является ли файл изображениемIsVideo: bool- Является ли файл видеоsave()- Сохранить файл по указанному путиIsOk()- Подходит ли файл по заданным расширениям, максимальному и минимальному размеру
Класс Board позволяет взаимодействовать с досками (b, news, po, soc и т.д).
Объявление:
board = dvach.Board('b')Теперь в переменной board хранится доска b, но там нет никакой информации, кроме названия доски. Чтобы получить список тредов на доске:
board.update_threads()Теперь в поле threads находится словарь с тредами. Ключ - это номер треда, значение - это тред (Thread).
Получить список с номерами тредов:
# Список из номеров тредов, каждый номер имеет строковой тип.
thread_nums = list(board.threads.keys())Отсортируем по популярности и снова получим список номеров тредов:
board.sort_threads_by_posts()
thread_nums = list(board.threads.keys())Первый элемент теперь является номером самого популярного треда:
most_popular_num = thread_nums[0]Мы получили номер самого популярного треда, теперь получим сам тред из словаря threads:
thread = board.threads[most_popular_num]
В этом словаре значение имеет тип Thread. Посмотрим тип переменной thread:
print(type(thread))Получим: <class 'dvach.Thread'>
Получим список постов в треде:
print(f"Количество постов (длина posts): {len(thread.posts)}")
print(f"Количество постов (posts_count): {thread.posts_count}")
thread.update_posts()
print(f"Количество постов (длина posts): {len(thread.posts)}")
print(f"Уникальных просмотров: {thread.unique_posters}")На выходе получим:
Количество постов (длина posts): 0
Количество постов (posts_count): 60
Количество постов (длина posts): 64
Уникальных просмотров: 34
unique_posters - появляется только после вызова update_posts() или get_posts().
Получение количества постов с помощью len(thread.posts) является более точным, но требует загрузки всех постов, в то время как thread.posts_count известно во время получения тредов на доске.
Для сохранения треда используйте класс HtmlGenerator и метод get_thread_htmlpage. Этот метод возвращает html код, который можно сохранить в файл.
op_file = thread.posts[0].files[0] # Картинка в ОП-посте
img_path = os.path.normpath(f'./{op_file.name}') # Путь, куда мы ее сохраним
op_file.save(img_path) # Сохраняем картинку
# Получаем html
html = dvach.HtmlGenerator.get_thread_htmlpage(thread, img_path)
# Создаём файл
file = open(f'thread_{thread.num}.html', 'w')
# Записывает туда html страницу
file.write(html)Или используйте функцию:
# Файл сохранится в папку, в которой выполняется скрипт с именем thread_{num}.html
thread.save('.')После получения списка постов с помощью update_posts() в поле posts появился список постов начиная с ОП-поста.
Посмотрим второй пост в треде:
post = thread.posts[1]
print(f"Номер: {post.num}")
print(f"Текст: {post.comment}")
print(f"Количество файлов: {len(post.files)}")На выходе получаем:
Номер: 210762237
Текст: Бамп
Количество файлов: 1
Теперь получим первый файл в посте, если файл есть:
if len(post.files) > 0:
file = post.files[0]
print(type(file))На выходе получим: <class 'dvach.Post_file'>
Посмотрим больше информации о файле:
print(f"Имя файла: {file.name}")
print(f"Ширина: {file.width}")
print(f"Высота: {file.height}")
print(f"Отображаемое имя: {file.displayname}")
print(f"Ссылка: {file.download_link}")На выходе:
Имя файла: 16200245064090.jpg
Ширина: 3118
Высота: 1754
Отображаемое имя: 1620024504280.jpg
Ссылка: https://2ch.hk/b/src/245763818/16200245064090.jpg
Можно легко сохранить файл:
file.save(file.name)Файл будет сохранен в директорию в которой выполняется скрипт с именем 16200245064090.jpg
Можно указать кастомный путь:
file.save(f"/home/username/{file.name}")Весь код, используемый в примерах:
import dvach
import os
# Объявить доску
board = dvach.Board('b')
# Скачать треды
board.update_threads()
# Получить список номеров тредов
thread_nums = list(board.threads.keys())
# Отсортировать по количеству постов
board.sort_threads_by_posts()
# Обновить список с номерами тредов
thread_nums = list(board.threads.keys())
# Номер самого популярного треда
most_popular_num = thread_nums[0]
# Самый популярный тред
thread = board.threads[most_popular_num]
# Посмотреть тип переменной
print(type(thread))
print(f"Количество постов (длина posts): {len(thread.posts)}")
print(f"Количество постов (posts_count): {thread.posts_count}")
# Скачать посты
thread.update_posts()
print(f"Количество постов (длина posts): {len(thread.posts)}")
print(f"Уникальных просмотров: {thread.unique_posters}")
op_file = thread.posts[0].files[0] # Картинка в ОП-посте
img_path = os.path.normpath(f'./{op_file.name}') # Путь, куда мы ее сохраним
op_file.save(img_path) # Сохраняем картинку
# Получаем html
html = dvach.HtmlGenerator.get_thread_htmlpage(thread, img_path)
# Создаём файл
file = open(f'thread_{thread.num}.html', 'w')
# Записывает туда html страницу
file.write(html)
# Получить второй пост (который сразу после ОП-поста)
post = thread.posts[1]
print(f"Номер: {post.num}")
print(f"Текст: {post.comment}")
print(f"Количество файлов: {len(post.files)}")
if len(post.files) > 0:
# Получить первый файл
file = post.files[0]
print(type(file))
print(f"Имя файла: {file.name}")
print(f"Ширина: {file.width}")
print(f"Высота: {file.height}")
print(f"Отображаемое имя: {file.displayname}")
print(f"Ссылка: {file.download_link}")
# Сохранить файл
file.save(file.name)
# file.save(f"/home/username/{file.name}")