さくっとPocketのブックマークをはてなブックマークに移行
Pocketのブックマークをはてなブックマークに移行しようとしたのですが、2021/05/23時点では残念ながら、スパム対策機能のためにはてなブックマークのインポート機能が停止されていました。
一方で、Pocketのエクスポート機能は存在し、はてなブックマークにはREST APIが用意されています。この2つを利用して移行を行います。
手動で一つ一つ移行するのはめんどくさくてやりたくなかったため、こちらの機能およびAPIを利用して、一括で移行しました。
Pocket側の準備
Pocketのエクスポート機能でエクスポートを参考にPcketのエクスポート機能でエクスポートすると、ril_export.html
というファイル名でエクスポートされます。そちらのファイルから、リスト中のWebページを以下のようにして、urls.txt
というテキストファイルとして取り出します。
$ cat ril_export.html |
grep -o '<li><a href=".*">.*</a></li>' |
tr '"<>' ' ' |
awk '{print $4}' > urls.txt
その後、上記URLのリストを手抜きでそのままPythonのリストで利用できるように、Visual Studio Codeを使っていたので、urls.txt
のファイルの中身について、改行から次の行の先頭部分までを", "
に置換して、その後文字列の前後をPythonのリストになるように[
と"]
を追加します。
はてなブックマーク側の準備
Consumer key を取得して OAuth 開発をはじめようを参考に、Consumer Keyを取得し、"OAuth Consumer Key"と"OAuth Consumer Secret"を控えておきます。
その後、はてなブックマークのREST APIを利用するのスクリプトを参考にさせていただきました。migration.py
というファイル名で作成し、HatenaApi
/HatenaApiError
クラスについては、ほぼそのまま利用しました。今回使い捨てで特に失敗しても気にしないので、OAuth1Session
クラス中のcallback_uri
については適当に接続可能なURLを利用するように変更したのみです。
かつ、当該クラスの直後に以下を追加しました。authorize
関数の引数4箇所については先程控えた情報に置き換えてください。
if __name__ == '__main__':
api = HatenaApi()
api.authorize("はてなのユーザーID", "はてなのパスワード", "YOUR_CONSUMER_KEY", "YOUR_CONSUMER_SECRET", "write_public")
my_bookmark_list = ["https://...", ... , "https://..."] # "Pocket側の準備"で作成したリストの貼り付け
for bookmark in my_bookmark_list:
res = api.add_bookmark(bookmark, "", [])
res.raise_for_status()
print(res.text)
全体としては以下のようなコードになります。
import requests
from requests_oauthlib import OAuth1Session
class HatenaApiError(Exception):
class HatenaApi:
LOGIN_URL = 'https://www.hatena.ne.jp/login'
session = None
def _get_rk(self, hatena_id, password):
payload = {'name': hatena_id, 'password': password}
response = requests.post(self.LOGIN_URL, data=payload)
if not "Set-Cookie" in response.headers:
raise HatenaApiError("cannot get rk.ID/Password is wrong, or Hatena API spec changed.")
if not "rk=" in response.headers['Set-Cookie']:
raise HatenaApiError("cannot get rk.ID/Password is wrong, or Hatena API spec changed.")
rk = response.headers["Set-Cookie"].split("rk=")[1].split(";")[0]
return rk
def _get_authorization_redirect_url(self, user_id, password, authorization_url):
rk = self._get_rk(user_id, password)
res_auth = requests.get(authorization_url, headers={"Cookie" : "rk="+ rk}).text
rkm = res_auth.split("<input type=\"hidden\" name=\"rkm\" value=\"")[1].split("\"")[0]
oauth_token = res_auth.split("<input type=\"hidden\" name=\"oauth_token\" value=\"")[1].split("\"")[0]
res_redirect = requests.post(
authorization_url,
headers={"Cookie": "rk="+ rk},
params={
"rkm": rkm,
"oauth_token": oauth_token,
"name": "%E8%A8%B1%E5%8F%AF%E3%81%99%E3%82%8B"
}
)
return res_redirect.url
def authorize(self, user_id, password, consumer_key, consumer_secret, scope):
self.session = OAuth1Session(
consumer_key,
consumer_secret,
callback_uri="https://hayashier.com" # このURLは実際にアクセスできる必要はありません.
)
self.session.fetch_request_token(
"https://www.hatena.com/oauth/initiate?scope={}".format(scope)
)
authorization_url = self.session.authorization_url("https://www.hatena.ne.jp/oauth/authorize")
redirect_response = self._get_authorization_redirect_url(
user_id,
password,
authorization_url
)
self.session.parse_authorization_response(redirect_response)
self.session.fetch_access_token("https://www.hatena.com/oauth/token")
def add_bookmark(self, url, comment="", tags=None):
if not self.session:
raise HatenaApiError("call authorize method.")
if tags:
for t in tags:
comment += "[{}]".format(t)
return self.session.post(
"https://bookmark.hatenaapis.com/rest/1/my/bookmark?url=",
params={
"url": url,
"comment" : comment,
}
)
if __name__ == '__main__':
api = HatenaApi()
api.authorize("hayashier", "xxxxx", "xxxxx", "xxxxx", "write_public")
実行
以下のコマンドを実行し、正常に実行が完了すれば、マイグレーション完了です。
$ pip install requests_oauthlib
$ python migration.py