📔
PythonでクリップボードのHTMLデータを取得(Windows限定)
TL;DR
-
powershell Add-Type -AssemblyName System.Windows.Forms; [System.Text.Encoding]::UTF8.GetString([System.Windows.Forms.Clipboard]::GetData('html format').ToArray())
をsubprocess
で実行する
既存ライブラリについて
win32clipboard
Win32 API
の呼び出しが可能なwin32clipboard
でのwin32clipboard.GetClipboardData()
では、呼び出し先API自体の指定できる形式の中にHTML
がないためテキスト形式でしか取得できない。
pyperclip
win32clipboard
同様pyperclip.paste()
ではテキスト形式でしか取得できない。
PowerShell: Get-Clipboardについて
Get-Clipboard -TextFormatType Html
で取得することはできるが、日本語が文字化けする。
ターミナルの文字コードを変えても効果がないので、おそらくGet-Clipboard
コマンド自体の実装あるいはコマンド呼び出し元がUTF-8エンコーディングにしか対応していないと思われる。
Get-Clipboard -TextFormatType Html
Version:0.9
StartHTML:00000131
EndHTML:00000556
StartFragment:00000165
EndFragment:00000520
SourceURL:https://zenn.dev/about
<html><body>
<!--StartFragment--><div class="View_headerMainContainer__7gRMq"><h2 class="View_headerTitle__DIkXe">Write <span style="display: inline-block;">for yourself.</span></h2><p class="View_headerDescription__BXdhj">Zenn縺ッ繧ィ繝ウ繧ク繝九い縺ョ縺溘 a縺ョ譁ー縺励>諠・ア蜈ア譛峨さ繝溘Η繝九ユ繧」縺ァ縺吶・隱ー縺九・縺溘a縺ォ縲∬・蛻・・縺溘a縺ォ遏・隕九r蜈ア譛峨@縺セ縺励 g縺・・/p></div><!--EndFragment-->
</body>
</html>
対処方法
[System.Windows.Forms.Clipboard]::GetData()
を呼び出す。
この際に引数html format
を渡すことでHTMLデータのSystem.IO.MemoryStream
を取得することができるので、これをバイト配列に直してUTF-8でテキストエンコーディングすれば生データを取り出せる。
powershell
[System.Text.Encoding]::UTF8.GetString([System.Windows.Forms.Clipboard]::GetData('html format').ToArray())
Version:0.9
StartHTML:00000131
EndHTML:00000556
StartFragment:00000165
EndFragment:00000520
SourceURL:https://zenn.dev/about
<html><body>
<!--StartFragment--><div class="View_headerMainContainer__7gRMq"><h2 class="View_headerTitle__DIkXe">Write <span style="display: inline-block;">for yourself.</span></h2><p class="View_headerDescription__BXdhj">Zennはエンジニアのための新しい情報共有コミュニティです。 誰かのために、自分のために知見を共有しましょう。</p></div><!--EndFragment-->
</body>
</html>
あとはPythonからならこのコマンドをsubprocess
等でPowerShell経由で取得すれば良い。
PowerShellがモジュールから呼び出せるなら理論上どの言語でも行けるはず。
サンプルスクリプト
clipboard.py
import re
from re import MULTILINE, Match
from subprocess import PIPE, Popen
START_HTML_PATTERN, END_HTML_PATTERN = [
re.compile(pattern, flags=MULTILINE)
for pattern in [rb"^StartHTML:(\d+)", rb"^EndHTML:(\d+)"]
]
def main() -> None:
stdout, _ = Popen(
(
"powershell",
"Add-Type",
"-AssemblyName",
"System.Windows.Forms;"
"[System.Text.Encoding]::UTF8.GetString("
"[System.Windows.Forms.Clipboard]::GetData('html format').ToArray()"
")",
),
stdout=PIPE,
stderr=PIPE,
).communicate()
start, end = [
pattern.search(stdout) for pattern in [START_HTML_PATTERN, END_HTML_PATTERN]
]
if 0 == len([match for match in [start, end] if not isinstance(match, Match)]):
print(stdout[int(start.group(1)) : int(end.group(1))].decode("shift_jis"))
else:
print("no html data")
if __name__ == "__main__":
main()
実行例
python clipboard.py
<html><body>
<!--StartFragment--><div class="View_headerMainContainer__7gRMq"><h2 class="View_headerTitle__DIkXe">Write <span style="display: inline-block;">for yourself.</span></h2><p class="View_headerDescription__BXdhj">Zennはエンジニアのための新しい情報共有コミュニティです。 誰かのために、自分のために知見を共有しましょう。</p></div><!--EndFragment-->
</body>
</html>
補足
StartHTML
の開始デックス~EndHTML
の終了インデックスまでのバイトデータを正規表現で取得して切り出してShift_JISでエンコードしている。これにより<html>
~</html>
までのテキストデータを取り出せる。
html format
のデータがない場合、あるいは正規表現にマッチしなかった場合は無い旨を出力する。
Discussion