【RPA例題】HTMLタグやアトリビュートをAutomation Anywhereと正規表現を使って簡単に整える (HTMLピーラー)

# 概要
複雑なHTMLページからリンクのリストなどの単純な情報を引き抜きたいときに、よく該当部分をブラウザで選択してMicrosoft Wordにコピペして、HTMLとして保存 (フィルターあり)するという手法を使います。Wordに一度ペーストするとWord独特のスタイルがタグに入ってしまうため、フィルターを通して保存することでだいぶ装飾が軽減されるのですが、しかし、ツール特有のクセで変なところで改行が入ったり、特定のアトリビュートが残っていたりと、バルクで削除したいときがあります。

Javscriptや正規表現対応のテキストエディタ等で正規表現を使って削除する方法もありますが、今回は、繰り返し同じパターンの削除を簡単に実行できるように、RPAである**Automation Anywhere Enterprise 11.3.3**を使ってRPAで対応ロジックを組んでみましょう～

**WordのHTMLとして保存 (フィルターあり)オプション**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/63c95e40-fe1c-211c-4121-d46b0eb61fb9.png)

**生成されるHTMLの例**:改行が変なところで入っていたり、行の途中にもインデントがスペースで入っている、余計なclass、styleなどのアトリビュートやspanなどのタグがある。

```HTML:input.htm
<ul style='margin-top:0mm' type=disc>
 <li class=MsoNormal><span lang=EN-US><a
     href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/distributed-architecture.html"><span
     lang=EN-US><span lang=EN-US>分散アーキテクチャ</span></span></a></span></li>
 <li class=MsoNormal><span lang=EN-US><a
     href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/security-architecture-model.html"><span
     lang=EN-US><span lang=EN-US>セキュリティ</span></span><span lang=EN-US><span
     lang=EN-US> </span></span><span lang=EN-US><span lang=EN-US>アーキテクチャ</span></span><span
     lang=EN-US><span lang=EN-US> </span></span><span lang=EN-US><span
     lang=EN-US>モデル</span></span></a></span></li>
 <li class=MsoNormal><span lang=EN-US><a
     href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/data-protection-and-access-control.html"><span
     lang=EN-US><span lang=EN-US>データ保護とアクセス制御</span></span></a></span></li>
 <ul style='margin-top:0mm' type=circle>
  <li class=MsoNormal><span lang=EN-US><a
      href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/independent-control-planes-bot-creators-and-bot-runners.html">Bot
      Creator <span lang=EN-US><span lang=EN-US>と Bot Runner </span></span><span
      lang=EN-US><span lang=EN-US>の独立したカテゴリ</span></span></a></span></li>
  <li class=MsoNormal><span lang=EN-US><a
      href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/role-based-access-control-rbac.html"><span
      lang=EN-US><span lang=EN-US>ロールベースアクセス制御</span></span></a></span></li>
```

# 対象ファイルの読み書き

あらかじめ空の変数を3つ作成しておきます。

* `$Body$` (読み込んだテキスト全体)
* `$LastChar$` (レコードの最後の文字)
* `$Separator$` (レコードをつなぐ際に間に挟む文字)

まず最初に対象のHTMLファイルをテキストファイルとしてAutomation Anywhere Enterprise Client (AACE)に読み込みます。それには**「Read from CSV/Text」コマンド**を使います。このコマンドをアクションリストにドラッグ＆ドロップしたら「テキスト」を選んで、開くファイルを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/2d0a5967-8ef5-51e0-d0c1-f8e163b8c585.png)

>注意点:
>
>* テキストファイルの読み込みは拡張子が.txtである必要があります。.htm等他の拡張子の場合は、あらかじめファイル名を変更しておくか、ボットの中でファイル名変更を行います。(今回はあらかじめ変更してあるものとします)

すると、LOOPコマンドとともにコマンドが生成されます。このループの中では、3行目のコメントに記載がある通り、システム変数の`$Filedata Column(1)$`を使って読み込んだテキストファイルの中身を参照できます。(改行でレコードが区切られているので、レコードがなくなるまでループして、変数`$Body$`につなげていきます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/06621973-5968-abe8-cb58-7314e9bdd7df.png)

つまりこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/31e35742-948d-2248-ba0c-e6b2a8a8f014.png)

ただし、これだけだと、改行も、場合によっては改行の場所で本来スペースが入るべきところが入らず単語がつながってしまいます。そのため、間に`$Separator$`という変数を挟んで、この値を空白、改行、半角スペース、というように適宜変えて変数`$Body$`につなげていきます。

図でロジックを表すとこんなかんじです(^_-)-☆
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/a20f35c0-dc40-b1ce-ee81-2a76591fbc38.png)

これを実装すると以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/31b645f5-6177-c55d-539b-e20b7fd592c6.png)

>ちなみに、空白文字や改行文字の変数等への設定方法は「[Automation Anywhere で変数に特殊文字を代入するには？](https://qiita.com/Okura_/items/e5928de7b3ddb9ba544a)」を参照してください。

これで、変数`$Body$`にHTMLファイルの内容が(閉じタグ後の改行付きで)読み込まれました。

# String Operationコマンドの「Replace」による正規表現による置換

さて、テキストファイルの内容が変数`$Body$`に読み込まれたので、以後は、この`$Body$`の中身を正規表現で加工していくことにします。分かりやすくするため、正規表現の書式は<font color="red">赤文字</font>で書くことにします。

String OperationコマンドでReplaceを選択、「ソース文字列」「出力を変数に割り当て」には両方とも`$Body$`を指定、あとは「検索」「置換」に入れるパターンを変えていけば、文字列のパターンマッチによる置換が成立します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/5ffc1144-9894-4015-68c8-f0cd696003f5.png)

>**メモ:** ここでは、この後使う正規表現のメタ文字を簡単に解説します。ちなみに、Automation Anywhereでは巷で出回っている正規表現の実装はほぼカバーされているようです。
>
>|メタ文字|説明|
|:--|:--|
|<font color="red">\*</font>|直前の文字の0回以上の繰り返しにマッチする繰り返し文字。同等の正規表現で書くと<font color="red">{0,}</font>|
|<font color="red">+</font>|直前の文字の1回以上の繰り返しにマッチする繰り返し文字。同等の正規表現で書くと<font color="red">{1,}</font>|
|<font color="red">?</font>|直前の文字の0回または1回の繰り返しにマッチする繰り返し文字。同等の正規表現で書くと<font color="red">{0,1}</font>。また、繰り返し文字はデフォルトではマッチしうる最大の文字列にマッチするようになっています。繰り返しのメタキャラクタの後ろに <font color="red">?</font>を付けるとマッチしうる最小の文字列にマッチするようになります。(例: <font color="red">+?</font>)|
|<font color="red">\w</font>|半角英数字 or \_ 。同等の正規表現で書くと<font color="red">[a-zA-Z_0-9]</font>|
|<font color="red">[]</font>|カギカッコ内のいずれかの文字。|
|<font color="red">()</font>|パターンのグループ化。「置換」部分で<font color="red">\$1</font> (1番目のグループパターン)、<font color="red">\$2</font> (2番目のグループパターン)...のように参照が可能となる。|
|<font color="red">[^]</font>|次に続く文字以外。カギカッコの外で使われると別の意味 (行頭)になるので注意。|
|<font color="red">｜</font>|パイプ。ORの表現に使います。パイプで並列された、それぞれの正規表現パターンのいずれかが一致すれば、マッチングが成立します。|
|<font color="red">(?!)</font>|否定先読み (Negative lookahead)。マッチする文字列に含めたくない条件がある場合に使います。<font color="red">look(?!ahead)</font>: lookの先にaheadがない場合に、lookにマッチさせます。|
>
>尚、正規表現のより包括的な解説は、たとえば以下の記事を参照してください。
>
>* **入門編:**[初心者歓迎！手と目で覚える正規表現入門・その１「さまざまな形式の電話番号を検索しよう」](https://qiita.com/jnchito/items/893c887fbf19e17d3ff9)
>* **リファレンス:**[正規表現あれこれ](https://qiita.com/ikmiyabi/items/12d1127056cdf4f0eea5)
>* **よく使うパターン:**
>     * [正規表現を可視化してまとめたチートシート](https://qiita.com/sea_ship/items/0b35b5c1c98eebfa5128)
>     * [form用正規表現判定/備忘](https://qiita.com/fubarworld2/items/9da655df4d6d69750c06)
>     * [HTML5におけるinput要素のpattern、type属性のおさらい](https://qiita.com/ka215/items/795a179041c705bef03b)
>
>注意点:
>
>* Automation AnywhereではJavascript等で使われる修飾子<font color="red">\g</font> (グローバルマッチ) は使えません。 (使う必要がありません！) Automation AnywhereのReplaceコマンドでの正規表現による置換では<font color="red">\g</font>がついているのと同等の動作、つまり文字列中を何回もマッチングを行います。

それでは、HTMLファイルの整形のために、具体的にパターンマッチによる置換を指定していきます。

### 1.半角英数字の間の2文字以上の半角スペースを1文字半角スペースに置換

たとえば

```HTML:
<a        href="...
```
を

```HTML:
<a href="...
```
に変換します。

**ダイアログボックスに指定する値:**

**検索:**3つのパターンマッチ (半角英数字 or \_)(1文字以上の半角スペース)(半角英数字 or \_)を検索

```
(\w)( +)(\w)
```
**置換:**見つかった(半角英数字 or \_)の間に半角スペースを1つ入れた文字列で置換

```
$1 $3
```

### 2.ul、ol、li、a、br、img以外のタグをすべて削除
まず、不要なタグを削除しましょう。ここではul、ol、li、a、br、img以外のタグを削除します。たとえばspanやbはすべて削除されることになります。

**ダイアログボックスに指定する値:**

**検索:**否定先読み (Negative lookahead)を使って、指定したタグ以外のタグをマッチします。否定先読みの内側では OR (<font color="red">|</font>) の指定ができます。

```
(?!<\/?(ul|ol|li|a|br|img)(>|\s[^>]*>))<("[^"]*"|\'[^\']*\'|[^\'">])*>
```
**置換:**空白にしておく。つまり検索でマッチした場合は削除。

```

```

### 3.タグからhref、src以外のアトリビュートを削除
タグを軽量化するために、どうしても必要な情報を保持している`<a href>`、`<img src>`アトリビュート以外は削除してしまいます。たとえば、class、style、typeなどのアトリビュートはすべて削除されることになります。

正規表現を作る際に、「～以外を削除」のように汎用的に作成するのが難しかったので、「消したいアトリビュートを指定して消去」する構文を複数回実行する方針に変更しました。特定のアトリビュートを削除するには以下の指定を行い、複数回Replaceコマンドを実行します。

今回は、class、style、typeアトリビュートを削除するように3回実行することにします。

**ダイアログボックスに指定する値:**

**検索:**"style"アトリビュートを削除したい場合。イコールの後がシングルクオート/ダブルクオートで囲われている場合、または囲われていない場合の両方に対応しました。アトリビュートの中身の文字列の指定は、"<"、"="、">"はぬかし、かつ、マッチしうる最小の文字列にマッチするよう<font color="red">+?</font> を指定しておくのがポイントです。

```
(<.*) +style *= *[\u0021-\u003b\u003f-\u007E]+?(>| )
```
**置換:**上記でカッコが付いた部分だけを残し、間のアトリビュートに当たる部分は削除するように指定します。

```
$1$2
```

## 完成
以上のことをすべて踏まえた最終的なアクションリストは以下の通りとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/ce49b073-b065-35ae-915f-09c534b64b9c.png)

これを実行すると、出力されたresult.txtの中身を見ると、たしかに余計なタグやアトリビュートが取り払われ、HTMLがシンプルできれいになっていることがわかります！

```HTML:result.txt
<ul>
 <li><a href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/distributed-architecture.html">分散アーキテクチャ</a></li>
 <li><a href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/security-architecture-model.html">セキュリティ アーキテクチャ モデル</a></li>
 <li><a href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/data-protection-and-access-control.html">データ保護とアクセス制御</a></li>
 <ul>
  <li><a href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/independent-control-planes-bot-creators-and-bot-runners.html">Bot Creator と Bot Runner の独立したカテゴリ</a></li>
  <li><a href="https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/security-architecture/role-based-access-control-rbac.html">ロールベースアクセス制御</a></li>
```

[FormatHTML.atmxをGitHubからダウンロード](https://github.com/o-kura/AutomationAnywhere/blob/e3fee41c60cbbff7eab6fc1f27d98d214ee45791/v11/FormatHTML.atmx)

# 最後に
Automation Anywhereにおける正規表現でのテキスト置換は、マスターするととても便利です(^_-)-☆
いろいろな正規表現を覚えて試してみると、いろいろと応用範囲が広がりそうです。