[PYTHON] I want to import Google Keep memos into Bear (memo app)

Motivation for writing:

I tried using Bear and it was pretty good, so I thought I'd try importing the notes stored in Google Keep (not just Evernote).

You can easily take notes anywhere, paste a piece of code, share it between devices, have a proper search function, and if possible, seek a memo app that does not worry about privacy policy (and rather than being free) , I want to emphasize the continuity of services and policies)

Or Evernote refugee diary

Evernote https://evernote.com/intl/jp/ Evernote, which I've been using for years, was stressed by the text format. It was good enough to write down what I came up with and save a crop of a blog post (convenient enough) I also wanted to paste a piece of code, so I thought many times that I could at least use markdown compatible notation.

The limit on the number of devices that can be used with the free plan was fine because it should have been used on Mac and iPhone (I didn't feel like moving to the paid plan because of the format stress for) In December of last year, we decided to export all the data and withdraw from Evernote after the change in the rules that allows Evernote employees to view the user's notes was announced.

Kobito

http://kobito.qiita.com/ I used to occasionally (still occasionally) organize technical information that I plan to publish later (on Qiita). As a markdown editor with real-time preview.

I once wrote an article like this. kobitonote.py --Synchronize save items edited with Kobito to Evernote

Day One

http://dayoneapp.com/ Diary app. macOS version (4800 yen), iOS version (600 yen).

I've been using the old version (Day One Classic) on macOS (I think it was around 1200 yen) and iOS for about 4 years now.

You can use markdown. However, the search function (especially Japanese) is not good enough. (By the way, I often search for information like "when did I go to the barber shop") The data stored in the cloud is encrypted, and employees do not access the encrypted files unless legally required, which is a satisfactory level for privacy protection for the time being. It's nice to sync my diary, but if I edit the same entry from multiple devices and there is a conflict, it will be overwritten (rather than leaving multiple versions), so when I write a diary from another device It covers operations such as launching a new entry, writing it, and editing it later, or saving the diary entry in edit mode without leaving it open for the time being.

I'm not particularly dissatisfied with the diary app (thanks to this app, I got into the habit of writing a diary), but the search function makes it one step further as a memo app.

Data stored in the cloud is encrypted and encrypted only when legally required Private notes can be written on Day One (even without Evernote).

Notes attached to macOS

To rescue Evernote refugees, or to lock in the MacOS / iOS / iCloud camp, The memo app that comes with macOS has the ability to import Evernote export files (.enex). (I said to enclose it because there is no export function from the memo app)

It's an OS standard, and it synchronizes with the memo app on iOS, so is this okay? I thought it was a short while. The search function is not working at all. Too many notes? No, I could search with the iOS app, so I don't think that's the case. Not being able to search is completely meaningless as an electronic memo, so look for other options.

Google Keep

https://keep.google.com/ A memo app made by Google. Use with a web browser. There is also an iOS app.

I'm worried about the privacy of the memo (* in the sense that it doesn't specify that privacy will be protected) It is convenient to search normally. You can refer to the memo from Google Docs. (This is useful as a writing tool)

Bear

bear-writer http://www.bear-writer.com/

I spent three months segregating my private notes with Day One (Classic) and the notes I wanted to search with Google Keep, but I read this article yesterday.

―― [bear is a pretty good line for apps. It's pretty interesting because I'm doing various things around the essence of innovation such as markdown. --toukubo.com](http://toukubo.com/post/159021941846/bear%E3%81%A3%E3%81%A6%E3%82%A2%E3%83%97%E3%83%AA% E3% 81% 8C% E3% 81% 8B% E3% 81% AA% E3% 82% 8A% E8% 89% AF% E3% 81% 84% E7% B7% 9A% E3% 81% 84% E3% 81% A3% E3% 81% A6% E3% 82% 8Bmarkdown% E3% 81% A8% E3% 81% 8B% E3% 81% AE% E3% 82% A4% E3% 83% 8E% E3% 83% 99% E3% 83% BC% E3% 82% B7% E3% 83% A7% E3% 83% B3% E3% 81% AE% E6% 9C% AC% E8% B3% AA% E3% 81% AE% E5% 91% A8% E3% 82% 8A% E3% 81% A7% E8% 89% B2% E3% 80% 85% E3% 82% 84% E3% 81% A3% E3% 81% A6) -["There is no concept of" notebook ". --toukubo.com](http://toukubo.com/post/159053123251/%E3%83%8E%E3%83%BC%E3%83%88%E3%83%96%E3%83%83% E3% 82% AF% E3% 81% AE% E6% A6% 82% E5% BF% B5% E3% 81% 8C% E7% 84% A1% E3% 81% 84% E3% 81% 93% E3% 81% A8) -[Seriously bear is amazing. I really like it after a long time. Extremely elegant as an information design. --toukubo.com](http://toukubo.com/post/159054402126/%E3%81%BE%E3%81%98bear%E3%81%99%E3%81%94%E3%81%84%E4 % B9% 85% E3% 80% 85% E3% 81% AB% E3% 82% 81% E3% 81% A1% E3% 82% 83% E3% 81% 8F% E3% 81% A1% E3% 82 % 83% E6% B0% 97% E3% 81% AB% E5% 85% A5% E3% 81% A3% E3% 81% 9F% E6% 83% 85% E5% A0% B1% E8% A8% AD % E8% A8% 88% E3% 81% A8% E3% 81% 97% E3% 81% A6% E3% 82% 82% E3% 81% AE% E3% 81% 99% E3% 81% 94% E3 % 81% 84% E3% 82% A8% E3% 83% AC% E3% 82% AC% E3% 83% B3% E3% 83% 88)

For the time being, I installed the iOS version and the macOS version.

First of all, the design is excellent. (You can choose your favorite from several themes) You can use your own format and markdown for the text format. Mutual links between memos can be made (= can be used like a wiki). Evernote export files (.enex) can be imported. You can also import rdf and md. (It seems that you can also import from Day One) ◎ The "Escape Involuntary Tags" option is required when importing. Because, if there is a # character, the word after that will be recognized as a tag, and hundreds of meaningless tags will be listed in the tag list. Tags are part of the text (not text attributes), so if you delete the tags (although you can only delete them one by one from the tag list), the tag notation in the text (where you thought it was, eg #! / Bin /) sh) is erased. There is no stress in searching. There is a sense of security in the privacy policy. However, it often falls during synchronization. (Pro account (150 yen per month) is required for device-to-device synchronization.)

I want to import Google Keep notes into Bear

So, here is the main subject of this entry.

Importing from Google Keep ... Markdown individual HTML files exported by Google Takeout, although not supported directly from Bear Can be imported by converting to.

$ unzip takeout-yyyymmddThhmmssZ-001.zip

An HTML file (1 file = 1 memo) will be expanded under ./Takeout/Keep/, so markdown it by writing an appropriate script and import it from Bear.

$ python html2md.py dir ./Takeout/Keep

For your reference, I will paste html2md.py (a suitable script I used) below. (Required Click. Use at your own risk, or edit / improve as you like. The same directory as the HTML file (in the above example, .. A large number of md files can be created in / Takeout / Keep / and below). Bear cannot select HTML files when importing, so even if you select all, only md can be picked up)

html2md.py


#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import click
import os
import sys
from bs4 import BeautifulSoup
from datetime import datetime
import time


def datetime_to_unixtime(dt):
    assert isinstance(dt, datetime)
    return time.mktime(dt.timetuple())


def touch(fname, atime=datetime.now(), mtime=datetime.now()):
    if not os.path.exists(fname):
        open(fname, 'a').close()
    os.utime(fname, (datetime_to_unixtime(atime), datetime_to_unixtime(mtime)))


def parse(fp):
    soup = BeautifulSoup(fp, 'lxml')

    note = soup.find('div', class_='note')

    title, timestamp, archived, content, labels = None, None, False, None, []

    for div in note.find_all('div'):
        class1 = div['class'][0]
        if class1 == 'heading':
            tstr = div.text.strip()
            timestamp = datetime.strptime(tstr, '%Y/%m/%d %H:%M:%S')
            # print 'TIMESTAMP (%s)' % timestamp
        elif class1 == 'archived':
            archived = True
            # print '+ARCHIVED'
        elif class1 == 'title':
            title = div.text.strip()
            # print 'TITLE (%s)' % title.encode('utf-8')
        elif class1 == 'content':
            contents = []
            for content in div.contents:
                if isinstance(content, unicode):
                    contents.append(content)
                else:
                    if content.name == 'br':
                        contents.append('\n')
                    else:
                        contents.append(content.text)
            content = ''.join(contents)
            # print 'CONTENT', content.encode('utf-8')
        elif class1 == 'labels':
            labels = [label.text for label in div.find_all('span', class_='label')]
            # print 'LABELS', labels
        else:
            # print class1, div
            pass

    return (title, timestamp, archived, content, labels)


@click.group()
def cli():
    pass


def conv(html_path):
    with open(html_path, 'r') as fp:
        (title, timestamp, archived, content, labels) = parse(fp)
        print 'TITLE:', title
        print 'TIMESTAMP:', timestamp
        # print 'ARCHIVED:', archived
        if archived:
            labels.append('archived')
        # print content
        labels.append('keep')
        print 'LABELS:', ' '.join(['#%s' % label for label in labels])
        # print

    if html_path.endswith('.html'):
        md_path = html_path.replace('.html', '.md')
    else:
        md_path = html_path + '.md'

    with open(md_path, 'w') as fp:
        def _tagify(label):
            if ' ' in label:
                return '\\#%s\\#' % label
            else:
                return '#%s' % label
        if title:
            fp.write(title.encode('utf-8'))
        fp.write('\n\n')
        fp.write(content.encode('utf-8'))
        fp.write('\n')
        fp.write('%s\n' % ' '.join([_tagify(label.encode('utf-8')) for label in labels]))

    touch(md_path, atime=timestamp, mtime=timestamp)


@cli.command()
@click.argument('html-path', type=click.Path('r'))
def one(html_path):
    assert os.path.exists(html_path)
    conv(html_path)


@cli.command()
@click.argument('html-dir', type=click.Path('r'), default='.')
def dir(html_dir):
    assert os.path.exists(html_dir) and os.path.isdir(html_dir)
    for path in os.listdir(html_dir):
        full_path = os.path.join(html_dir, path)
        conv(full_path)


if __name__ == '__main__':
    cli()

Recommended Posts

I want to import Google Keep memos into Bear (memo app)
I want to INSERT a DataFrame into MSSQL
I want to upload a Django app to heroku
I want to remove Python's Unresolved Import Warning with vsCode
I want to solve Sudoku (Sudoku)
I want to record the execution time and keep a log.
I want to use shortcut translation like DeepL app on Linux
I want to automatically answer Google Form at 5 o'clock every morning
I want to understand systemd roughly
[Memo] How to use Google MµG
I want to scrape images to learn
I want to do ○○ with Pandas
I want to copy yolo annotations
I want to debug with Python
[Python memo] I want to get a 2-digit hexadecimal number from a decimal number
[Google Colab] I want to display multiple images side by side in tiles