hayashier Tech Blogs
  • hayashier Tech Blogs
  • Author's Books
    • 実践Redis入門 (日本語版)
    • 実践Redis入門 (한국어판)
  • Top Contents
    • Dive Deep Redis
    • Dive Deep Memcached
    • Kubernetes 入門
    • TCP 入門
    • TLS 入門
    • GPG 入門
    • サービス障害が発生した場合の対応方法
    • よく使うトラブルシューティング コマンド実行例 まとめ
    • コピペで使えるELBのアクセスログ解析による事象分析 (ShellScript, Athena)
  • Containers
    • Docker 入門
    • Nomad 導入
    • Dockerを利用してさっと検証環境構築
  • Kubernetes
    • Kubernetes 入門
    • Kubernetes 導入 with Amazon Linux 2
    • EKSを利用してKubernetesでSpring MVCをデプロイ (NLB + Auto Scaling)
  • Load Balancer
    • ALB 認証 導入
    • TLS extensions support with ALB
    • ELB(CLB,ALB,NLB)の種類ごとのHTTPレスポンスの違い
    • ELB(CLB) で WebSocket 通信
  • RDBMS
    • PostgreSQL DBA 入門
    • RDBMS Benchmark Get Started
    • RDBMS サンプルデータ生成 Get Started
    • RDS PostgreSQL Extensions Get Started
    • RDBMS Engine Inspection for Troubleshooting
  • Redis
    • Dive Deep Redis ~ 入門から実装の確認まで
    • Dive Deep Redis Internals ~ GETコマンド実行時の動作 ~
    • RedisのString型は今でも本当に512MBが上限か?
    • Redis 公式ドキュメント まとめ
    • Redis / Memcached Source Code Reading - Overview -
  • Memcached
    • Dive Deep Memcached ~ 入門から実装の確認まで ~
    • Dive Deep Memcached ~ SETコマンド実行時の動作 ~
    • Memcached 公式ドキュメント まとめ
    • memtier_benchmark + memcached-tool の導入
    • Redis / Memcached Source Code Reading - Overview -
  • Hadoop
    • Hadoop Get Started
  • Networking
    • TCP 入門
    • TLS 入門
    • ksnctf: HTTPS is secure, Writeup (TLS 通信解読)
    • オンプレ側ルーター(Cisco 1812J, Juniper SRX210, YAMAHA RTX 1210)から Direct Connect へ BGP 設定
  • Software
    • アルゴリズムとデータ構造 入門
    • デザインパターン 入門
    • ソフトウェアテスト 入門
  • System Admin
    • Shell Script 入門
    • サービス障害が発生した場合の対応方法
    • よく使うトラブルシューティング コマンド実行例 まとめ
    • コピペで使えるELBのアクセスログ解析による事象分析 (ShellScript, Athena)
    • GPG 入門
    • Operation Misc
  • Development
    • ローカル環境のプログラミング言語のバージョンを切り替え macOS
    • /usr/local/Cellar/pyenv/1.2.21/libexec/pyenv: No such file or directoryのエラーの対処方法
  • AWS
    • AWS Misc
    • AWS CLI, AWS SDKのリトライ処理の実装について
    • AWS CLI バージョンアップでエラー発生を解消
    • Elastic Beanstalkで稼働しているアプリケーション(Ruby, Sinatra)をAmazon Linux AMIからAmazon Linux2へ移行
    • Elastic Beanstalkでインスタンス入れ替え後にnginxのデフォルトの画面が表示されてしまう問題の対応
    • Amazon Lightsail に SSL 証明書設置 with Let's Encrypt (自動更新)
    • Amazon Lightsailで10分で作るお手軽Markdownで書く独自ドメインのブログサイト構築
    • Lambdaをローカルでテスト(with Docker)
    • ECS + ALB でダウンタイムなしでデプロイ
    • `Repository packages-microsoft-com-prod is listed more than once in the configuration`のメッセージの解消方法
  • Others
    • Pandoc 導入
    • textlint + prh による文章校正
    • 紙書籍をPDFに変換
    • Sphinx 導入
    • さくっとPocketのブックマークをはてなブックマークに移行
    • Macが突然起動しなくなった話
    • Macでターミナルが開かない (zsh編)
    • ホスト型 IDS Tripwire とネットワーク型 IDS Snort の導入 with CentOS 6
    • JMeter 導入
    • Squid 導入 with Amazon Linux AMI
    • Spring MVCを導入 (+ MySQL, Redis)
    • 外資系企業で働いている場合の確定申告方法 (RSU考慮)
Powered by GitBook
On this page
  • さくっとPocketのブックマークをはてなブックマークに移行
  • Pocket側の準備
  • はてなブックマーク側の準備
  • 実行
  1. Others

さくっとPocketのブックマークをはてなブックマークに移行

さくっとPocketのブックマークをはてなブックマークに移行

Pocketのブックマークをはてなブックマークに移行しようとしたのですが、2021/05/23時点では残念ながら、スパム対策機能のためにはてなブックマークのインポート機能が停止されていました。

  • はてなブックマークのインポート機能を、スパム対策のため一時的に停止します

一方で、Pocketのエクスポート機能は存在し、はてなブックマークにはREST APIが用意されています。この2つを利用して移行を行います。

  • Pocketのエクスポート機能でエクスポート

  • はてなブックマーク REST API

手動で一つ一つ移行するのはめんどくさくてやりたくなかったため、こちらの機能および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("はてなのユーザーID", "はてなのパスワード", "YOUR_CONSUMER_KEY", "YOUR_CONSUMER_SECRET", "write_public")

    my_bookmark_list = ["https://...", ... , "https://..."]

    for bookmark in my_bookmark_list:
        res = api.add_bookmark(bookmark, "", [])
        res.raise_for_status()
        print(res.text)

実行

以下のコマンドを実行し、正常に実行が完了すれば、マイグレーション完了です。

$ pip install requests_oauthlib
$ python migration.py
PreviousSphinx 導入NextMacが突然起動しなくなった話

Last updated 22 days ago