はじめに
弊社で使っているIP電話サービスBIZTEL。バージョン2系と呼ばれる古いバージョンを使っているため、管理画面がかなり使いづらい。特に、ユーザーの一覧をエクスポートする機能がないので、欠番を簡単に確認できない。
そこで、Pythonを使ってユーザー一覧をCSVで吐き出してみようと思った。
ちなみに、これからBIZTELを導入する場合は、BIZTEL 3系になると思うから、管理画面も改善しているはず。
前提条件
- BIZTELのシステム管理権限
- Windows 11
- Python 3.10.3
- requests 2.27.1
- beautifulsoup4 4.10.0
コード
import re
import csv
import time
import urllib3
import requests
from bs4 import BeautifulSoup
# SSL証明書のエラー警告を無視
urllib3.disable_warnings()
class Login:
def __init__(self, url, id, pw):
self.session = requests.session()
# URL末尾にスラッシュがない場合はつける
self.base_url = url if url[-1] == '/' else url + '/'
# ログイン情報をセット
payload = {
'inpAccount': id,
'inpPassword': pw
}
# verify=FalseとしないとSSL証明書エラーとなる
self.session.post(self.base_url + 'adminlogin.php', data=payload, verify=False)
class AccountManagement(Login):
def __init__(self, url, id, pw):
super().__init__(url, id, pw)
self.account_management_url = self.base_url + 'account_management.php'
account_management_response = self.session.get(self.account_management_url, allow_redirects=False)
self.soup = BeautifulSoup(account_management_response.text, 'html.parser')
def get_accounts(self):
# 正規表現でユーザーの合計数を取得
pattern = r'(?<=合計 )[0-9]+(?= 件)'
total_text = self.soup.select('td:contains("合計")')
total = re.search(pattern, total_text[-1].get_text(strip=True)).group(0)
# ページ数を計算
pages = int(total) // 10 + 1
# ユーザー情報を格納するリスト
accounts = []
# 1~pagesまでページを取得する
for i in range(pages):
page = {
'Ppage': i
}
response = self.session.post(self.account_management_url, data=page, allow_redirects=False)
soup = BeautifulSoup(response.text, 'html.parser')
# 名前と内線番号を取得
names = [e.get_text() for e in soup.select('body > table > tr > td > table > tr > td > table > tr > td > table > TR > td > a[href="#"]')]
phone_nums = [e.get_text(strip=True) for e in soup.select('body > table > tr > td > table > tr > td > table > tr > td > table > TR > td[width="90"]')]
# 名前と内線番号をリストに格納
for name, phone in zip(names, phone_nums):
accounts.append([name, phone])
# インターバル
time.sleep(1)
# ソート
accounts.sort(key=lambda x: x[1])
# ヘッダーをセット
accounts.insert(0, ['ユーザ名', '内線番号'])
# CSV ファイルへ出力する
with open('output.csv', mode='w') as f:
writer = csv.writer(f, lineterminator='\n')
writer.writerows(accounts)
使用例
Biztel = AccountManagement('https://biztel:1234', 'ID', 'Password')
Biztel.get_accounts()
SSLエラー回避
BIZTELはSSLサーバ証明書に自己証明書を利用しているため警告画面が表示されるとのこと。
セキュリティ上は問題ないらしいが、スクレイピングをする上で、エラーが出て止まってしまうので、回避策が必要。
そこで、urllib3.disable_warnings()
を追記。
BIZTELではサーバとの通信を暗号化しており、その際に使用しているSSLサーバ証明書に自己証明書を利用しているため警告画面が表示されますが 、セキュリティ上問題になるものではございません。
ログイン
inpAccount
とinpPassword
にIDとパスワードを渡してPOST。
アカウント管理ページ
class AccountManagement(Login):
def __init__(self, url, id, pw):
super().__init__(url, id, pw)
self.account_management_url = self.base_url + 'account_management.php'
account_management_response = self.session.get(self.account_management_url, allow_redirects=False)
self.soup = BeautifulSoup(account_management_response.text, 'html.parser')
- クラス
Login
を継承する。 - アカウント管理用のURLは
https://biztel:1234/account_management.php
となる。 -
allow_redirects=False
これがないとログイン画面に飛ばされる。 - BS4の標準パーサーである
html.parser
を使う。
アカウント管理ページをループ
def get_accounts(self):
# 合計を取得
pattern = r'(?<=合計 )[0-9]+(?= 件)'
total_text = self.soup.select('td:contains("合計")')
total = re.search(pattern, total_text[-1].get_text(strip=True)).group(0)
# ページ数を計算
pages = int(total) // 10 + 1
accounts = []
for i in range(pages):
page = {
'Ppage': i
}
response = self.session.post(self.account_management_url, data=page, allow_redirects=False)
soup = BeautifulSoup(response.text, 'html.parser')
# 名前と内線番号を取得
names = [e.get_text() for e in soup.select('body > table > tr > td > table > tr > td > table > tr > td > table > TR > td > a[href="#"]')]
phone_nums = [e.get_text(strip=True) for e in soup.select('body > table > tr > td > table > tr > td > table > tr > td > table > TR > td[width="90"]')]
# 名前と内線番号をリストに格納
for name, phone in zip(names, phone_nums):
accounts.append([name, phone])
# インターバル
time.sleep(1)
# ソート
accounts.sort(key=lambda x: x[1])
accounts.insert(0, ['ユーザ名', '内線番号'])
# CSV ファイルへ出力する
with open('output.csv', mode='w') as f:
writer = csv.writer(f, lineterminator='\n')
writer.writerows(accounts)
- 正規表現を使ってユーザー数の合計を
total
へ格納。 - 1ページあたり10人なので、求めたユーザー数を10で割った整数商に1を足すと総ページ数となる。
-
accounts
にユーザー情報の一覧を格納。 - アカウント管理ページのページ数はパラメーター
Ppage
で管理。 - 1ページごとにユーザー名と内線番号を取得して
accounts
に追加。 - サーバーへ負荷をかけないように
time.sleep(1)
で1秒間のインターバル。 -
accounts.sort(key=lambda x: x[1])
で内線番号を基準にソートして、accounts.insert(0, ['ユーザ名', '内線番号'])
でヘッダーを追加。 - CSV出力。
おわりに
バージョン3系へ早く移行したい。。。