automagicaことはじめ

# automagicaことはじめ

## automagicaとは

`automagica`を利用すると、最近流行のRPA(Robotics Process Automation)をpythonで実施することができる。
`Autohotkey`も類似のツールだが、こちらは独自のスクリプト言語でwindowsに特化した命令を多数持っており歴史も長い。日本語の情報も充実している。
そのような状況の中で`automagica`を利用する利点は、以下のようなものが挙げられるだろう。

* windows操作に限定しない様々な自動化処理関数を持っている。というのも、`automagica`自体は他のオープンソースのライブラリを統一的にかつ簡単に利用できるようなラッパー関数を提供しているもの。
* pythonで書ける。
* `Autohotkey`は単にwindows操作のスクリプト化しかできないが、`automagica`はwebページをhtml構文解析を行い、タグなどから必要な情報を取得する、といったことができる。(実際は基盤になっている`Selenium`の機能だが)
* マウス、キーボードの操作、エクセルの操作なども可能でこれらをシームレスに扱える。

今回は、上述のweb解析機能を使って、webページから必要な情報を取得してそれをエクセルにコピーする、という機能を実現する。

## 実現すること

* 論文検索ページで任意の検索ワードを設定し検索する
* 検索結果の任意の要素(ここでは論文名、文献、著者名、ページ数)を取得
* 取得した情報をエクセルファイルとして保存

## automagicaのインストール

`automagica`は`pip`では最新版が入れられないようだ。githubから`pip`で入れるためには以下のようにする。
```
pip install git+https://github.com/OakwoodAI/automagica/
```
`from automagica import *`のようにして使おう。

## ブラウザをコントロールする

### excelファイルとブラウザの立ち上げ

`automagica`では先述の通りHTML構造から任意の要素を取得する、という使い方ができるが、まず以下のようにしてブラウザを動かす。
`target_URL`は論文検索システムCiniiを今回は指定する。

```python

    # excel open
    excel_path = this_excel_path
    ExcelCreateWorkbook(path=excel_path)

    # Open Chrome
    browser = ChromeBrowser()

    # Browse target URL
    target_URL = 'https://ci.nii.ac.jp'
    browser.get(target_URL)

```

`automagica`がベースにしている`selenium`は、操作するブラウザーとしてgooglechrome、firefoxまたはedgeを用いることができる。
chromebrowserというのがvivaldiとかも対象にしているのかは分からない。
ともかく作成したインスタンス(ここでは`browser`)にHTMLの構造が取得されるので、これに操作していく。

excelの操作は`ExcelCreateWorkbook`でまずworkbookを作成する。

### 検索ワードを入力して実行する

以下のようなコードを実行する。

```python

    ## Enter Search Text
    search_word = "hoge"
    browser.find_element_by_name('q').send_keys(search_word)
    ## search実行ボタンの取得
    exec_search = browser.find_element_by_xpath('//*[@id="article_form"]/div/div[1]/div[2]/div[2]/button')
    ## searchボタンを押す
    exec_search.click()

```

`find_element_by_name`はタグの`name`で指定されている要素名で探す。
この場合は、検索ワードの入力部分が、`<input name="q">`となっている。名前がqだということだ。

このように該当ページの取得したいHTMLの要素がどのように一意に特定できるのかは、試行錯誤する必要がある。

検索ワード部分を指定したらそこにテキスト入力するメソッドが、`send_keys()`になるので、引数に検索ワードを入れる。

検索ワードが入力できたら検索実行ボタンを押す。ここでは`find_element_by_xpath`で指定している。
Xpathは[こちら](https://qiita.com/rllllho/items/cb1187cec0fb17fc650a)によると、

```
XPathはXML文章中の要素、属性値などを指定するための言語です。
XPathではXML文章をツリーとして捉えることで、要素や属性の位置を指定することができます。
HTMLもXMLの一種とみなすことができるため、XPathを使ってHTML文章中の要素を指定することができます。
```

ということ。イメージとしてはファイルをドライブルートから絶対パスで要素を指定するのと同じような形で、
ある要素のHTMLの中での場所を指定するような形、かと思う。

これはchromeであれば右クリック→検証を行うと、取得したい要素のHTML上の表記がわかるので、
その上で右クリックして`Copy->Copy XPath`で取得できるので、それを引数に入れる。

便利だけど、ファイルの絶対パス指定と同じで少しでも構造が変わってしまうと指定できなくなる柔軟性の低さもある。
この辺りは使いわけていく必要あり。

指定できたら、そのオブジェクトの`click()`メソッドでボタンを押すのと同様の効果が得られる。

### ページ結果の取得

1ページには複数件の文献検索結果が表示される。各文献毎に内容を取得して文献数だけループする。

`find_elements_by_class_name`は引数の名前のクラス名を持つ全要素のリストを取得する。
ここではリストの各要素でループを実行することで、各論文のコンテンツにアクセスしている。
`find_element_by_class_name`は全要素ではなく単一の要素を返す。一番最初にヒットしたもののようだ。
`find_element_by`は他にも`tag_name`など様々なものが使えるので、HTMLを見て使いやすいものを使用するのがいいと思う。

excelに対する結果の書き込みは`ExcelWriteRowCol`で行う。開いたexcelファイルのパス、かきこむ場所の行数、列数、書き込む内容、となっている。

```python

# 各論文のコンテンツを示すelementを1ページ内の塊、elementsを取得
this_articles = browser.find_elements_by_class_name('paper_class')

# elements内の各要素elementでループ

for this_article in this_articles:

		# 通し番号を増加
		total_index += 1

		# タイトルの取得
		this_link = this_article.find_element_by_tag_name('a')
		this_title = this_link.text
		link_text = this_link.get_attribute("href")

		# 著者の取得
		this_authors = this_article.text.split("\n")[1]

		# ジャーナルデータの取得
		this_journal_data = this_article.find_element_by_class_name('journal_title').text

		# 結果の保存
		# print(total_index,"\n", this_title,"\n", this_authors,"\n",link_text,"\n", this_journal_data)

		# excelファイルに書き込み
		ExcelWriteRowCol(excel_path, r=total_index, c=1, write_value=total_index)
		ExcelWriteRowCol(excel_path, r=total_index, c=2, write_value=this_title)
		ExcelWriteRowCol(excel_path, r=total_index, c=3, write_value=this_authors)
		ExcelWriteRowCol(excel_path, r=total_index, c=4, write_value=link_text)
		ExcelWriteRowCol(excel_path, r=total_index, c=5, write_value=this_journal_data)

## 最終ページでなければ「次へを押す」、最終ページならば終りにする
if this_page != count_next_button:

		browser.find_element_by_xpath('//*[@class="paging_next btn pagingbtn"]').click()
		this_page += 1
else:
		print("正常に終了しました")
		break

```


### まとめ

`automagica`は操作自動化に関する様々なモジュールを統一的に扱うということでとっつきやすくはあるが、まだ開発途上なのかドキュメントにも命令に対する説明が不足しているところがあって使いにくいところもある。

なので現段階では`automagica`で例えば上記で取り上げたwebスクレイピングをとりあえずやってみて、本格的に実施する時は`Selenium`を使用するとかするのがいいのかもしれない。僕も`automagica`のおかげで、pythonで実施できる自動化モジュールのことを知ることができた、という側面はあるけど、`automagica`にこだわる必要はあるのかな？と思っているところだ。

もしかしたら`Automagica`は、必要最小限の機能を簡単に使ってもらう、というところに開発の主眼があるのかもしれない。

今後は`automagica`で使用しているモジュール群に関心を向けて、自動化へのとりくみを行っていきたい。
以下にコードの全文を載せておくので、参考になれば幸いです。

### 論文検索とエクセル取得コードの全文

```python

# -*- coding:utf-8 -*
from automagica import *

def cinii_auto_search(this_excel_path, this_search_word):
    ''' 保存するexcelのパス・名前と検索語を入力して
    automagicaによる自動検索、保存を行う'''

    def get_loop_number(browser)->int:
        '''検索ヒット数と位置ページ表示数から「次へ」を押す回数を設定'''
        # search wordでの検索件数の取得
        this_header = browser.find_element_by_class_name('heading')

        # 取得できたelementからヒット件数と一ページあたりの表示数を抽出する
        ## 空白で区切られているのでリスト取得
        this_text = this_header.text.split()
        ## 総件数は2つ目、一回の表示数は3つ目の後半
        hit_num_list_index = 1
        search_hit_num = int(this_text[hit_num_list_index][:-2]) # 末尾の「件中」を除いている
        page_view_num_index = 2
        page_view_num = int(this_text[page_view_num_index].split("-")[1])

        # 「次へ」を押下する回数は、既に1ページ目が表示されているので、
        # 総ページ数 // 一ページ件数 で求まる商となる
        count_next_button = search_hit_num // page_view_num
        return count_next_button

    # excel open
    excel_path = this_excel_path

    ExcelCreateWorkbook(path=excel_path)


    # Open Chrome
    browser = ChromeBrowser()

    # Browse target URL
    target_URL = 'https://ci.nii.ac.jp'
    browser.get(target_URL)

    # phase 1 : enter search word
    ## Enter Search Text
    search_word = this_search_word
    browser.find_element_by_name('q').send_keys(search_word)
    ## search実行ボタンの取得
    exec_search = browser.find_element_by_xpath('//*[@id="article_form"]/div/div[1]/div[2]/div[2]/button')
    ## searchボタンを押す
    exec_search.click()

    # phase 2 : ページループの取得
    count_next_button = get_loop_number(browser)

    # 変数設定
    this_page = 0 # 表示ページ。0始まり
    total_index = 0 # 検索articleの通し番号。表示は1始まり

    # phase 3 : 1ページの結果の取得、ループ
    while this_page <= count_next_button:
    #for i in range(0,2):


        # 各論文のコンテンツを示すelementを1ページ内の塊、elementsを取得
        this_articles = browser.find_elements_by_class_name('paper_class')

        # elements内の各要素elementでループ

        for this_article in this_articles:

            # 通し番号を増加
            total_index += 1

            # タイトルの取得
            this_link = this_article.find_element_by_tag_name('a')
            this_title = this_link.text
            link_text = this_link.get_attribute("href")

            # 著者の取得
            this_authors = this_article.text.split("\n")[1]

            # ジャーナルデータの取得
            this_journal_data = this_article.find_element_by_class_name('journal_title').text

            # 結果の保存
            # print(total_index,"\n", this_title,"\n", this_authors,"\n",link_text,"\n", this_journal_data)

            ExcelWriteRowCol(excel_path, r=total_index, c=1, write_value=total_index)
            ExcelWriteRowCol(excel_path, r=total_index, c=2, write_value=this_title)
            ExcelWriteRowCol(excel_path, r=total_index, c=3, write_value=this_authors)
            ExcelWriteRowCol(excel_path, r=total_index, c=4, write_value=link_text)
            ExcelWriteRowCol(excel_path, r=total_index, c=5, write_value=this_journal_data)
        
        ## 最終ページでなければ「次へを押す」、最終ページならば終りにする
        if this_page != count_next_button:

            browser.find_element_by_xpath('//*[@class="paging_next btn pagingbtn"]').click()
            this_page += 1
        else:
            print("正常に終了しました")
            break
		
```