Compare commits

...

3 Commits

Author SHA1 Message Date
Alexander Popov 5aadc4603a
update to 1.0.2 2022-09-16 00:46:17 +03:00
Alexander Popov b2f6d710b3
Update README 2022-09-15 23:11:18 +03:00
Alexander Popov 6b9532b790
updates requirements and use black 2022-09-15 22:57:19 +03:00
6 changed files with 211 additions and 58 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_style = space
indent_size = 4
[*.md]
trim_trailing_whitespace = false

1
.gitignore vendored
View File

@ -1,2 +1 @@
venv/
images/

View File

@ -1,5 +1,41 @@
#### Скрипт для скачивания изображений девушек с блога [stanis.ru](https://blog.stanis.ru/)
## About us
Все загруженные изображения будут рядом со скриптом в директории `images`.
Файл `.resume` является служебным и необходим для предотвращения повторной
загрузки ранее загруженных изображений.
Скрипт для скачивания голых и не только девушек с блога [stanis.ru](https://blog.stanis.ru/)
### Как использовать?!
**Первое**
Установить [Python 3](https://python.org/).
**Второе**
Скачать необходимые зависимости.
```sh
pip install -r requirements.txt
```
**Третье**
Запустить скрипт и ждать.
```sh
python3 stanis-tits.py
```
### Ньансы!
По умолчанию, изображения загружаются в диекторию `images/` рядом с исполняемым скриптом.
Изменить это поведение можно переназначив в коде константу `DOWNLOAD_DIRECTORY`.
### Лицензия
Проект распространяется как общественное достояние.
Смотреть на сиськи должен каждый!
### Комментарии
На русском, да, и что?! Пошёл нахуй.

1
requirements-dev.txt Normal file
View File

@ -0,0 +1 @@
black

View File

@ -1,2 +1,2 @@
requests==2.20.0
beautifulsoup4==4.5.1
requests==2.28.1
beautifulsoup4==4.11.1

View File

@ -6,79 +6,180 @@ import os.path
import requests
from bs4 import BeautifulSoup
__author__ = 'Alexander Popov'
__version__ = '1.0.1'
__license__ = 'Unlicense'
__author__ = "Alexander Popov"
__version__ = "1.0.2"
__license__ = "Unlicense"
DOWNLOAD_DIR = './images'
# Путь к директории для загрузки изображений
DOWNLOAD_DIRECTORY = "./images"
def checkResumeFile():
if not os.path.exists('{0}/.resume'.format(DOWNLOAD_DIR,)):
if not os.path.exists(DOWNLOAD_DIR):
os.mkdir(DOWNLOAD_DIR)
def resume_load():
"""Возвращает список последних 20 загруженных файлов"""
with open('{0}/.resume'.format(DOWNLOAD_DIR,), 'w') as f:
f.write('0')
return([0])
# Проверяет наличие файла .resume
if not os.path.exists(
"{0}/.resume".format(
DOWNLOAD_DIRECTORY,
)
):
# Создаёт директорию для загрузки сисек
if not os.path.exists(DOWNLOAD_DIRECTORY):
os.mkdir(DOWNLOAD_DIRECTORY)
# Создаёт файл .resume и пишет в него 0
with open(
"{0}/.resume".format(
DOWNLOAD_DIRECTORY,
),
"w",
) as f:
f.write("0")
return [0]
else:
with open('{0}/.resume'.format(DOWNLOAD_DIR,), 'r') as f:
lines = [line.split('\n')[0] for line in f][-20:]
with open(
"{0}/.resume".format(
DOWNLOAD_DIRECTORY,
),
"r",
) as f:
lines = [int(line.split("\n")[0]) for line in f][-20:]
return(lines)
return lines
def saveResume(resumeList):
resumeList.sort()
with open('{0}/.resume'.format(DOWNLOAD_DIR,), 'w', encoding='utf-8') as f:
for item in resumeList[-20:]:
f.write('{0}\n'.format(item))
def resume_save(donwloaded_list):
"""Сохраняет список последних 20 загруженных файлов"""
donwloaded_list.sort()
with open(
"{0}/.resume".format(
DOWNLOAD_DIRECTORY,
),
"w",
encoding="utf-8",
) as f:
for item in donwloaded_list[-20:]:
f.write("{0}\n".format(item))
def getImagesLinks(page):
URL = lambda page: 'http://blog.stanis.ru/?back={0}'.format(page,)
COOKIES = dict(block='951')
def get_images_links(page):
"""В качестве аргумента получает номер страницы
и возвращает списком адреса всех изображений"""
r = requests.get(URL(page), cookies=COOKIES)
soup = BeautifulSoup(r.text.encode('cp1251'),
"html.parser", from_encoding="windows-1251")
# На сайте фильтр изображений настроен через cookies
# Так что устанавливаем их в нужное положение
cookies = dict(block="951")
imagesData = soup.findAll('img', src=re.compile('img/*'))
# Загружаем страницу и подсовываем её парсеру с необходимыми параметрами
r = requests.get(
"http://blog.stanis.ru/?back={0}".format(
page,
),
cookies=cookies,
)
soup = BeautifulSoup(
r.text.encode("cp1251"), "html.parser", from_encoding="windows-1251"
)
imagesUrl = list()
# Получаем все теги <img> на странице
img_tags = soup.findAll("img", src=re.compile("img/*"))
for image in imagesData:
imagesUrl.append(image['src'].split('/')[1])
# Получаем все адреса изображений и сохраняем их в список img_links
img_links = list()
for image in img_tags:
img_links.append(image["src"].split("/")[1])
return(imagesUrl)
return img_links
def imageDownload(image):
response = requests.get('https://blog.stanis.ru/imgs/{0}'.format(image,),
stream=True)
def image_download(image):
"""В качестве аргумента получает уникальное имя изображения,
скачивает и сохраняет его на диск"""
with open('{0}/{1}'.format(DOWNLOAD_DIR, image),
'wb') as out_image:
shutil.copyfileobj(response.raw, out_image,)
response = requests.get(
"https://blog.stanis.ru/imgs/{0}".format(
image,
),
stream=True,
)
image_size = int(response.headers.get("Content-Length", 0))
if (
os.path.exists("{0}/{1}".format(DOWNLOAD_DIRECTORY, image))
and int(os.path.getsize("{0}/{1}".format(DOWNLOAD_DIRECTORY, image)))
== image_size
):
pass
else:
with open("{0}/{1}".format(DOWNLOAD_DIRECTORY, image), "wb") as out_image:
shutil.copyfileobj(
response.raw,
out_image,
)
if __name__ == '__main__':
resumeFiles = checkResumeFile()
if __name__ == "__main__":
"""Главный цикл программы"""
LOOP = True
downloadPage = 0
resume_files = resume_load()
while LOOP:
imagesLinks = getImagesLinks(downloadPage)
imagesLinks.sort()
current_page = 0
downloaded_counter = 0
for image in imagesLinks:
if not image.split('.')[0] in resumeFiles:
imageDownload(image)
resumeFiles.insert(0, image.split('.')[0],)
else:
LOOP = False
WHILE_BREAK = False
downloadPage += 1
while True:
try:
# Получаем адреса изображений и сортируем их в порядке возрастания
images = get_images_links(current_page)
images.sort()
saveResume(resumeFiles)
# Костыль
if WHILE_BREAK:
break
# По очереди скачиваем изображения
for image in images:
# На сайте могут обновляться изображения,
# когда находятся варианты более лучшего
# качества и разрешения.
# К именам таких файлов добавляется _NUM,
# где NUM - количество изменений,
# например 1356_3.jpg
# Файл .resume хранит последние 20 загруженных файлов.
# Бывает иногда, что изображение удаляется
# и может получиться так, что сохраненное имя в .resume
# будет отсутсвовать на странице, что будет в холостую
# продолжать работу скрипта.
# Файлы на странице расположены в обратном порядке,
# для этого перед их загрузкой и сохранением в .resume
# списки сортируются от меньшего к большему.
# По этому было принято решение, удалять часть _NUM
# из имени файла при сохранение в .resume.
# Один хуй никто не будет парсить сайт с самого начала,
# чтобы скачать сиськи в более высоком качестве.
# Если в списке resume присутствует текущий загружаемый файл
# тогда цикл программы останавливается.
# P.S. смотри на костыль WHILE_BREAK
if not int(image.split(".")[0].split("_")[0]) in resume_files:
image_download(image)
resume_files.insert(
0,
int(image.split(".")[0].split("_")[0]),
)
downloaded_counter += 1
resume_save(resume_files)
else:
WHILE_BREAK = True
break
current_page += 1
except KeyboardInterrupt:
print("Загружено {0} файлов.".format(downloaded_counter))
quit()