前記事QRZ.COM XML サービスについてのメモ(前置き) の続きです。
文書の場所
QRZ.COMが提供するXMLサービスの規格文書は次のリンクにあります。
https://www.qrz.com/XML/current_spec.html
正式名称は「QRZ XML Logbook Data Specification」です。version1.34が最新文書となります(2020年7月15日)。
ただし実際のXMLバージョンは1.36となっているようです。いつリビジョンアップしたのかはわかりません。
著者はAA7BQのFred Lloyd、VA7STVのStephen McLauglin両氏で、aa7bqコールサインは本文書内の利用例のためにも使われています。
仕様概要
仕様はシンプルです。
(1)qrz.comの特定URIに、GETあるいはPOSTでqrz.comの自分のユーザ名とパスワードを投げます。
(2)認証されたらセッション情報がXML形式で返されアクセスkeyが得られます。
※認証失敗した場合はError項目がXML形式で返されます。
(3)上記keyを用いて、検索したいコールサインのリクエストを行います。これもGET/POSTどちらも可能です。
(4)成功したら、該当コールサインのQRZDatabaseに格納されている情報がXML形式で返されます。QRZ.COMにブラウザでアクセスしコールサイン検索を行ったときに表示される「detail」項目の情報です。
以下、順を追ってその内容を見ていきます。
認証用のURI・パラメタと返されるXML要素
認証用URIは次のようになっています。
3種類のパターンがありますが最新バージョン用のものだけ掲出しておきます。
https://xmldata.qrz.com/xml/current/?username=xx1xxx;password=abcdef
(POSTの場合?以下を除いたURIにusername,passwordの値を投げます)。
成功すると次のようなXMLが返ります。ただし同例は、旧いバージョンでの取得結果ですので、最新クエリ結果とは異なっている場合があります。ご注意ください。
<QRZDatabase xmlns="http://xmldata.qrz.com" version="1.24">
<Session>
<Key>省略</Key>
<Count>14</Count>
<SubExp>non-subscriber</SubExp><−−−無料の場合のユーザはnon-subscriberとなります
<GMTime>日付</GMTime>
<Remark>cpu: 0.098s</Remark>
</Session>
</QRZDatabase>
失敗すると次のようになります。
<QRZDatabase xmlns="http://xmldata.qrz.com" version="1.24">
<Session>
<Error>Username/password incorrect </Error><−−−エラーの要素が表示される
<GMTime>日付</GMTime>
<Remark>cpu: 0.096s</Remark>
</Session>
</QRZDatabase>
コールサインクエリ用URIとXML要素
上記Key要素で囲まれた文字列を用いて、次のURIにアクセスします。先の例と同様GETでもPOSTでもかまいません。なお以下のURIはGET表現です。
https://xmldata.qrz.com/xml/current/?s=キー;callsign=コールサイン
文書の中で、著者の一人のaa7bqのコールサインを使ったXML例が掲載されています。
<もし有料のサービス登録済ユーザがリクエストすればどのような項目を得ることが出来るか>という例です。
なお同コールサインへのクエリについては、有料未登録ユーザでも同じようなフルの項目が得られるようになっているようです(実際に試すとわかります)。
<?xml version="1.0" ?>
<QRZDatabase version="1.34"><−−−現在は1.36です
<Callsign>
<call>AA7BQ</call>
<aliases>N6UFT,KJ6RK,DL/AA7BQ</aliases>
<dxcc>291</dxcc>
<fname>FRED L</fname>
<name>LLOYD</name>
<addr1>8711 E PINNACLE PEAK RD 193</addr1>
<addr2>SCOTTSDALE</addr2>
<state>AZ</state>
<zip>85255</zip>
<country>United States</country>
<ccode>291</ccode>
<lat>34.23456</lat>
<lon>-112.34356</lon>
<grid>DM32af</grid> <−−グリッドロケーター
<county>Maricopa</county>
<fips>04013</fips>
<land>USA</land>
<efdate>2000-01-20</efdate>
<expdate>2010-01-20</expdate> <−−−昔の例なので、現在とは違っている
<p_call>KJ6RK</p_call>
<class>E</class>
<codes>HAI</codes>
<qslmgr>NONE</qslmgr> <--任意だが、qslマネージャが個人の場合はそのコールサインなど、またビューロ経由の場合via bureauと記載される場合が多い
<email>flloyd@qrz.com</email>
<url>https://www.qrz.com/db/aa7bq</url>
<u_views>115336</u_views>
<bio>3937/2003-11-04</bio>
<image>https://files.qrz.com/q/aa7bq/aa7bq.jpg</image>
<serial>3626</serial>
<moddate>2003-11-04 19:37:02</moddate>
<MSA>6200</MSA>
<AreaCode>602</AreaCode>
<TimeZone>Mountain</TimeZone>
<GMTOffset>-7</GMTOffset>
<DST>N</DST>
<eqsl>Y</eqsl> <--eQSLを使っているか(Yとなっているが、文書では0/1あるいはブランクを返すとなっている。齟齬があるが理由不明)
<mqsl>Y</mqsl> <--郵便QSL配送可能か(同上)
なおこの例ではlotwのノードフィールドはない。現行バージョン1.36でクエリすると返ってくる。
<cqzone>3</cqzone>
<ituzone>2</ituzone>
<geoloc>user</geoloc>
<attn>c/o QRZ LLC</attn>
<nickname>The Boss</nickname>
<name_fmt>FRED "The Boss" LLOYD</name_fmt>
<born>1953</born>
</Callsign>
<Session>
<Key>2331uf894c4bd29f3923f3bacf02c532d7bd9</Key>
<Count>123</Count>
<SubExp>Wed Jan 1 12:34:03 2013</SubExp>
<GMTime>Sun Nov 16 04:13:46 2012</GMTime>
</Session>
</QRZDatabase>
ちなみに有料未登録ユーザがクエリを発行すると、実際は次の情報しか得られません。
<Callsign>
<call>コールサイン</call>
<fname>ファーストネーム</fname>
<name>ラストネーム</name>
<addr2>住所</addr2>
<country>国</country>
</Callsign>
その他の機能
他にも対象局の経歴(biography)情報を取得する(htm=コールサイン)、DXCC情報にかかわるクエリも発行出来ます。(前者の場合はXMLではなくhtmlで情報が返ります。)
また、この文書には他に緯度経度とグリッドデータについての計算方法(考え方)の項目があって興味深いのですが、本記事の主旨から外れるところがあるので、これは別記事で記載することにします。
テスト用コード(python)
上の接続・検索手順を踏まえたPython のテスト用コードは次のようになります。
※QRZ.COMに未登録コールサインの処理はしていません。単純にエラーが返ります。
実行ディレクトrには、コードの実行ファイル以外に、自分のユーザ名、パスワードを二行で書いたcredentials.txtという名前のファイルを置きます。
import requests
from xml.etree import ElementTree
def get_credentials():
with open('credentials.txt', 'r') as file:
lines = file.readlines()
username = lines[0].strip()
password = lines[1].strip()
return username, password
def get_session_key(username, password):
url = "https://xmldata.qrz.com/xml/current/"
data = {'username': username, 'password': password}
response = requests.post(url, data=data)
if response.status_code != 200:
raise Exception(f"認証リクエストでエラーが発生しました。ステータスコード: {response.status_code}")
root = ElementTree.fromstring(response.content)
namespace = {'ns': 'http://xmldata.qrz.com'}
key_element = root.find(".//ns:Key", namespace)
if key_element is None:
raise Exception("XMLレスポンスにKeyエレメントが存在しません。")
return key_element.text
def check_for_error_in_response(response):
root = ElementTree.fromstring(response.content)
namespace = {'ns': 'http://xmldata.qrz.com'}
error_element = root.find(".//ns:Error", namespace)
return error_element is not None
def fetch_and_save_data(callsign):
username, password = get_credentials()
try:#セッションキーが変わることがあるので、その対策もしている
with open('latest.key', 'r') as file:
session_key = file.read().strip()
except FileNotFoundError:
session_key = None
if session_key:
url = "https://xmldata.qrz.com/xml/current/"
data = {'s': session_key, 'callsign': callsign}
response = requests.post(url, data=data)
if not check_for_error_in_response(response):
with open(f"{callsign}.txt", "w") as file:
file.write(response.text)
return
session_key = get_session_key(username, password)
with open('latest.key', 'w') as file:
file.write(session_key)
url = "https://xmldata.qrz.com/xml/current/"
data = {'s': session_key, 'callsign': callsign}
response = requests.post(url, data=data)
if response.status_code != 200 or check_for_error_in_response(response):
raise Exception(f"新しいキーでのリクエストでエラーが発生しました。ステータスコード: {response.status_code}, レスポンス: {response.text}")
with open(f"{callsign}.txt", "w") as file:
file.write(response.text)
# fetch_and_save_data("コールサイン")
# 上が実際の実行メソッドです。コメントを外し、実際に情報を得たいコールサインを書いてください。同じディレクトリに、
# コールサイン.txt名のファイルが出来ます。