PythonでWebスクレイピング ~REDMINEからの情報抽出とエクセル出力~

python,Webスクレイピング,webスクレイピング

会社でredmine使っているのだけど、見やすく情報を抽出したかったので検討してみました。

pythonを使ってredmineをスクレイピングし、各チケット情報をエクセルに出力します。

redmine用のライブラリ(Python-Redmine)があるらしいですが、今回は使いません。

環境構築

まずは試験用に、ローカルPC(windows)へのredmineの環境構築を行います。

redmineのインストール

調べてみたらwindows用のパッケージが2023年8月から非サポートとなっていました。

じゃあインストーラが手に入らないのかというとそんなことはなく、以下公式の分かりづらいところから引っ張れます。

https://github.com/bitnami/vms/issues/690

以下サイトを参考にしました。

redmine起動

スクレイピングさえできればいいので環境周りはざっくりと。

インストールが完了したら以下が表示されます。そのうちの「サービス開始」をクリックしておく。

ブラウザで「127.0.0.1/redmine」にアクセスすることでおなじみの画面が出てきます。

まず右上のログインボタンから、インストール時に設定したユーザ名とパスワードでログインしましょう。

そうすると左上に「管理」が出てきてプロジェクトを追加できます。

プロジェクトの作成方法やチケットの書き方などは割愛。以下は今回の試験用に作ったプロジェクトです。

redmineのスクレイピング

各チケットの情報を実際にとってみます。

src

import time
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
import pandas as pd
import datetime
import pprint

chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation",'enable-logging'])


# スクレイピングメイン処理
def main():
    driver = webdriver.Chrome(ChromeDriverManager().install(), options=chrome_options)
    driver.get("http://127.0.0.1/redmine/projects/scraping/issues/gantt")
    # ログイン用にwait
    print("ログインを完了し、ガントチャートを開いてください")
    for try_cnt in range(20):
        print(try_cnt)
        time.sleep(1)
    # チケット情報取得
    get_redmine_inf(driver)
    driver.quit()
    return


# チケット情報取得
def get_redmine_inf(driver):
    ticket_url_list = []
    ticket_info_dec = {"ID": "","チケット名": "","ステータス": "","優先度": "","担当者": "","開始日": "","期日": "","進捗率": "",}
    excel = make_excel(ticket_info_dec)
    # ガントチャートから各チケットのURLを取得
    gnt_all_ticket = driver.find_elements(By.CSS_SELECTOR,'.issue-subject.hascontextmenu')
    for gnt_ticket in gnt_all_ticket:
        ticket_url = gnt_ticket.find_element(By.TAG_NAME,'a').get_attribute('href')
        ticket_url_list.append(ticket_url)
    # チケットの情報取得
    for ticket_url in ticket_url_list:
        driver.get(ticket_url)
        time.sleep(1)
        # ID
        ticket_info_dec["ID"] = ticket_url.split("/")[-1]
        # チケット名
        ticket_info_dec["チケット名"] = driver.find_element(By.CSS_SELECTOR,'.subject').find_element(By.TAG_NAME,'h3').text
        # ステータス
        ticket_info_dec["ステータス"] = driver.find_element(By.CSS_SELECTOR,'.status.attribute').find_element(By.CSS_SELECTOR,'.value').text
        # 優先度
        ticket_info_dec["優先度"] = driver.find_element(By.CSS_SELECTOR,'.priority.attribute').find_element(By.CSS_SELECTOR,'.value').text
        # 担当者
        ticket_info_dec["担当者"] = driver.find_element(By.CSS_SELECTOR,'.assigned-to.attribute').find_element(By.CSS_SELECTOR,'.value').text
        # 開始日
        ticket_info_dec["開始日"] = driver.find_element(By.CSS_SELECTOR,'.start-date.attribute').find_element(By.CSS_SELECTOR,'.value').text
        # 期日
        ticket_info_dec["期日"] = driver.find_element(By.CSS_SELECTOR,'.due-date.attribute').find_element(By.CSS_SELECTOR,'.value').text
        # 進捗率
        ticket_info_dec["進捗率"] = driver.find_element(By.CSS_SELECTOR,'.progress.attribute').find_element(By.CSS_SELECTOR,'.value').text
        pprint.pprint(ticket_info_dec)
        excel.add_inf(ticket_info_dec)
    # エクセルで保存
    now = datetime.datetime.now()
    file_name = '{}.xlsx'.format(now.strftime('%Y%m%d_%H%M%S'))
    excel.save_file(file_name,"抽出結果")


class make_excel:
    # エクセルヘッダ記入
    def __init__( self , init_dic) :
        key_list = init_dic.keys()
        self.df = pd.DataFrame(columns=key_list)
    # 行追加
    def add_inf ( self , add_dict ) :
        self.df = self.df.append( add_dict , ignore_index=True)
        return
    # ファイル保存
    def save_file( self , file_name , title ):
        self.df.to_excel(file_name, sheet_name=title)
        return


if __name__ == "__main__":
    main()

出力結果

こんな感じでエクセルで出力されます。

src解説

ログイン用のwait

ログイン用に20秒のwaitを実装しました。この間に手動でログインしておく必要があります。

やろうと思えば自動ログインも出来なくはないのですが、今時会社なんかだと2段階認証が一般的だと思うので、ここら辺も考慮してユーザが主導でログインまではもっていくようにしました。

必要あればwait時間はより大きめにしてもいいと思います。

また、wait中にガントチャート画面を開いておく必要があるのですが、手動でガントチャートにフィルタをかけておくことで必要なチケット情報のみ抽出させることも出来ます。

    # ログイン用にwait
    print("ログインを完了し、ガントチャートを開いてください")
    for try_cnt in range(20):
        print(try_cnt)
        time.sleep(1)

情報抽出

以下画面上の情報のみスクレイピングしていますが、取ろうと思えばほか情報(説明、コメントなど)もスクレイピング可能です。

エクセル出力

この辺は過去記事参照してください。

最後に

あくまでローカル環境での動作確認のため、クラウド上などでは勝手が違うかもしれません。

もし利用いただく場合は、対象の環境や取りたい情報に応じてカスタマイズいただければと思います。