CAS番号もしくは化合物名からSMILESを取得する

### はじめに
[前回の記事][16]では某試薬会社のサイトからCAS番号・製品名を取得しました。
今回はこれらの情報からSMILES構造式を取得する方法を紹介します。

### 環境
- Windows 11
- python 3.8.8
- beautifulsoup4 4.11.1
- pubchempy 1.0.4

---
### CAS番号取得の方針
CAS番号や製品名(化合物名)からSMILES構造式を取得する方法はいくつかありますが、
今回は以下の2つを利用して情報を収集します。
- [PubChem][17]
- [Chemistry Identifier Resolver (以下CIR)][18]

[17]:https://pubchem.ncbi.nlm.nih.gov/
[18]:https://cactus.nci.nih.gov/chemical/structure

PubChemからの情報取得については[PubChemPy][19]を利用し、
CIRからの情報取得についてはBeautifulSoup4を利用します。

[19]:https://pypi.org/project/PubChemPy/1.0/

### PubChemPyを用いた情報取得の方法

PubChemPyの導入および使用方法については以下のサイト参考にしました。

- [化合物データベースPubChemをpythonで使いこなす][20]
- [pubchem から化合物情報を一気に取得する][21]

[20]:https://future-chem.com/pub-chem-py/
[21]:https://qiita.com/ottantachinque/items/b9986e676c51a8e2805b

今回はnameとしてCAS番号を入力して検索し、IsomericSMILESを習得します。
ただし化合物によってはIsomericSMILESを取得できない場合もあり(ポリマー等)、
そういった場合の例外処理も実施します。

```python
import pandas as pd
import pubchempy as pcp

def find_smiles_from_CASNo(cas):
    try:
        return pcp.get_properties('IsomericSMILES', cas, 'name', as_dataframe=True).iloc[0][0]
    except:
        return None
```

### BeautifulSoup4を用いたCIRからの情報取得の方法

CIRの使用方法については以下のサイトを参考にしました。

- [NIHのChemical identifer resolverでcas番号、SMILES、InChiKey、IUPAC名を取得しよう][22]

[22]:https://netdekagaku.com/chemicalidenticyresolver/

化合物名を組み込んだ適切なURLを生成すればSMILESが取得できるようです。
もちろん検索する化合物名によってはページが見つからないため、
そういった場合に備えて以下のような関数を作成いたしました。

```python
import urllib.request
from bs4 import BeautifulSoup

def find_smiles_from_name(name):
    url = 'https://cactus.nci.nih.gov/chemical/structure/{}/smiles'.format(name)
    request = urllib.request.Request(url = url)
    try:
        html = urllib.request.urlopen(request)
        soup = BeautifulSoup(html, "html.parser")
        return soup.text
    except:
        return None
```

---

以上のように2パターンの取得方法を用意しましたが、
実際に試してみるとPubChemからの情報取得の方が取得率が高かったため、
PubChemから情報取得→失敗したものだけCIRで検索をかけるのが良さそうです。

### 実際のコード

アルファベット別に試薬データを取得したフォルダを作業ディレクトリとし、

Aから順にファイル読み込み→PubChemから情報取得→CIRから情報取得→
一旦Excelファイルに保存→Zまで終わったらデータフレームを統合→
SMILESを取得できなかった試薬の行を削除→Excelファイルに保存

といった流れで情報を取得します。

```python
import pandas as pd
import time
import urllib.request
from bs4 import BeautifulSoup
import time
import pubchempy as pcp
#パッケージの読み込み

def find_smiles_PubChem(cas):
    time.sleep(3)
    try:
        return pcp.get_properties('IsomericSMILES', cas, 'name', as_dataframe=True).iloc[0][0]
    except:
        return None

def find_smiles_CIR(row):
    if row['Smiles'] is not None:
        return row['Smiles']
    else:
        url = 'https://cactus.nci.nih.gov/chemical/structure/{}/smiles'.format(row['Name'])
        request = urllib.request.Request(url = url)
        time.sleep(3)
        try:
            html = urllib.request.urlopen(request)
            soup = BeautifulSoup(html, "html.parser")
            return soup.text
        except:
            return None

prefix_list = [chr(i) for i in range(65,91)] #AからZまでの文字を用意

for prefix in prefix_list:
    t1 = time.time()
    df = pd.read_excel('compoundList_{}.xlsx'.format(prefix)) #データフレームの読み込み
    df['Smiles'] = df['CAS'].map(find_smiles_PubChem) #PubChemからデータ取得
    df['Smiles'] = df.apply(find_smiles_CIR, axis = 1) #PubChemからSMILES取得できなかったものだけCIR検索
    df.to_excel("SMILES_List_{}.xlsx".format(prefix), index = False) #Excelファイルに保存
    t2 = time.time()
    print("compounds_{0} finished. {1} seconds".format(prefix, t2 - t1)) #終了メッセージと所要時間表示

#最後にファイルを統合する。
df_all = pd.DataFrame()

for prefix in prefix_list:
    df = pd.read_excel('SMILES_List_{}.xlsx'.format(prefix))
    df_all = pd.concat([df_all, df])

df_all = df_all.dropna(subset = ['Smiles'])

df_all.to_excel('TCI_Smiles_List.xlsx')
```

実際に動かし始めるとかなり時間がかかるので、時間に余裕があるときに実施しましょう。

すくれいぴんぐするぞ！おーばぁないとで

--- 
### おわりに
某試薬会社から取得した試薬リストを基にSMILES構造式を取得しました。
購入可能な試薬構造リストを基にケモインフォ的な解析を行うことで、
設計した分子を合成できずプロジェクトが絵空事に……ということも減らせるかも？
皆さんぜひ有効活用してみてください。


---
### これまでの記事のシリーズ
[RDKit入門①：まずは環境構築][1]
[RDKit入門②：1分子の読み込みと部分構造検索][2]
[RDKit入門③：複数分子の読み込み (前編)と分子群の描画][3]
[RDKit入門④：複数分子の読み込み (後編)とデータフレームの加工][4]
[RDKit入門⑤：データフレーム内の分子群に対する部分構造検索][5]
[RDKit入門⑥：Morganフィンガープリントの作成とそれを用いたタニモト係数の計算による分子類似性評価][6]
[RDKit入門⑦：反応式の取り扱い(前編)][7]
[RDKitで化学反応を扱った際の生成物群を整理し図示する方法][8]
[RDKit入門⑧：反応式の取り扱い(後編)][9]
[RDKitで化学反応を繰り返しつつ分子の重複数をカウント・記載する][10]
[RDKit入門⑨：データフレーム内の分子群に対する化学反応の適用とExcelファイルへの出力][11]
[RDKit入門⑩：データフレーム内の分子群に対する完全一致検索][12]
[指定した回数だけ化学反応を実施する関数の作成][13]
[BRICSフラグメントを利用した分子構造生成関数の簡略化][14]
[Butinaモジュールによる類似化合物のクラスタリング][15]
[試薬会社のサイトからスクレイピングで化合物データを取得する][16]

[1]:https://qiita.com/Ru-PNP/items/ed5a8530611ef0d712b7
[2]:https://qiita.com/Ru-PNP/items/58d733ef327677d273fd
[3]:https://qiita.com/Ru-PNP/items/1cea34b864e993e4e1e1
[4]:https://qiita.com/Ru-PNP/items/62a108d92c1ddd522e58
[5]:https://qiita.com/Ru-PNP/items/b469ac1b907909a9d821
[6]:https://qiita.com/Ru-PNP/items/79873f0e3eab444f5bfe
[7]:https://qiita.com/Ru-PNP/items/49a080a015414553bbad
[8]:https://qiita.com/Ru-PNP/items/baebf99e269bd7fb2fb5
[9]:https://qiita.com/Ru-PNP/items/e0c1cccf4a7b0cd3f412
[10]:https://qiita.com/Ru-PNP/items/25d40044f3985a406982
[11]:https://qiita.com/Ru-PNP/items/897541250b69733f6f1e
[12]:https://qiita.com/Ru-PNP/items/6185eb2fe873e9d9a893
[13]:https://qiita.com/Ru-PNP/items/7bbea91407790e7f76a1
[14]:https://qiita.com/Ru-PNP/items/c1d02a9716c3489ab911
[15]:https://qiita.com/Ru-PNP/items/56c35f9abd10bd633e1b
[16]:https://qiita.com/Ru-PNP/items/1db4f5f91cfb80bf30f7