【Vue.js, Express, Python】HTMLからテキストとWordファイルを自動生成する

# 課題、目的

今まで毎週Wordで書いてきたレポートを、最後に一つのWordファイルにまとめて提出する、という謎の課題が期末に出された。
しかし私は、Wordファイルが増えるのが嫌なので、Wordファイルで作成したレポートを、はてなブログにまとめて保存していた。
はてなブログからWordにコピペしようとしたのだが、Wordファイルの表示がaタグなどがじゃまできれいにならない。
<img width="789" alt="スクリーンショット 2020-07-24 18.59.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/a04cac9d-e4d0-441c-125d-375bfe93b336.png">
これらを手作業で直すのはあまりに億劫である。
そこで、**HTMLファイルをテキストに変換**すればうまくいきそう、と思い、変換サイトを見つけたが、せっかくなので自分で作ってみることにした。
ついでに、勉強と経験のために**変換されたテキストを元にWordファイルを自動生成**することにした。

文章が長すぎると404エラーになったり、他にもまだバグがあるかもしれないが、コードの整理をする前に頭の整理をするため、記事を書いていく。

# 完成形

https://github.com/wafuwafu13/Word-Creater

<img width="678" alt="スクリーンショット 2020-07-27 14.06.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/36ca024b-e1cc-f2ac-e405-0b6d4cc2cf82.png">


## 1 デザイン

<img width="1413" alt="スクリーンショット 2020-07-27 12.21.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/902075a0-c550-8ca6-142f-dd45a486bb27.png">

## 2 HTML => TEXT 変換

<img width="1431" alt="スクリーンショット 2020-07-27 12.22.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/23a38e3a-7088-db89-7539-f847d35f5b67.png">

## 3 TEXT保存

<img width="738" alt="スクリーンショット 2020-07-27 12.24.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/9c652e9f-fbee-57c0-f8e6-ff22095826a1.png">


## 4 Wordファイル作成

<img width="735" alt="スクリーンショット 2020-07-27 12.25.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402245/3059c9be-63a9-17b9-733d-390b091a59df.png">


# 実装

## 1 デザイン

[Vuetify](https://vuetifyjs.com/ja/)を用いた。

## 2 HTML => TEXT 変換

[JavaScriptでHTMLタグを削除する正規表現](https://qiita.com/miiitaka/items/793555b4ccb0259a4cb8) を実装した。

## 3 TEXT保存

保存すべきカラムは、名前(name)とテキスト(text)である。
axiosを使ってVueからExpressにクエリパラメータとして両者を送信し、ExpressからMySQLに保存することにした。
Node.jsでMySQLに接続する方法は
[Node.jsアプリケーションとMySQLを接続しよう！](https://prog-8.com/docs/nodejs-mysql)
を参考にした。
公式はこれ。
https://github.com/mysqljs/mysql

```js:App.vue
clickSaveBtn () {
  console.log('pushed save button')
  this.saveDialog = false

  axios
    .get('http://localhost:3000/' + this.saveFilename + '/' + this.text)
    .then(res => console.log(res))
    .catch(error => {
      console.log('error occurs in axios ' + error)
    })
},
```

```server.js
app.get('/:name/:text', (req, res) => {
    console.log('from frontend params: name => ' + req.params.name)
    console.log('from frontend params: text => ' + req.params.text)

    connection.query({
        sql: 'INSERT INTO documents (name, text) values (?, ?)',
        values: [req.params.name, req.params.text]
    }, (error, results) => {
        console.log('↓ results from db')
        console.log(results)
    })
})
```

## 4 Wordファイル作成

まずは、MySQLにある名前(name)のデータを選択肢としてブラウザに表示させる。
この記事が参考になった。
[Nuxt.jsとmysqlを連携してデータを表示してみた](https://qiita.com/ktn/items/a53a791a95315c36f933) 

```js:App.vue
clickCreateBtn () {
    console.log('pushed create button')
    axios
      .get('http://localhost:3000/filenames')
      .then(res => {
        console.log('↓response from server')
        console.log(res.data)
        for (let i = 0; i < res.data.length; i++) {
          this.filenames.push(res.data[i].name)
        }
      })
      .catch(error => {
        console.log('error occurs in axions ' + error)
      })
},
```

```server.js
app.get('/filenames', (req, res) => {
    let filenames = []
    connection.query('SELECT name from documents', (error, row, fields) => {
        if (error) {
            console.log('error occured in db: '+ error)
        }
        let files = []
        for (let i = 0; i < row.length; i++) {
            files.push(row[i])
        }
        filenames = JSON.stringify(files)
        res.header('Content-Type', 'application/json; charset=utf-8')
        res.send(filenames)
    })
})
```

なお、Node.js側でCORSポリシーを無効にしなければならないので注意。

```server.js
app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});
```

最後に、選択された名前のファイルを元に、そのファイル名とテキストをDBから取り出してWordファイルを作成していく。

PythonとMySQLを連携させるのにはmysqlclientを用いた。
この記事が参考になった。
[Python3でMySQLを使う – 基本操作からエラー処理まで](https://www.craneto.co.jp/archives/1219/)
公式はこれ。
https://pypi.org/project/mysqlclient/

Node.jsとPythonを連携させるのにはPython-shellを用いた。
この記事が参考になった。
[Python-shellの使い方](https://qiita.com/NT1123/items/09aed7b23388190cba23)
公式はこれ。
https://www.npmjs.com/package/python-shell

PythonからWordファイルを作成する方法はこの記事が参考になった。
[【python-docx】PythonでWordを操作する](https://dropoutgs.com/python-docx/)


```js:App.vue
clickStartCreateBtn () {
   this.createDialog = false
   axios
      .get('http://localhost:3000/word/' + this.createFilename)
      .then(res => console.log(res))
      .catch(error => {
        console.log('error occurs in axios ' + error)
      })
}
```

```server.js

app.get('/word/:filename', (req, res) => {
    let filename = req.params.filename
    console.log('from frontend params: filename => ' + filename)
    let { PythonShell } = require('python-shell')
    let pyshell = new PythonShell('createWord.py')

    pyshell.send(filename)

    pyshell.on('message', function (data) {
        console.log(data);
    });
})
```

```createWord.py
filename = sys.stdin.readline()
print('filename =>', filename.strip())

con = MySQLdb.connect(
    host = os.environ.get("DB_HOST"),
    user = os.environ.get("DB_USER"),
    passwd = os.environ.get("DB_PASSWORD"),
    db = os.environ.get("DB_NAME"),
)

cur = con.cursor()
sql = "select text from documents where name = %s"
cur.execute(sql, (filename.strip(),))

rows = cur.fetchall()

document = Document()
print('text =>', rows[0][0])
text = rows[0][0]
document.add_paragraph(text)
document.save(filename.strip()+'.docx')

print('success')
```

# まとめ

役に立つものを作りたいわけじゃなかったので、Python-shellに出会えただけで大きな収穫だった。
応用が効きそうなので気が向いたら色々試していきたい。