锤子阅读文章搜索
根据锤子阅读APP提供的接口编写的 Python 脚本,支持文章搜索,供学习交流。

缘由

  1. 之前找大象公会一篇文章,在公众号里被屏蔽了,发现锤子阅读里的竟然还可以看,可能存到自己的服务器上了吧。以后文章封了可以在这里找找。
  2. 锤子阅读不支持文章搜索,有时候想要找个文章难。
  3. 划水好无聊。

脚本调用

使用 Python3 编写,单个文件,无第三方库。结果有命令行和文本两种输出方式,具体参数如下:

  • -h 帮助文档
  • -s 订阅号名称
  • -k 搜索关键词,不填返回最新的文章
  • -n 返回文章数,默认10
  • -m 搜索匹配等级,默认1匹配标题 2匹配标题和摘要
  • -o 输出方式, 默认1命令行输出 2写入本地文件到当前目录

代码

就 200 行代码,直接贴出来了,不放到 Github 上了。

附锤子阅读APP下载 链接

import requests
import sys
import getopt

PAGE_SIZE = 100
TEMP_FILE = 'result.md'


def api_site_articles(site_id, offset, page_size):
    """订阅号文章列表的接口json返回结果

    :param site_id:
    :param offset:
    :param page_size:
    :return:
    """
    params = {
        'site_id': site_id,
        'offset': offset,
        'page_size': page_size
    }
    url = r'http://reader.smartisan.com/index.php?r=article/getList'
    response = requests.get(url, params)
    return response.json()


def api_search_site(name, offset, page_size):
    """搜索订阅号的接口json返回结果

    :param name:
    :param offset:
    :param page_size:
    :return:
    """
    params = {
        'name': name,
        'offset': offset,
        'page_size': page_size
    }
    url = r'http://reader.smartisan.com/index.php?r=site/search'
    response = requests.get(url, params)
    return response.json()


def search_site(name):
    """订阅号搜索

    有结果返回第一个,无结果返回空

    :param name:
    :return:
    """
    result = api_search_site(name, 0, 20)
    if result['data']['list']:
        site_json = result['data']['list'][0]
        site_id = site_json['id']
        site_name = site_json['name']
        return site_id, site_name
    else:
        return None, None


def search_titles(site_id, offset, page_size, search_name, match_level):
    """根据搜索关键词返回文章

    :param site_id:
    :param offset:
    :param page_size:
    :param search_name:
    :param match_level:
    :return:
    """
    title_list = []
    result = api_site_articles(site_id, offset, page_size)
    for i in result['data']['list']:
        if search_name:
            if i.get('title').find(search_name) > -1:
                title_list.append(i)
            elif match_level == 2:
                if i.get('brief').find(search_name) > -1:
                    title_list.append(i)
        else:
            title_list.append(i)

    return title_list


def get_article_count(site_id):
    """返回当前订阅号的文章总数

    :param site_id:
    :return:
    """
    result = api_site_articles(site_id, 0, 1)
    count = result['data']['count']
    return int(count)


def get_args():
    """
    编写此脚本的理由:
        1. 锤子阅读不支持文章搜索功能
        2. 锤子阅读的文章是经过转码的, 可能会在自己服务器上保留,可用于被封文章查询

    参数定义:
        -h    帮助文档
        -s    订阅号名称
        -k    搜索关键词,不填返回最新的文章
        -n    返回文章数,默认10 
        -m    搜索匹配等级,默认1匹配标题 2匹配标题和摘要
        -o    输出方式, 默认1命令行输出 2写入本地文件到当前目录

    返回:
        文章标题和文章链接

    附:
        锤子阅读官网:https://www.smartisan.com/apps/#/reader
    """

    site = ''
    keyword = ''
    num = 10
    match_level = 1
    output = 1

    try:
        opts, args = getopt.getopt(sys.argv[1:], 'hs:k:n:m:o:', ['help='])
    except getopt.GetoptError:
        print('Error ! Use -h or --help to show this help message')
        sys.exit(2)
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            print(get_args.__doc__)
            sys.exit(0)
        elif opt == '-s':
            site = arg
        elif opt == '-k':
            keyword = arg
        elif opt == '-n':
            num = int(arg)
        elif opt == '-m':
            match_level = int(arg)
        elif opt == '-o':
            output = int(arg)
    return site, keyword, num, match_level, output


def main_by_args(site, keyword, num, match_level, output):

    # 查找订阅号site_id
    site_id, site_name = search_site(site)
    if not site_id:
        print("订阅号不存在")
        exit(0)
    print('正在搜索 {} 的文章'.format(site_name))

    # 获取当前订阅号文章总数,计算遍历最高次数
    count = get_article_count(site_id)
    range_count = int(count / PAGE_SIZE) + 1

    # 遍历查找
    search_list = []
    for i in range(range_count):
        titles = search_titles(site_id, i, PAGE_SIZE, keyword, match_level)
        search_list.extend(titles)
        if len(search_list) > num:
            break

    # 输出
    if not search_list:
        print("无搜索结果")
        exit(0)
    else:
        search_list = search_list[:num]
        if output == 1:
            for i in search_list:
                print('{0} {1}'.format(i.get('title'), i.get('origin_url')))
        elif output == 2:
            with open(TEMP_FILE, 'w', encoding='utf-8') as f:
                for i in search_list:
                    f.write('[{}]({})'.format(i.get('title'), i.get('origin_url')))
                    f.write('\r\n')
                    f.write('\r\n')
            print('结果已成功写入 {} 中'.format(TEMP_FILE))


def main():
    site, keyword, num, match_level, output = get_args()
    main_by_args(site, keyword, num, match_level, output)


if __name__ == '__main__':
    # main_by_args('大象公会', '同性恋', 10, 1, 1)  # debug
    main()

Last modified on 2020-03-11