Skip to content
 
 

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

macOS 内置词典内容拆解

最近女朋友在背单词,被我推荐了下 macOX 下自带的 New Oxford American Dictionary ,表示十分受用, 我就想着是否可以把词典内容,按需求取出,比如语源出处,或者派生词,这样更好去归纳记忆。

首先去查了 API ,通过官方的提供的文档来看只有 DCSCopyTextDefinition 可以或得到无标记的纯文本内容,通过文本解析来说定然没那么简单方便准确,所以找了下其他的方法。

调用 macOS 内置词典查询

#! /usr/bin/env xcrun swift 

import Foundation
import CoreServices


func getDefinition(_ textString : String) -> String? {
    let range : CFRange = CFRangeMake(0, textString.characters.count)
    
    if let definition = DCSCopyTextDefinition(nil, textString, range) {
        return definition.takeRetainedValue() as String
    }
    return nil
}

func main() {
    if Process.arguments.count != 2{
        print("Usage: madicly [word]")
    } else {
        let word = Process.arguments[1]
        if let definition = getDefinition(word) {
            print(definition)
        } else {
            print("No definition found in Dictionary.")
        }
    }
}
main()

私有API

其实直觉上,直接解析词典包会比较容易,尝试再三后发现有诸多问题,在此略过,最终尝试找到可以通过私有 API DCSCopyRecordsForSearchString 来获得词典应用展示的 HTML 内容。 并没有找到如何用 Swift 调用私有 API 的方法,下面换了 objc。

#import <Foundation/Foundation.h>
NSArray *DCSCopyAvailableDictionaries();
NSArray *DCSCopyRecordsForSearchString(DCSDictionaryRef dictionary, CFStringRef string, void *, void *);
NSArray *DCSRecordCopyData(CFTypeRef record);
NSString *DCSDictionaryGetName(DCSDictionaryRef dictID);

NSString * const DictZh = @"牛津英汉汉英词典";
NSString * const DictEn = @"New Oxford American Dictionary";
NSString * const DictJa = @"ウィズダム英和辞典 / ウィズダム和英辞典";
NSString * const DictKo = @"뉴에이스 영한사전 / 뉴에이스 한영사전";

int main(int argc, char * argv[])
{
        @autoreleasepool {
        if(argc < 2)
        {
                printf("Usage: %s [word]\n", argv[0]);
                return -1;
        }
        NSString *dictname = DictEn;
        int c;
        while( (c = getopt(argc, argv, "zejk")) != -1) {
            switch(c) {
                case 'z':
                    dictname = DictZh;
                    break;
                case 'e':
                    dictname = DictEn;
                    break;
                case 'j':
                    dictname = DictJa;
                    break;
                case 'k':
                    dictname = DictKo;
                    break;
                case '?':
                    printf("Unknown flag: %c", optopt);
                    break;
            }
        }
        CFTypeRef dictionary = NULL;
        NSArray *dicts = DCSCopyAvailableDictionaries();
        for (NSObject *aDict in dicts) {
              NSString *aShortName =  DCSDictionaryGetName((DCSDictionaryRef)aDict);
              if ([aShortName isEqualToString:dictname])
                  dictionary = (DCSDictionaryRef)aDict;
        }
        NSString * word = [NSString stringWithCString: argv[2] encoding: NSUTF8StringEncoding];
        NSArray *records = DCSCopyRecordsForSearchString(dictionary, (CFStringRef)word, 0, 0);
        for (id record in records) {
            printf("%s", [(NSString*)DCSRecordCopyData(record) UTF8String]);
        }
        }
        return 0;
}

解析 HTML

实际上去得到的内容还包含了 CSS 样式, 下面是去掉样式后的样子。

<html>
   <body>
      <d:entry id="m_en_us1225918" d:title="benefactor" class="entry">
         <span class="hg"><span d:dhw="1" role="text" syllabified="ben·e·fac·tor" class="hw">benefactor<span class="gp tg_hw"> </span></span><span class="pr"> |<span d:pr="US_IPA" soundFile="benefactor#_us_2" media="online" geo="us" class="ph">ˈbɛnəˌfæktər</span>| </span><span class="gp tg_hg"> </span></span><span class="sg"><span class="se1"><span class="gp tg_se1"></span><span role="text" class="posg"><span d:ps="1" class="pos"><span class="gp tg_pos">noun</span></span><span class="gp tg_posg"> </span></span><span d:abs="1" id="m_en_us1225918.001" class="msDict t_core"><span role="text" class="df">a person who gives money or other help to a person or cause<span class="gp tg_df">.</span></span></span></span><span class="gp tg_sg"> </span></span>
         <span role="text" class="etym">
            <span class="gp ty_label tg_etym">ORIGIN </span><span class="dg"><span class="date">late Middle English</span></span>: from <span class="la">Latin</span>
            <html>
               <body>
                  <d:entry id="m_en_us1225918" d:title="benefactor" class="entry"><span class="hg"><span d:dhw="1" role="text" syllabified="ben·e·fac·tor" class="hw">benefactor<span class="gp tg_hw"> </span></span><span class="pr"> |<span d:pr="US_IPA" soundFile="benefactor#_us_2" media="online" geo="us" class="ph">ˈbɛnəˌfæktər</span>| </span><span class="gp tg_hg"> </span></span><span class="sg"><span class="se1"><span class="gp tg_se1"></span><span role="text" class="posg"><span d:ps="1" class="pos"><span class="gp tg_pos">noun</span></span><span class="gp tg_posg"> </span></span><span d:abs="1" id="m_en_us1225918.001" class="msDict t_core"><span role="text" class="df">a person who gives money or other help to a person or cause<span class="gp tg_df">.</span></span></span></span><span class="gp tg_sg"> </span></span><span role="text" class="etym"><span class="gp ty_label tg_etym">ORIGIN </span><span class="dg"><span class="date">late Middle English</span></span>: from <span class="la">Latin</span>, from <span class="ff"> bene facere  </span><span class="trans"><span class="gp tg_tr"></span>do good (to)<span class="gp tg_tr"></span></span><span class="xrg"> (see <span class="xr"><a href="x-dictionary:r:m_en_us1225916:com.apple.dictionary.NOAD">benefaction</a></span>) </span><span class="gp tg_etym">.</span></span></d:entry>
               </body>
            </html>
            from <span class="ff"> bene facere  </span><span class="trans"><span class="gp tg_tr"></span>do good (to)<span class="gp tg_tr"></span></span><span class="xrg"> (see <span class="xr"><a href="x-dictionary:r:m_en_us1225916:com.apple.dictionary.NOAD">benefaction</a></span>) </span><span class="gp tg_etym">.</span>
         </span>
      </d:entry>
   </body>
</html>

Python 解析

简易的用 BeautifulSoup 解析了下写了个类,封装了下单词诸如音标,解释,语源等信息。

#! /usr/bin/env python
import sys
from bs4 import BeautifulSoup
import subprocess


class Mdict:
    def __init__(self, word):
        self.word = word
        p = subprocess.Popen(['bin/dicttool', '-e', word],
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        out, err = p.communicate()
        self._soup = BeautifulSoup(out, "lxml")
        p_zh = subprocess.Popen(['bin/dicttool', '-z', word],
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        out_zh, err_zh = p_zh.communicate()
        self._soup_zh = BeautifulSoup(out_zh, "lxml")

    @property
    def IPAs(self):
        return [item.text.strip() for item in self._soup.find("span", "hg").findAll("span", {"d:pr": "US_IPA"})]

    @property
    def etym(self):
        res = self._soup.find("span", "etym")
        return(res.text if res else None)

    @property
    def derivatives(self):
        return [item.find("span", "l").text.strip() for item in self._soup.findAll("span", "subEntry")]

    @property
    def roots(self):
        return [item.text.strip() for item in self._soup.findAll("span", "ff")]

    @property
    def origin(self):
        return [item.text.strip() for item in self._soup.find("span", "etym").findAll("span", "la")]

    @property
    def definitions_zh(self):
        return [item.find("span", "trans").text.strip() for item in self._soup_zh.findAll("span", "trg")]


def printword(w):
    print(w.word, " ")
    print("|{}|".format(','.join(w.IPAs)))

    for i, k in enumerate(w.definitions_zh):
        print(i+1, ".", k)
    print("\n{}\n".format(w.etym))

if __name__ == '__main__':
    word = sys.argv[1]
    q = Mdict(word)
    printword(q)

使用

在 IPython 实验一下效果如下

In [1]: from mdict import Mdict

In [2]: Mdict('benefactor')
Out[2]: <mdict.mod.Mdict instance at 0x10b86fab8>

In [3]: word = Mdict('benefactor')

In [4]: word.definitions_zh
Out[4]:
[u'\u8d5e\u52a9\u4eba',
 u'\u4e0d\u77e5\u540d\u7684/\u533f\u540d\u7684\u6350\u52a9\u8005']

In [5]: word.etym
Out[5]: u'ORIGIN late Middle English: from Latin, from  bene facere  \u2018do good (to)\u2019  (see benefaction) .'

In [6]: word.IPAs
Out[6]: [u'\u02c8b\u025bn\u0259\u02ccf\xe6kt\u0259r']

In [7]: word.origin
Out[7]: [u'Latin']

后记

到此,大概能满足我用 Python 批量的取得单词中的某项内容的需求, 遂把经过写下来供以后有需要的人参考。 抛砖引玉,简易的代码在 Github ,如果有兴趣请点赞支持或者完善代码。

附录

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages