Рубрики
Без рубрики

Создан приложение соскребания данных аниме с kyivymd и BS4

Я большой поклонник аниме и как любой любовник аниме, я всегда хочу быть в курсе моего любимого аниме …. Теги с Python, kivymd, аниме, веб-описанием.

Я большой поклонник аниме и как любой любовник аниме, я всегда хочу, чтобы быть в курсе моего любимого аниме. Я хочу знать, был ли новым эпизодом и, как правило, я всегда проверяю эти аниме сайты для обновлений. Но что часто происходит, это то, что я в конечном итоге приклеиваю к сайту, обнаружив все больше и больше аниме, пока не поймешь «О, это было три часа!» 😅

Я решил создать приложение, которое просто поддерживает меня с несколькими аниме, которые я хочу посмотреть. Это был довольно хороший способ встать на скорость с помощью веб-соскоба и работать с некоторыми из тех Кивимд компоненты.

Приложение довольно просто. Сначала добавьте URL аниме на https://gogoanime.vc Затем перейдите к экрану «Обновление Anime» и освежитесь, удаваясь. Это начнет процесс соскабливания данных с сайта и отобразит список аниме, с изображением справа от каждого списка элемента и данные о аниме, таком как имя, количество эпизодов, и, закончил ли он нет.

Настройка

Сначала создайте виртуальную среду. Внутри этого установите kivy, kivymd, beautifulsoup4, lxml и запросы со следующими командами:

  • Пип устанавливает Кив
  • Пип Установите Кивимд
  • PIP Установите BS4
  • PIP Установка запросов
  • PIP Установите LXML

Мы собираемся использовать lxml Парсер с bowioSoup4.

Теперь создайте 3 файла в одном каталоге:

  • main.py – Содержать основной код приложения
  • Main.kv – Содержать код интерфейса
  • Scrap.py – Этот файл будет содержать весь код, ответственный за соскреб.

Давайте получим кодирование!

Внутри main.py добавьте следующие строки

from kivymd.app import MDApp
from kivy.uix.screenmanager import ScreenManager
from kivymd.uix.screen import MDScreen

class ScreenManagement(ScreenManager):
    pass

class MainWindow(MDScreen):
    pass

class AddUrlScreen(MDScreen):
    pass

class MainApp(MDApp):
    def build(self):
        self.theme_cls.primary_palette = "DeepPurple"

if __name__ == "__main__":
    app = MainApp()
    app.run()

Здесь я просто создаю скелет приложения. Он будет иметь два экрана, основной экран, ответственный за отображение информации аниме и экран URL, где мы можем добавить URL для аниме, который мы хотим лома. Мне нравится цвет фиолетовый И так я установил self.theme_cls.primary_palette «глубокому». Вы можете проверить kivymd Документация Больше подробностей.

Далее, внутри Main.kv, добавьте следующий код:

ScreenManagement:
    MainWindow:
    AddUrlScreen:

:
    name: 'mainscreen'

    #toolbar
    MDBoxLayout:
        orientation: 'vertical'

        MDToolbar:
            pos_hint: {'top': 1}
            title: 'AnimeUpdate'
            right_action_items: [["link-variant-plus", lambda x: lambda x:app.open_settings_screen()]]
        MDLabel:

Здесь мы добавляем два экрана, которые мы создали в менеджер экрана. Затем мы определяем экран «MainWindow». Затем мы даем ему имя, чтобы мы могли ссылаться на него позже. Затем мы добавляем панель инструментов с виджетом справа. Виджет позвонит app.open_settings_screen () Функция, которую мы создадим позже. «MDLabel» – это просто заполнитель. Если вы запустите приложение, теперь вы получите следующее:

Далее мы добавляем компоновку обновления. Изменить класс в Main.kv смотреть следующим образом:

:
    name: 'mainscreen'

    #toolbar
    MDBoxLayout:
        orientation: 'vertical'

        MDToolbar:
            pos_hint: {'top': 1}
            title: 'AnimeUpdate'
            right_action_items: [["link-variant-plus", lambda x: lambda x:app.open_settings_screen()]]

        # add these lines
        MDScrollViewRefreshLayout:
            id: refresh_layout
            refresh_callback: root.refresh_callback
            root_layout: root

            MDGridLayout:
                id: box
                adaptive_height: True
                cols: 1

В приведенном выше коде мы добавляем Mdscrollviewlayout Что собирается позволить нам обновить экран, провожу вниз сверху. Обратите внимание, что мы удалили MDLabel Отказ Мы также добавляем Mdgridlayout который собирается содержать элементы списка.

Теперь измените MainWindow класс в main.py смотреть следующим образом:

class MainWindow(MDScreen):
    def refresh_callback(self, *args):
        print("Refreshing...")

Мы определяем функцию под названием Refresh_Callback который будет называться, когда мы проводим. На данный момент он собирается распечатать «освежающую …» в терминале.

Сохранить и запустить приложение с Python Main.py В терминале, а затем проведите на экране:

Далее мы собираемся создать экран URL. Внутри Main.kv Добавьте следующий код:

:
    name: 'addurl'
    id: addurl

    # toolbar
    MDBoxLayout:
        orientation: 'vertical'

        MDToolbar:
            pos_hint: {'top': 1}
            title: 'Settings'
            left_action_items: [["keyboard-backspace", lambda x: app.return_to_main_window()]]

        MDFloatLayout:
            size_hint: 1, .9
            id: linkscreen

            MDTextField:
                id: linkinput
                size_hint: .8, None
                pos_hint: {'center_x': .5, 'y': .9}
                hint_text: 'Add Url'
                mode: 'rectangle'
                text_validate_unfocus: False
                on_text_validate: root.add_url(linkinput.text)

            ScrollView:
                md_bg_color: app.theme_cls.primary_color
                pos_hint: {'center_x': .5, 'y': .1}
                size_hint: .9, .8

                MDList:
                    id: linklist

AddUrlscreen Имеет панель инструментов, но на этот раз виджет слева. Этот виджет вернет нас на главный экран, позвонив на app.return_to_main_window () Функция, которую мы собираемся определить дальше. Экран URL имеет текстовое поле. Мы набор text_validate_unfocus к Ложь Так что текстовое поле не теряет фокусировки, когда мы нажимаем ввод. Когда мы нажимаем ввод, текст внутри текстового поля будет подан на функцию root.add_url () Что мы также собираемся определить дальше.

Модифицировать main.py следующее:

#...
from kivy.uix.screenmanager import ScreenManager, CardTransition # new import
#...

class AddUrlScreen(MDScreen):
    def add_url(self, text):
        """Add the url to list and save the url in shelve file"""
        self.ids.linkinput.focus = True
        self.ids.linkinput.text = ''
        print(text)

class MainApp(MDApp):
    def build(self):
        self.theme_cls.primary_palette = "DeepPurple"
        self.root.transition= CardTransition() # new line

    #define methods to switch between screens
    def open_settings_screen(self):
        """open setting window"""
        self.root.current = 'addurl'
        self.root.transition.direction = 'down'

    # new method
    def return_to_main_window(self):
        self.root.current = 'mainscreen'
        self.root.transition.direction = 'up'

Здесь мы импортируем Кардтрансция который является одним из многочисленных анимаций экрана перехода. В AddUrlscreen Класс, мы также добавляем add_url Метод, который прямо сейчас отображает только текст в терминале. В MainApp Класс, мы устанавливаем экран перехода на Кардтрансция и добавить методы для переключения между Windows.

Теперь мы собираемся определить элемент пользовательского списка, который добавляется в экран URL-адреса всякий раз, когда мы нажимаем Enter в поле Text:

В Main.kv Добавить:

:
    IconLeftWidget:
        icon: "web"

    IconRightWidget:
        icon: "trash-can"
        theme_text_color: "Custom"
        text_color: 1, 0, 0, 1
        on_release: root.delete_item(root.text)

Модифицировать main.py :

from kivymd.uix.list import OneLineAvatarIconListItem, ThreeLineAvatarIconListItem, ImageLeftWidget

# new class
class CustomListItem(OneLineAvatarIconListItem):
    def delete_item(self, text):
        """Delete list item"""
        self.parent.remove_widget(self)

# modify AddUrlScreen
class AddUrlScreen(MDScreen):
    def add_url(self, text):
        """Add the url to list"""
        self.ids.linklist.add_widget(CustomListItem(text=text)) # new line
        self.ids.linkinput.focus = True
        self.ids.linkinput.text = ''
        # removed print line

Мы импортировали три предмета, некоторые из которых мы собираемся использовать позже. Создать CustomListitem класс, который наследует от Onelineavatariconlistitem класс. CustomListitem Иконка слева и значок удаления справа. Если вы запускаете приложение сейчас, вы можете добавлять и удалять элементы:

Теперь мы собираемся настроить способ сохранить URL-адреса, которые мы добавили, чтобы нам не нужно продолжать добавлять URL-адреса каждый раз, когда мы хотим получить информацию о аниме. Для этого мы собираемся использовать Python полза модуль. Модифицировать main.py следующее:

import shelve, os # import shelve and os

# modify CustomListItem to delete item from shelve file
class CustomListItem(OneLineAvatarIconListItem):
    def delete_item(self, text):
        """Delete list item"""
        with shelve.open('./save_files/mydata')as shelf_file:

            url_list = shelf_file['url_list']
            url_list.remove(str(text))
            shelf_file['url_list'] = url_list

        self.parent.remove_widget(self)

# modify the AddUrlScreen class
class AddUrlScreen(MDScreen):
    def add_url(self, text):
        """Add the url to list and save the url in shelve file"""
        self.ids.linklist.add_widget(CustomListItem(text=text))
        self.ids.linkinput.focus = True
        self.ids.linkinput.text = ''

        # saving to shelve file
        with shelve.open('./save_files/mydata') as shelf_file:
            url_list = shelf_file['url_list']
            url_list.append(str(text))
            shelf_file['url_list'] = url_list

    def on_pre_enter(self):
    '''Load the shelve file with list item from shelve file'''
        try:
            with shelve.open('./save_files/mydata') as shelf_file:
                self.ids.linklist.clear_widgets()
                for item in shelf_file['url_list']:
                    self.ids.linklist.add_widget(CustomListItem(text=item))

        except KeyError:
            with shelve.open('./save_files/mydata') as shelf_file:
                shelf_file['url_list'] = []



class MainApp(MDApp):
    #.....

    # add this function to create two directories at start up
    def on_start(self):
        try:
            os.mkdir('images')
            os.mkdir('save_files')
        except:
            pass

Хорошо, я знаю, что это много кода Но позвольте мне сделать все возможное, чтобы объяснить, что здесь происходит. Мы изменим CustomListItem, чтобы, когда мы удаляем этот виджет, мы также удаляем данные, которые мы сохранили в файле SLEDLES. В AddUrlscreen , мы добавляем код, чтобы сохранить данные в полке файла в списке под названием URL_LIST Отказ Поэтому каждый раз, когда мы нажимаем Enter в текстовом поле, текст сохраняется в файл SLALVE внутри папки с именем Save_files который находится в нашем текущем рабочем каталоге. On_Pre_enter Метод позволяет делать вещи, когда мы навигация на AddUrlscreen . Он загрузит сохраненные URL-адреса и добавить CustomListitem виджеты. on_start Функция создает две папки, когда приложение запускается.

Веб соскоб с красивойSUP4

Я не буду входить в детали BeautifulSoup4. Вы можете найти больше информации об этом в Документация Отказ Сайт, который мы собираемся откинуть Gogoanime . Если вы посетите этот сайт, введите имя аниме в поле поиска, и нажмите Enter, вы будете перенаправлены на страницу результатов. Здесь, если вы нажмете на аниме, вы будете направлены на страницу с деталями этого аниме. URL будет выглядеть так: https://gogoanime.vc/category/name-of-anime Отказ

Это тип URL, который будет использоваться ваше приложение для получения деталей. Щелкните правой кнопкой мыши на сайте и нажмите «Осмотреть». Это откроет небольшой раздел, который позволяет просматривать детали определенных элементов, таких как HTML, используемый для их создания и их классов и так далее. Мы собираемся выбрать изображение, имя аниме, статус и количество эпизодов. Есть много способов выбрать элементы, используя PositionSoup4, я использовал селекторы CSS.

Внутри Scrap.py Добавьте следующий код:

import os,sys
import requests
import shelve
from bs4 import BeautifulSoup

def download_webpage(url):
    try:
        # use requests to get the url text
        res = requests.get(url)
        res.raise_for_status()

        # parse the text to BeautifulSoup
        get_soup_text = BeautifulSoup(res.text, features='lxml')

        # download the image
        print("Downloading")
        download_details(get_soup_text)
    except Exception as e:
        print(e)

def download_details(soup_text):
    # get the anime title
    anime_title = soup_text.select('.anime_info_episodes > h2')[0].getText()
    # get the number of episodes
    episodes = soup_text.select('.anime_video_body ul li .active')[0].get('ep_end')
    # get the status
    completed = True if soup_text.find(title = 'Completed Anime') != None else False
    ongoing = True if soup_text.find(title = 'Ongoing Anime') != None else False
    # get the image
    image_elements = soup_text.select('.anime_info_body_bg img')

    if image_elements == []:
        pass
    else:
        # get image source
        image_url = image_elements[0].get('src')
        image = requests.get(image_url)
        image.raise_for_status()

    ## Save details to shelve file
    with shelve.open('./save_files/mydata') as shelf_file:
        file_name = os.path.basename(image_url)

        shelf_file[anime_title] = {
            'episodes': episodes,
            'completed': completed,
            'ongoing': ongoing,
            'image': file_name
            }

    # save the image
    if os.path.exists(f'./images/{file_name}') != True:
        image_file = open(os.path.join('images', file_name), 'wb')
        for chuck in image.iter_content(100000):
            image_file.write(chuck)
        image_file.close()
    else:
        pass

Я знаю, это много кода Но я обещаю, что мы почти закончили. Этот код имеет две функции, Download_webpage и Download_Details Отказ Download_webpage использует Запросы Для скачивания текста из данного URL. Затем текст анализируется на BeautifulSoup, которая позволяет нам выбрать элементы. Download_Details Затем принимает этот анализируемый текст и выбирает элементы (имя аниме, количество эпизодов и т. Д.). Мы снова используем запросы, чтобы получить источник изображения и загрузить изображение и сохранить его в папке с именем изображения.

И это то, что для веб-соскабливания. Теперь до конца приложения.

Заканчивая заявлением

Модифицировать main.py :

#...
from scrap import download_webpage # new import
from threading import Thread # new import

# modify MainWindow class
class MainWindow(MDScreen):
    #...

    # add new method
    def get_anime_info(self):
        '''Get the anime info and creates a list item widget and adds it to screen'''
        # open shelve files and get the urls
        with shelve.open('./save_files/mydata') as shelf_file:
            url_list = shelf_file['url_list']

        # download data from each url
        for url in url_list:
            download_webpage(url)

        # after downloading the data and saving it shelve file get the data and display it
        with shelve.open('./save_files/mydata') as shelf_file:
            for key in shelf_file.keys():
                if key != 'url_list':
                    print(key)
                    anime = shelf_file[key]
                    episodes = anime['episodes']
                    completed = anime['completed']
                    image = anime['image']

                    anime_complete = 'completed' if completed else 'ongoing'
                    # create a list item with the data
                    list_item = ThreeLineAvatarIconListItem(text=key, secondary_text=f"[b]Status:[/b] {anime_complete}", tertiary_text=f"[b]Episodes:[/b] {episodes}")
                    #add image to the list item
                    list_item.add_widget(ImageLeftWidget(source=f"./images/{image}"))

                    # finally add the list item to screen
                    self.ids.box.add_widget(list_item)

Импортируйте функцию соскабливания из Scrap.py и Нить от резьба Модуль, который мы собираемся использовать позже. Далее мы добавляем новый метод для MainWindow Отказ Этот метод будет нести ответственность за загрузку и отображение информации аниме каждый раз, когда мы обновляем. Во-первых, он собирается получить URL-адреса, которые были сохранены в полке. Это пройдет эти URL для Download_webpage функция. Эта функция будет загружать все данные аниме и сохранить информацию в файле SLALVE. После этого информация извлекается из файла SHALVE и, наконец, элементы списка создаются и добавлены на экран.

Теперь для окончательного дополнения, чтобы все работало.

#main.py

# Modify MainWindow
class MainWindow(MDScreen):
    # modify refresh_callback
    def refresh_callback(self, *args):
        print("Refreshing...")

        # new addition
        def refresh_callback():
            self.ids.box.clear_widgets()

            # call the get_anime_info method
            self.get_anime_info()
            self.ids.refresh_layout.refresh_done()

        anime_thread = Thread(target=refresh_callback)
        anime_thread.start()

Мы изменим main.py добавление Refresh_Callback функция внутри другого Refresh_Callback функция. Теперь, когда мы обновляем (Swipe Down), функция называется, и данные аниме загружаются и отображаются:

Приложение завершено! Теперь вы можете быть в курсе вашего любимого аниме. Сейчас иди, посмотрите кто-нибудь один ударман.

Отказ от ответственности: Это только для чистой образовательной цели. Не используйте это никакого способа, которые могут быть признаны атакующими Gogoanime Отказ

Полный код доступен здесь

Оригинал: “https://dev.to/ngonidzashe/created-an-anime-data-scraping-application-with-kivymd-and-bs4-cm4”