对howdoi的一个简单分析。

曾经看到过下面的这样一段js代码:

try{doSth();
}
catch (e){ask_url = "https://stackoverflow.com/search?q="window.location.href= ask_url + encodeURIComponent(e)
}

howdoi基本就是把这个流程做成了Python脚本。其基本流程如下:

  • step1:利用site语法组装搜索语句(默认指定搜索stackoverflow网站)
  • step2:利用google搜索接口获取搜索引擎第一页排名第一的连接
  • step3:访问该链接,根据排名从高倒下,提取代码块文本
  • step4:提取到就显示到终端,没有提取到就提示未找到答案

当然,howdoi也作了一些其他的工作:

  • 代理设置
  • 既往问题进行缓存,提高下次查询的速度
  • 查询的目标网站可配置
  • 做成Python script脚本命令,方便快捷
  • 代码高亮格式化输出

更多分析请看代码注释:

!/usr/bin/env python######################################################
#
# howdoi - instant coding answers via the command line
# written by Benjamin Gleitzman (gleitz@mit.edu)
# inspired by Rich Jones (rich@anomos.info)
#
######################################################import argparse #用于获取脚本命令行参数
import glob
import os
import random
import re
import requests #用于发送http(s)请求
import requests_cache
import sys
from . import __version__
#用于控制台彩色高亮格式化输出
from pygments import highlight
from pygments.lexers import guess_lexer, get_lexer_by_name
from pygments.formatters.terminal import TerminalFormatter
from pygments.util import ClassNotFound
# 用于网页解析
from pyquery import PyQuery as pqfrom requests.exceptions import ConnectionError
from requests.exceptions import SSLError# 兼容Python2.x和Python3.x的库
if sys.version < '3':import codecsfrom urllib import quote as url_quotefrom urllib import getproxies# 处理unicode: http://stackoverflow.com/a/6633040/305414def u(x):return codecs.unicode_escape_decode(x)[0]
else:from urllib.request import getproxiesfrom urllib.parse import quote as url_quotedef u(x):return x#设置google搜索url
if os.getenv('HOWDOI_DISABLE_SSL'):  # 使用系统环境变量中非SSL的http代替httpsSEARCH_URL = 'http://www.google.com/search?q=site:{0}%20{1}'VERIFY_SSL_CERTIFICATE = False
else:SEARCH_URL = 'https://www.google.com/search?q=site:{0}%20{1}'VERIFY_SSL_CERTIFICATE = True
#设置目标问答网站
URL = os.getenv('HOWDOI_URL') or 'stackoverflow.com'#浏览器UA,用于伪造浏览器请求,防止网站对脚本请求进行屏蔽
USER_AGENTS = ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:11.0) Gecko/20100101 Firefox/11.0','Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0) Gecko/20100 101 Firefox/22.0','Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0',('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.5 (KHTML, like Gecko) ''Chrome/19.0.1084.46 Safari/536.5'),('Mozilla/5.0 (Windows; Windows NT 6.1) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46''Safari/536.5'), )
#格式化答案输出
ANSWER_HEADER = u('--- Answer {0} ---\n{1}')
NO_ANSWER_MSG = '< no answer given >'#设置缓存文件路径
XDG_CACHE_DIR = os.environ.get('XDG_CACHE_HOME',os.path.join(os.path.expanduser('~'), '.cache'))
CACHE_DIR = os.path.join(XDG_CACHE_DIR, 'howdoi')
CACHE_FILE = os.path.join(CACHE_DIR, 'cache{0}'.format(sys.version_info[0] if sys.version_info[0] == 3 else ''))#获取代理(在国内China尤其有用,不解释)
def get_proxies():proxies = getproxies()filtered_proxies = {}for key, value in proxies.items():if key.startswith('http'):if not value.startswith('http'):filtered_proxies[key] = 'http://%s' % valueelse:filtered_proxies[key] = valuereturn filtered_proxiesdef _get_result(url):try:return requests.get(url, headers={'User-Agent': random.choice(USER_AGENTS)}, proxies=get_proxies(),verify=VERIFY_SSL_CERTIFICATE).textexcept requests.exceptions.SSLError as e:print('[ERROR] Encountered an SSL Error. Try using HTTP instead of ''HTTPS by setting the environment variable "HOWDOI_DISABLE_SSL".\n')raise e# 获取google搜索结果中的连接
def _get_links(query):result = _get_result(SEARCH_URL.format(URL, url_quote(query)))html = pq(result)#用pyquery进行解析return [a.attrib['href'] for a in html('.l')] or \[a.attrib['href'] for a in html('.r')('a')]def get_link_at_pos(links, position):if not links:return Falseif len(links) >= position:link = links[position - 1]else:link = links[-1]return link#代码格式化输出函数
def _format_output(code, args):if not args['color']:return codelexer = None# try to find a lexer using the StackOverflow tags# or the query argumentsfor keyword in args['query'].split() + args['tags']:try:lexer = get_lexer_by_name(keyword)breakexcept ClassNotFound:pass# no lexer found above, use the guesserif not lexer:try:lexer = guess_lexer(code)except ClassNotFound:return codereturn highlight(code,lexer,TerminalFormatter(bg='dark'))#利用政策匹配判断连接是否是问题
def _is_question(link):return re.search('questions/\d+/', link)#获取问题连接
def _get_questions(links):return [link for link in links if _is_question(link)]#获取答案(主要是解析stackoverflow的问答页面)
def _get_answer(args, links):links = _get_questions(links)link = get_link_at_pos(links, args['pos'])if not link:return Falseif args.get('link'):return linkpage = _get_result(link + '?answertab=votes')html = pq(page)first_answer = html('.answer').eq(0)#第一个答案instructions = first_answer.find('pre') or first_answer.find('code')#pre和code标签为目标代码块args['tags'] = [t.text for t in html('.post-tag')]if not instructions and not args['all']:text = first_answer.find('.post-text').eq(0).text()elif args['all']:texts = []for html_tag in first_answer.items('.post-text > *'):current_text = html_tag.text()if current_text:if html_tag[0].tag in ['pre', 'code']:texts.append(_format_output(current_text, args))else:texts.append(current_text)texts.append('\n---\nAnswer from {0}'.format(link))text = '\n'.join(texts)else:text = _format_output(instructions.eq(0).text(), args)if text is None:text = NO_ANSWER_MSGtext = text.strip()return textdef _get_instructions(args):links = _get_links(args['query'])if not links:return Falseanswers = []append_header = args['num_answers'] > 1initial_position = args['pos']for answer_number in range(args['num_answers']):current_position = answer_number + initial_positionargs['pos'] = current_positionanswer = _get_answer(args, links)if not answer:continueif append_header:answer = ANSWER_HEADER.format(current_position, answer)answer += '\n'answers.append(answer)return '\n'.join(answers)#启动缓存
def _enable_cache():if not os.path.exists(CACHE_DIR):os.makedirs(CACHE_DIR)requests_cache.install_cache(CACHE_FILE)#清除缓存
def _clear_cache():for cache in glob.glob('{0}*'.format(CACHE_FILE)):os.remove(cache)# 脚本主函数
def howdoi(args):#构造查询(主要是把问号删除)args['query'] = ' '.join(args['query']).replace('?', '')try:return _get_instructions(args) or 'Sorry, couldn\'t find any help with that topic\n'except (ConnectionError, SSLError):return 'Failed to establish network connection\n'#获取用户输入的命令行参数
def get_parser():parser = argparse.ArgumentParser(description='instant coding answers via the command line')parser.add_argument('query', metavar='QUERY', type=str, nargs='*',help='the question to answer')parser.add_argument('-p', '--pos', help='select answer in specified position (default: 1)', default=1, type=int)parser.add_argument('-a', '--all', help='display the full text of the answer',action='store_true')parser.add_argument('-l', '--link', help='display only the answer link',action='store_true')parser.add_argument('-c', '--color', help='enable colorized output',action='store_true')parser.add_argument('-n', '--num-answers', help='number of answers to return', default=1, type=int)parser.add_argument('-C', '--clear-cache', help='clear the cache',action='store_true')parser.add_argument('-v', '--version', help='displays the current version of howdoi',action='store_true')return parser#启动函数
def command_line_runner():parser = get_parser()args = vars(parser.parse_args())# 输出脚本版本if args['version']:print(__version__)return# 清除缓存if args['clear_cache']:_clear_cache()print('Cache cleared successfully')return# 如果没有query,就输出帮助信息if not args['query']:parser.print_help()return# 如果环境变量设置了禁止缓存,就清除缓存if not os.getenv('HOWDOI_DISABLE_CACHE'):_enable_cache()# 彩色输出if os.getenv('HOWDOI_COLORIZE'):args['color'] = True# 如果用户Python版本小于3就进行utf-8编码,如否,就正常启动if sys.version < '3':print(howdoi(args).encode('utf-8', 'ignore'))else:print(howdoi(args))if __name__ == '__main__':command_line_runner()

转载于:https://www.cnblogs.com/taceywong/p/8051095.html

howdoi 简单分析相关推荐

  1. R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集、非线性:基函数展开和样条分析、你简单分析的不重要特征,可能只是线性不显著、而非线性是显著的

    R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集.非线性:基函数展开和样条分析.你简单分析的不重要特征,可能只是线性不显著.而非线性是显著的 目录

  2. [EntLib]微软企业库5.0 学习之路——第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—上篇...

    在完成了后,今天开始介绍企业库中的新模块:Cryptographer(加密模块),这个模块在日常的大多数项目的作用非常重要,例如:网站会员密码.身份证号.网站配置等,通过对信息进行加密可以保证项目数据 ...

  3. FFmpeg资料来源简单分析:libswscale的sws_getContext()

    ===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...

  4. Mac与Phy组成原理的简单分析

    Mac与Phy组成原理的简单分析 2011-12-28 15:30:43 //http://blog.chinaunix.net/uid-20528014-id-3050217.html 本文乃fir ...

  5. python做数据可视化的代码_Python数据可视化正态分布简单分析及实现代码

    Python说来简单也简单,但是也不简单,尤其是再跟高数结合起来的时候... 正态分布(Normaldistribution),也称"常态分布",又名高斯分布(Gaussiandi ...

  6. ASIHTTPRequest源码简单分析

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 ASIHttprequest 是基于CFNetwork的,由于CFNetwork是比较底层的http库,功能比较少, ...

  7. Hessian 源码简单分析

    Hessian 源码简单分析 Hessian 是一个rpc框架, 我们需要先写一个服务端, 然后在客户端远程的调用它即可. 服务端: 服务端通常和spring 做集成. 首先写一个接口: public ...

  8. python预测股票价格tushare_用tushare对股票进行简单分析

    用tushare对股票进行简单分析(仅供交流学习) import numpy as np import pandas as pd import matplotlib.pyplot as plt imp ...

  9. 智能情绪分析技术_简单分析人工智能的表现在计算机网络应用技术中的优势

    简单分析人工智能的表现在计算机网络应用技术中的优势 大数据时代背景下, 计算机网络技术迅猛发展, 而人工智能技术的发展也进一步推动了计算机网络技术的发展, 两者相互融合, 相互促进, 实现了双赢发展. ...

最新文章

  1. 【UIKit】UIView基础学习
  2. linux++tar打包目录,linux中tar命令打包目录与排除目录打包linux操作系统 -电脑资料...
  3. Java程序员从笨鸟到菜鸟全部博客目录
  4. Qt翻译相关类之QDataStream
  5. MATLAB(二)数据的输入
  6. HDFS-文件读写过程
  7. Field 'CID' doesn't have a default value
  8. 记一次library cache lock/library cache pin导致的函数编译hang住分析及处理过程
  9. QObject::connect: No such signal QGraphicsView::mouseMovePoint(QPoint) in ***
  10. mini2440_x35 ubuntu下使用dnw进行传送文件
  11. [Ext JS 7]7.2 事件(Event)
  12. Cocos2d-x场景(Scene)详解
  13. 比想像力更难的,是按自己的价值观行动的勇气
  14. 星空编程 小甲鱼c语言,c++快速入门(小甲鱼)
  15. 《机器学习实战》 自制勘误表 中文版第一版2015年9月第11次印刷版
  16. VB 快速切换wifi软件 [提供案例参考吧]
  17. vue 项目中 zip 压缩包文件下载
  18. VS Code:推荐插件 - HTML格式化(包括JS、CSS)
  19. 使用python解析Wordpress导出的xml文件
  20. word怎么删除参考文献的横线_word2016怎么去掉引用参考文献的横线?

热门文章

  1. html移动滚动彩字字幕特效,如何制作滚动字幕 旋转好莱坞字幕特效效果图(超多滚动效果)...
  2. matlab中quat2angle,RPY_Euler_Quaternion_AngleAxis角度转化:Matlab、Python、Halcon版本
  3. python 将图片旋转多个角度_【技术】把你的朋友变成表情包?Python:So easy
  4. onnx模型部署 python_onnxruntime模型部署流程
  5. [CF446C]DZY Loves Fibonacci Numbers
  6. DAVY的神龙帕夫——读者的心灵故事|十二橄榄枝的传说
  7. Linux中逻辑卷的快照与还原
  8. 【转】ASPX和HTML获取系统根目录的路径
  9. Python—实训day9—使用pandas进行数据预处理
  10. IOS15打包静态库