使用Python 3进行网络爬虫、YouTube Data API和MongoDB(收集Hololive预定的视频直播内容 Part 2)

首先

上次(https://qiita.com/kerobot/items/6009cb0db643bceaf4e9)我创建了一个程序,它可以从Hololive和Youtube上收集直播计划和视频信息。
这次我将尝试将收集到的信息保存到MongoDB中。

img01.png

基于前一次的程序,只需添加处理MongoDB的部分,但由于使用了Poetry和pyenv,所以项目结构和程序内容也会全面改变。

虽然也开始调查Azure Cosmos DB(Mongo API),但价格模型不太理解,为了避免意外计费而放弃了。目前考虑只是在Azure虚拟机的Debian上安装MongoDB。

最终我想做的事情

最近一直在浏览虚拟YouTuber制作公司“hololive”等的视频直播计划,并希望能定期将这些计划存储到数据库中,并通过Web API进行访问以在Android应用程序中浏览。

    1. 创建一个程序来收集视频分发计划(上一个)

 

    1. 创建并自动化收集视频分发计划的数据库(本次)

 

    1. 创建一个用于查询存储的视频分发计划的Web API

 

    创建一个用于浏览视频分发计划的Android应用,可以参考Web API。

做过的事情 (zuò guò de shì

这次我们完成了“创建用于存储收集的视频发布计划的数据库并自动化收集”中的阶段2,即数据库的创建。

不久将来,将使用Debian + Python3 + MongoDB重新构建自动化的收集方法。

    1. 准备开发环境

引入 Poetry 和 pyenv 以进行 Python 版本管理和包管理
安装和配置 MongoDB
确认 MongoDB 连接

准备项目

使用 Poetry 创建项目并安装包
使用 pyenv 设置 Python 版本
初始化 git
创建 .gitignore 文件
下载用于网页爬虫的 geckodriver
启用 YouTube Data API v3 以获取 YouTube 视频信息
创建 .env 文件

编写程序(基于前一版本进行修改)

从 .env 文件获取配置信息
获取程序运行时的命令行参数
获取 Hololive Production 直播日程信息
从 YouTube 获取视频信息
输出 CSV 文件

运行程序

创建 launch.json 文件
调试运行程序
执行结果(在 MongoDB 注册的文档)
执行结果(输出的 CSV 文件)
文件结构

准备开发环境

我们已经准备好了下面这个环境的配置。

    • Windows 10 Pro 1909 x64

 

    • Python 3.8.5 x64

 

    • PowerShell 7.0 x64

 

    • Visual Studio Code 1.50.0 x64

 

    • Git for Windows 2.27.0 x64

 

    MongoDB 4.4.1 x64

诗歌和pyenv的引入

参考:在 Windows 10 上安装 Python,使用 Poetry 和 pyenv

> poetry --version
Poetry version 1.1.0

> pyenv --version
pyenv 2.64.2

安装和配置MongoDB。

参考资料:在Windows 10中安装MongoDB并从Python中使用

> mongo --version
MongoDB shell version v4.4.1
Build Info: {
    "version": "4.4.1",
    "gitVersion": "ad91a93a5a31e175f5cbf8c69561e788bbc55ce1",
    "modules": [
        "enterprise"
    ],
    "allocator": "tcmalloc",
    "environment": {
        "distmod": "windows",
        "distarch": "x86_64",
        "target_arch": "x86_64"
    }
}

检查MongoDB连接并创建数据库。

确认能够连接到MongoDB。

> mongo localhost:27017/admin -u admin -p
MongoDB shell version v4.4.1

创建一个数据库并设置角色(本次是dbOwner)。

MongoDB > use holoduledb
MongoDB > db.createUser( { user:"owner", pwd:"password", roles:[{ "role" : "dbOwner", "db" : "holoduledb" }] } );

一旦确认连接成功,便可以退出MongoDB的shell。

MongoDB > exit

项目准备

詩歌和 pyenv

使用诗歌创作项目。

> poetry new holocrawler --name app

进入项目的目录。

> cd holocrawler

为了保险起见,我会更新一下 pyenv。

> pyenv update

在pyenv中查看可安装的Python版本。

> pyenv install -l

在本地(项目目录内)设置要使用的 Python。

> pyenv local 3.8.5

我会确认是否切换成功。

> python -V
Python 3.8.5

如果无法切换到已设置的 Python,请使用 rehash 命令更新 shim。

> pyenv rehash

通过这样做,将更新位于$HOME/.pyenv/shims/下面的命令,并切换到设置的Python版本。

poetry を利用してパッケージを pyproject.toml に追加しインストールします。

> poetry add pylint
> poetry add beautifulsoup4
> poetry add requests
> poetry add selenium
> poetry add lxml
> poetry add google-api-python-client
> poetry add python-dotenv
> poetry add pymongo

パッケージがインストールされるとともに、pyproject.toml が下記のように更新されます。
pyproject.toml にパッケージを記述したうえで poetry install を実行しても良いです。

[tool.poetry.dependencies]
python = "^3.8"
pylint = "^2.6.0"
beautifulsoup4 = "^4.9.3"
requests = "^2.24.0"
selenium = "^3.141.0"
lxml = "^4.5.2"
google-api-python-client = "^1.12.3"
python-dotenv = "^0.14.0"
pymongo = "^3.11.0"

poetry を利用して Python のバージョンを確認してみます。

> poetry run python -V
Python 3.8.5

对git进行初始化

我会初始化 Git。

> git init

创建 .gitignore 文件

创建一个.gitignore文件,并设置要排除的目标。

> ni .gitignore
__pycache__/
*.egg-info/
*.egg
.env
.venv
env/
venv/
.python-version
/geckodriver.log
/geckodriver.exe

下载 geckodriver 以进行网络爬虫

下载geckodriver以便使用Selenium在Firefox上运行。

    1. 下载 geckodriver (geckodriver-v0.27.0-win64.zip)。

 

    解压缩 geckodriver-v0.27.0-win64.zip ,并将 geckodriver.exe 放置在任意位置并配置好路径。

启用YouTube Data API v3以获取YouTube视频信息。

Google Developer Console でプロジェクトを作成し、YouTube Data API v3 を有効化します。

    • Google Developer Console にログイン

 

    • ダッシュボードでプロジェクトを作成

 

    • ライブラリで YouTube Data API v3 を有効化

 

    認証情報で認証情報を作成して APIキー を取得

.env の作成

创建一个用于设置YouTube Data API v3的API密钥和MongoDB连接信息等的文件。

> ni .env
HOLODULE_URL = "<Holodule URL>"
API_KEY = "<Youtube Data API Key>"
API_SERVICE_NAME = "youtube"
API_VERSION = "v3"
MONGODB_USER = "<user>"
MONGODB_PASSWORD = "<password>"
MONGODB_HOST = "<localhost:27017>"

编写程序

保持从.env获取的配置信息的类

创建一个名为 app/settings.py 的文件。

import os
import urllib.request
from dotenv import load_dotenv

class Settings:
    def __init__(self, envpath):
        # .env ファイルを明示的に指定して環境変数として読み込む
        self.__dotenv_path = envpath
        load_dotenv(self.__dotenv_path)
        # 環境変数から設定値を取得
        self.__holodule_url = os.environ.get("HOLODULE_URL")
        if self.__check_url(self.__holodule_url) == False:
            raise ValueError("指定したURLにアクセスできません。")
        self.__api_key = os.environ.get("API_KEY")
        self.__api_service_name = os.environ.get("API_SERVICE_NAME")
        self.__api_version = os.environ.get("API_VERSION")
        self.__mongodb_user = os.environ.get("MONGODB_USER")
        self.__mongodb_password = os.environ.get("MONGODB_PASSWORD")
        self.__mongodb_host = os.environ.get("MONGODB_HOST")

    # ホロジュールのURL
    @property
    def holodule_url(self):
        return self.__holodule_url

    # Youtube Data API v3 の APIキー
    @property
    def api_key(self):
        return self.__api_key

    # Youtube Data API v3 の APIサービス名
    @property
    def api_service_name(self):
        return self.__api_service_name

    # Youtube Data API v3 の APIバージョン
    @property
    def api_version(self):
        return self.__api_version

    # mongodb の ユーザー
    @property
    def mongodb_user(self):
        return self.__mongodb_user

    # mongodb の パスワード
    @property
    def mongodb_password(self):
        return self.__mongodb_password

    # mongodb の ホスト:ポート
    @property
    def mongodb_host(self):
        return self.__mongodb_host

    # 指定したURLにアクセスできるかをチェック
    def __check_url(self, url):
        try:
            with urllib.request.urlopen(url):
                return True
        except urllib.request.HTTPError:
            return False

保存从HoloModule获取的信息的类。

创建app/holodule.py文件。

"""
ホロジュールの配信情報+Youtubeの動画情報含む
"""

class Holodule:
    codes = {
        "ときのそら"  : "HL0001",
        "ロボ子さん" : "HL0002",
        "さくらみこ" : "HL0003",
        "星街すいせい" : "HL0004",
        "夜空メル" : "HL0101",
        "アキ・ローゼンタール" : "HL0102",
        "赤井はあと" : "HL0103",
        "白上フブキ" : "HL0104",
        "夏色まつり" : "HL0105",
        "湊あくあ" : "HL0201",
        "紫咲シオン" : "HL0202",
        "百鬼あやめ" : "HL0203",
        "癒月ちょこ" : "HL0204",
        "大空スバル" : "HL0205",
        "大神ミオ" : "HL0G02",
        "猫又おかゆ" : "HL0G03",
        "戌神ころね" : "HL0G04",
        "兎田ぺこら" : "HL0301",
        "潤羽るしあ" : "HL0302",
        "不知火フレア" : "HL0303",
        "白銀ノエル" : "HL0304",
        "宝鐘マリン" : "HL0305",
        "天音かなた" : "HL0401",
        "桐生ココ" : "HL0402",
        "角巻わため" : "HL0403",
        "常闇トワ" : "HL0404",
        "姫森ルーナ" : "HL0405",
        "獅白ぼたん" : "HL0501",
        "雪花ラミィ" : "HL0502",
        "尾丸ポルカ" : "HL0503",
        "桃鈴ねね" : "HL0504",
        "魔乃アロエ" : "HL0505"
    }

    def __init__(self):
        self.__video_id = ""
        self.__datetime = None
        self.__name = ""
        self.__title = ""
        self.__url = ""
        self.__description = ""

    # キー
    @property
    def key(self):
        _code = Holodule.codes[self.name] if self.name in Holodule.codes else ""
        _dttm = self.datetime.strftime("%Y%m%d_%H%M%S") if self.datetime is not None else ""
        return _code + "_" + _dttm if ( len(_code) > 0 and len(_dttm) > 0 ) else ""

    # video_id
    @property
    def video_id(self):
        return self.__video_id

    @video_id.setter
    def video_id(self, video_id):
        self.__video_id = video_id

    # 日時
    @property
    def datetime(self):
        return self.__datetime

    @datetime.setter
    def datetime(self, datetime):
        self.__datetime = datetime

    # 名前
    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

    # タイトル(Youtubeから取得)
    @property
    def title(self):
        return self.__title

    @title.setter
    def title(self, title):
        self.__title = title

    # URL
    @property
    def url(self):
        return self.__url

    @url.setter
    def url(self, url):
        self.__url = url

    # 説明(Youtubeから取得)
    @property
    def description(self):
        return self.__description

    @description.setter
    def description(self, description):
        self.__description = description

    # ドキュメントへ変換
    def to_doc(self):
        doc = { 'key': str(self.key),
                'video_id': str(self.video_id),
                'datetime' : str(self.datetime.strftime("%Y%m%d %H%M%S")),
                'name' : str(self.name),
                'title' : str(self.title),
                'url' : str(self.url),
                'description' : str(self.description) }
        return doc

一个类用于获取Holodule和YouTube视频信息并将其存储到MongoDB数据库中。

创建一个名为app/holocrawler.py的文件。

"""
【ホロライブ】ホロジュールと Youtube の動画情報を取得して MongoDB へ登録する

1. 事前に geckodriver をダウンロードして配置し PATH を設定しておく
   geckodriver https://github.com/mozilla/geckodriver/releases
2. Google の YouTube Data API v3 を有効化して API キーを取得しておく
   Google Developer Console https://console.developers.google.com/?hl=JA
3. .envファイルを作成し、URLやAPIキーを設定しておく
   参考 : .env.sample
4. pyproject.toml を利用して Python のパッケージを一括インストールしておく
   参考 : pyproject.toml
"""

import sys
import os
import csv
import re
import datetime
import time
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from apiclient.discovery import build  #pylint: disable=import-error
from apiclient.errors import HttpError #pylint: disable=import-error
from pymongo import MongoClient
from urllib.parse import quote_plus
from bson.objectid import ObjectId
from app.settings import Settings
from app.holodule import Holodule

class HoloCrawler:
    def __init__(self, settings):
        self.__driver = None
        self.__wait = None
        # ホロジュールの URL
        self.__holodule_url = settings.holodule_url
        # YouTube Data API v3 を利用するための準備
        self.__youtube = build(settings.api_service_name, settings.api_version, developerKey=settings.api_key, cache_discovery=False)
        # mongodbのユーザー
        self.__mongodb_user = quote_plus(settings.mongodb_user)
        # mongodbのパスワード
        self.__mongodb_password = quote_plus(settings.mongodb_password)
        # mongodbの接続情報
        self.__mongodb_host = "mongodb://%s/" % (settings.mongodb_host)

    # Firefoxプロファイルの設定
    def __setup_profile(self):
        profile = webdriver.FirefoxProfile()
        # その他(参考)
        # profile.set_preference("browser.download.folderList", 1)
        # profile.set_preference("browser.download.dir", "********")
        # profile.set_preference("browser.download.manager.showWhenStarting", False)
        # profile.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/octet-stream-dummy")
        # profile.set_preference("browser.helperApps.alwaysAsk.force", False)
        # profile.set_preference("browser.download.manager.alertOnEXEOpen", False)
        # profile.set_preference("browser.download.manager.focusWhenStarting", False)
        # profile.set_preference("browser.download.manager.useWindow", False)
        # profile.set_preference("browser.download.manager.showAlertOnComplete", False)
        # profile.set_preference("browser.download.manager.closeWhenDone", False)
        return profile

    # Firefoxオプションの設定
    def __setup_options(self):
        options = Options()
        # ヘッドレスモードとする
        options.add_argument("--headless")
        return options

    # ホロジュールの取得
    def __get_holodule(self):
        # 取得対象の URL に遷移
        self.__driver.get(self.__holodule_url)
        # <div class="holodule" style="margin-top:10px;">が表示されるまで待機する
        self.__wait.until(EC.presence_of_element_located((By.CLASS_NAME, "holodule")))
        # ページソースの取得
        html = self.__driver.page_source.encode("utf-8")
        # ページソースの解析(パーサとして lxml を指定)
        soup = BeautifulSoup(html, "lxml")
        # タイトルの取得(確認用)
        head = soup.find("head")
        title = head.find("title").text
        print(title)
        # TODO : ここからはページの構成に合わせて決め打ち = ページの構成が変わったら動かない
        # スケジュールの取得
        holodule_list = []
        date_string = ""
        today = datetime.date.today()
        tab_pane = soup.find("div", class_="tab-pane show active")
        containers = tab_pane.find_all("div", class_="container")
        for container in containers:
            # 日付のみ取得
            div_date = container.find("div", class_="holodule navbar-text")
            if div_date is not None:
                date_text = div_date.text.strip()
                match_date = re.search(r"[0-9]{1,2}/[0-9]{1,2}", date_text)
                dates = match_date.group(0).split("/")
                month = int(dates[0])
                day = int(dates[1])
                year = today.year
                if month < today.month or ( month == 12 and today.month == 1 ):
                    year = year - 1
                elif month > today.month or ( month == 1 and today.month == 12 ):
                    year = year + 1
                date_string = f"{year}/{month}/{day}"
                # print(date_string)
            # ライバー毎のスケジュール
            thumbnails = container.find_all("a", class_="thumbnail")
            if thumbnails is not None:
                for thumbnail in thumbnails:
                    holodule = Holodule()
                    # Youtube URL
                    youtube_url = thumbnail.get("href")
                    if youtube_url is not None:
                        holodule.url = youtube_url
                        # print(holodule.url)
                    # 時刻(先に取得しておいた日付と合体)
                    div_time = thumbnail.find("div", class_="col-5 col-sm-5 col-md-5 text-left datetime")
                    if div_time is not None:
                        time_text = div_time.text.strip()
                        match_time = re.search(r"[0-9]{1,2}:[0-9]{1,2}", time_text)
                        times = match_time.group(0).split(":")
                        hour = int(times[0])
                        minute = int(times[1])
                        datetime_string = f"{date_string} {hour}:{minute}"
                        holodule.datetime = datetime.datetime.strptime(datetime_string, "%Y/%m/%d %H:%M")
                        # print(holodule.datetime)
                    # ライバーの名前
                    div_name = thumbnail.find("div", class_="col text-right name")
                    if div_name is not None:
                        holodule.name = div_name.text.strip()
                        # print(holodule.name)
                    # リストに追加
                    if len(holodule.key) > 0:
                        holodule_list.append(holodule)
        return holodule_list

    # Youtube 動画情報の取得
    def __get_youtube_video_info(self, youtube_url):
        # Youtube の URL から ID を取得
        match_video = re.search(r"^[^v]+v=(.{11}).*", youtube_url)
        video_id = match_video.group(1)
        # Youtube はスクレイピングを禁止しているので YouTube Data API (v3) で情報を取得
        search_response = self.__youtube.videos().list(
            # 結果として snippet のみを取得
            part="snippet",
            # 検索条件は id
            id=video_id,
            # 1件のみ取得
            maxResults=1
        ).execute()
        # 検索結果から情報を取得
        for search_result in search_response.get("items", []):
            # id
            vid = search_result["id"]
            # タイトル
            title = search_result["snippet"]["title"]
            # 説明
            description = search_result["snippet"]["description"]
            # IDとタイトルと説明を返却
            return (vid, title, description)
        return ("","","")

    # ホロジュールのスクレイピングと Youtube 動画情報から、配信情報リストの取得
    def get_holodule_list(self):
        try:
            # プロファイルのセットアップ
            profile = self.__setup_profile()
            # オプションのセットアップ
            options = self.__setup_options()
            # ドライバの初期化(オプション(ヘッドレスモード)とプロファイルを指定)
            self.__driver = webdriver.Firefox(options=options, firefox_profile=profile)
            # 指定したドライバに対して最大で10秒間待つように設定する
            self.__wait = WebDriverWait(self.__driver, 10)
            # ホロジュールの取得
            holodule_list = self.__get_holodule()
            # Youtube情報の取得
            for holodule in holodule_list:
                # video情報
                video_info = self.__get_youtube_video_info(holodule.url)
                # video_id
                holodule.video_id = video_info[0]
                # タイトル
                holodule.title = video_info[1]
                # 説明文(長いので100文字で切っている)
                holodule.description = video_info[2].replace("\r","").replace("\n","").replace("\"","").replace("\'","")[:100]
            # 生成したリストを返す
            return holodule_list
        except OSError as err:
            print("OS error: {0}".format(err))
        except:
            print("Unexpected error:", sys.exc_info()[0])
            raise
        finally:
            # ドライバを閉じる
            self.__driver.close()

    # 配信情報リストのCSV出力
    def output_holodule_list(self, holodule_list, filepath):
        try:
            # CSV出力(BOM付きUTF-8)
            with open(filepath, "w", newline="", encoding="utf_8_sig") as csvfile:
                csvwriter = csv.writer(csvfile, delimiter=",")
                csvwriter.writerow(["key", "video_id", "datetime", "name", "title", "url", "description"])
                for holodule in holodule_list:
                    csvwriter.writerow([holodule.key, holodule.video_id, holodule.datetime, holodule.name, holodule.title, holodule.url, holodule.description])
        except OSError as err:
            print("OS error: {0}".format(err))
        except:
            print("Unexpected error:", sys.exc_info()[0])
            raise
        finally:
            pass

    # 配信情報リストのDB登録
    def register_holodule_list(self, holodule_list):
        try:
            # MongoDB のコレクションからの削除と挿入
            client = MongoClient(self.__mongodb_host)
            db = client.holoduledb
            db.authenticate(name=self.__mongodb_user,password=self.__mongodb_password)
            collection = db.holodules
            for holodule in holodule_list:
                # video_id を条件としたドキュメントの削除
                video_id = holodule.video_id
                collection.delete_one( {"video_id":video_id} )
                # ドキュメントの挿入
                doc = holodule.to_doc()
                collection.insert_one(doc)
        except OSError as err:
            print("OS error: {0}".format(err))
        except:
            print("Unexpected error:", sys.exc_info()[0])
            raise
        finally:
            pass

创建__init__.py文件

创建app/__init__.py文件,并使main.py能够引用与目录名相同的命名空间模块。

from . import settings
from . import holodule
from . import holocrawler

__version__ = '0.1.0'

创建主要模块

创建一个名为main.py的文件。

import sys
import os
import argparse
from os.path import join, dirname
from app.settings import Settings
from app.holocrawler import HoloCrawler

RETURN_SUCCESS = 0
RETURN_FAILURE = -1

def main():
    # parser を作る(説明を指定できる)
    parser = argparse.ArgumentParser(description="ホロジュールのHTMLをSelenium + BeautifulSoup4 + Youtube API で解析して MongoDB へ登録")
    # コマンドライン引数を設定する(説明を指定できる)
    parser.add_argument("--csvpath", help="出力するCSVファイルのパス(任意)")
    # コマンドライン引数を解析する
    args = parser.parse_args()

    # ファイルパスの取得
    is_output = False
    csvpath = args.csvpath
    if csvpath is not None:
        # ディレクトリパスの取得と存在確認
        dirpath = os.path.dirname(csvpath)
        print(f"出力ディレクトリパス : {dirpath}")
        if os.path.exists(dirpath) == False:
            print("エラー : 出力するCSVファイルのディレクトリパスが存在しません。")
            return RETURN_FAILURE
        is_output = True

    try:
        # Settings インスタンス
        settings = Settings(join(dirname(__file__), '.env'))
        # HoloCrawler インスタンス
        holocrawler = HoloCrawler(settings)
        # ホロジュールの取得
        holodule_list = holocrawler.get_holodule_list()
        # ホロジュールの登録
        holocrawler.register_holodule_list(holodule_list)
        # ホロジュールの出力
        if is_output == True:
            holocrawler.output_holodule_list(holodule_list, csvpath)
        return RETURN_SUCCESS
    except:
        info = sys.exc_info()
        print(info[1])
        return RETURN_FAILURE

if __name__ == "__main__":
    sys.exit(main())

在GitHub上注册

我会在GitHub上创建并注册一个存储库。

> git status
> git add .
> git commit -m "first commit."
> git remote add origin https://github.com/********/********.git
> git push -u origin main

执行程序

创建lounch.json文件

在 Visual Studio Code 中创建 launch.json 文件,并设置输出文件路径。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal",
            "args": ["--csvpath","c:\\temp\\holodule.csv"]
        }
    ]
}

程序调试执行

使用 Visual Studio Code 进行调试执行。

如果从命令行执行,则执行如下所示。

> poetry run python main.py --csvpath c:\temp\test.csv

运行结果(已在MongoDB中注册的文档)

以下的文档将被注册到 MongoDB 的集合中。

{ "_id" : ObjectId("5f843cc5c87f84988a7972c4"), "key" : "HL0403_20201011_000000", "video_id" : "y3WPN1uBusg", "datetime" : "20201011 000000", "name" : "角巻わため", "title" : "【マインクラフト】ついにこの時が来た。チェスト整理回!!!【角巻わため/ホロライブ4期生】", "url" : "https://www.youtube.com/watch?v=y3WPN1uBusg", "description" : "チェストォーーー!!!(チェストだけにね本日遊ばせていただいているゲーム(Minecraft)https://www.minecraft.net/ja-jp/※本ゲームは Mojang に確 認を得た上" }
{ "_id" : ObjectId("5f843cc6c87f84988a7972c5"), "key" : "HL0502_20201011_001400", "video_id" : "EgLwctUuu6M", "datetime" : "20201011 001400", "name" : "雪花ラミィ", "title" : "【Dead by Daylight】今日から始めるDBD生活【雪花ラミィ/ホロライブ】", "url" : "https://www.youtube.com/watch?v=EgLwctUuu6M", "description" : "ホロライブ5期生の雪花ラミィ(Yukihana Lamy )です!怖い人からたくさん逃げて脱出するゲームしていきます。感想などは #らみらいぶ #雪花ラミィ で呟いて頂けたら嬉しいです!୨୧┈┈┈┈┈┈" }
{ "_id" : ObjectId("5f843cc7c87f84988a7972c6"), "key" : "HL0G03_20201011_015800", "video_id" : "2Nsp0z1AmV8", "datetime" : "20201011 015800", "name" : "猫又おかゆ", "title" : "【雑談】ゲリラ!新居ではじめての夜更かし【ホロライブ/猫又おかゆ 】", "url" : "https://www.youtube.com/watch?v=2Nsp0z1AmV8", "description" : "ノープランだらだら雑談!?_____________________________________________________________________________ ※僕とみんなのお約" }

执行结果(输出的CSV文件)

当您指定命令行参数并执行时,会生成如下所示的 CSV 文件。

HL0501_20201013_195900,jPP8y696Vkc,2020-10-13 19:59:00,獅白ぼたん,【Far Cry 5】信じて崇めて服従してくれるよねぇ?:#02【ホロライブ/獅白ぼたん】,https://www.youtube.com/watch?v=jPP8y696Vkc,今日はフォールズエンドの攻略とドラブマン砦を攻略してハークを仲間にしたい感じ!本ゲームは Ubisoft に確認を得た上、Ubisoft Video Policy (https://www.ubiso
HL0305_20201013_195900,mEsDSRfV95U,2020-10-13 19:59:00,宝鐘マリン,【歌ってみた】青春を感じる!!古めの歌枠/Boomer Song【ホロライブ/宝鐘マリン】,https://www.youtube.com/watch?v=mEsDSRfV95U,サムネイラスト…火ノ❅ホロケット ホ18 @soraneko_hinoお借りしている音源↓・一味の有志の方の製作カラオケ・カラオケ歌っちゃ王 様▷https://www.youtube.com/cha
HL0504_20201013_200000,MJt3YMbAxkM,2020-10-13 20:00:00,桃鈴ねね,【making】✨お礼イラストメイキング✨【ホロライブ/桃鈴ねね】,https://www.youtube.com/watch?v=MJt3YMbAxkM,「せんちょーおすみつきねねは天才神!!!!」お名前呼ばれんかった!!!!って人はねねのリプ欄にアピールしにきてね(;_;)ごめんね(;_;)いつも応援ありが?※日本語以外の言語だと大いに読み間違えてい
...

文件结构

holocrawler
├ .venv
├ .vscode
|  └ launch.json
├ app
|  ├ __init__.py
|  ├ holocrawler.py
|  ├ holodule.py
|  └ settings.py
├ .env
├ .gitignore
├ .python-version
├ geckodriver.exe
├ geckodriver.log
├ main.py
├ poetry.lock
├ pyproject.toml
└ README.md

最后

我已经完成了在”我想做的事情2″中的”创建数据库以及自动化收集用于储存已采集视频发布计划的数据”的部分,只是完成了数据库的创建。

上次以来的变化并不大,但对我个人而言,能够使用Poetry和pyenv是一个成果。

我打算在Azure虚拟机上构建定期执行环境,并创建一个用于参考MongoDB文档的Web API。

广告
将在 10 秒后关闭
bannerAds