GitHub 里老早之前就 Star 了 felixglow/Stock 这个项目,原作者 felixglow 。昨天晚上又想起这个来,于是今儿早上就拿出源码来看――我对其中相当多的部分都不熟悉,有些还是第一次了解。在这里将我的理解记录下来,其中的错谬之处,还望各位大神指正。
实现思路在 README ,作者这样写道:
通过调用新浪股票API,实时查询股票价格
支持查询多支股票,通过threading多线程同时查询结果
通过Queue实现线程池
requests请求接口
optparse实现命令行参数处理
这就是这个项目的主要思路――爬取新浪股票 API 来获取数据,通过 threading 实现多线程以达到同时查询多支股票的目的,最后用 optparse 实现命令行参数的处理。
具体解析 __author__ = 'felix' # 原作者为 felix import requests # requests 用于爬取新浪股票 API import time import sys # sys 用于在解释器中交互 import threading # threading 用于多线程处理 from Queue import Queue # Queue 是有关队列的库 from optparse import OptionParser # OptionParser 用于在命令行中添加选项 class Worker(threading.Thread): # 创建类 Work ,多线程获取 def __init__(self, work_queue, result_queue): # 写入必须绑定的强制属性 self, work_queue, result_queue # __init__ 方法的第一个参数一定是 self ,用于表示创建的实例本身 # 其中,实例是根据类来创建的 # 在 __init__ 方法内部,可将各种属性绑定到 self ,因为 self 之乡创建的实例本身 threading.Thread.__init__(self) self.work_queue = work_queue self.result_queue = result_queue self.start() def run(self): # 增加一个新方法 run while True: func, arg, code_index = self.work_queue.get() # 获取 func, arg, code_index res = func(arg, code_index) self.result_queue.put(res) if self.result_queue.full(): res = sorted([self.result_queue.get() for i in range(self.result_queue.qsize())], key=lambda s: s[0], reverse=True) # sorted() 是用于排序的方法,返回副本,原始输入不变 # Queue.get() 用于获取队列 # Queue.qsize() 返回队列大小 # key=lambda s: s[0]:关键词为 lambda s: s[0] # lambda s: s[0]:匿名函数,返回第一个元素 # reverse=True:降序排列 res.insert(0, ('0', u'名称 股价')) # list.insert() 用于将指定对象插入列表的指定位置 print '***** start *****' for obj in res: print obj[1] print '***** end *****\n' self.work_queue.task_done() # 在完成一项工作后,Queue.task_done() 会向任务已经完成的队列发送一个信号 class Stock(object): # 股票实时价格获取 def __init__(self, code, thread_num): self.code = code self.work_queue = Queue() self.threads = [] self.__init_thread_poll(thread_num) def __init_thread_poll(self, thread_num): self.params = self.code.split(',') # parmas 会向函数传入一个字典 self.params.extend(['s_sh000001', 's_sz399001']) # 默认获取沪指、深指 # extend(): 扩展,与 append() 的区别为,extend() 加入的元素是分别单个加入的 self.result_queue = Queue(maxsize=len(self.params[::-1])) for i in range(thread_num): self.threads.append(Worker(self.work_queue, self.result_queue)) def __add_work(self, stock_code, code_index): self.work_queue.put((self.value_get, stock_code, code_index)) # self.value_get 涉及到下面的装饰器 def del_params(self): for obj in self.params: self.__add_work(obj, self.params.index(obj)) def wait_all_complete(self): for thread in self.threads: if thread.isAlive(): # 判断线程是否是激活的 # 从调用 start() 方法启动线程,到 run() 方法执行完毕或者遇到未处理异常而中断,这段时间内线程是激活的 thread.join() # join() 的作用是阻塞进程直到线程执行完毕 # 依次检查线程池中的线程是否接触,没有结束就阻塞线程直到线程结束 # 如果结束则跳转执行下一个线程的 join() 函数 @classmethod # 装饰器,返回函数的高阶函数 def value_get(cls, code, code_index): slice_num, value_num = 21, 3 name, now = u'――无――', u' ――无――' if code in ['s_sh000001', 's_sz399001']: slice_num = 23 value_num = 1 r = requests.get("http://hq.sinajs.cn/list=%s" % (code,)) # 爬取新浪股票 API res = r.text.split(',') if len(res) > 1: name, now = r.text.split(',')[0][slice_num:], r.text.split(',')[value_num] return code_index, name + ' ' + now if __name__ == '__main__': # 该脚本用语直接运行,而不能被 import parser = OptionParser(description="Query the stock's value.", usage="%prog [-c] [-s] [-t]", version="%prog 1.0") # 生成命令行说明 # %prog 将会以当前程序名的字符串来代替 parser.add_option('-c', '--stock-code', dest='codes', help="the stock's code that you want to query.") # 使用 add_option() 来定义命令行参数,即加入选项 # dest 是储存的变量 parser.add_option('-s', '--sleep-time', dest='sleep_time', default=6, type="int", help='How long does it take to check one more time.') parser.add_option('-t', '--thread-num', dest='thread_num', default=3, type='int', help="thread num.") options, args = parser.parse_args(args=sys.argv[1:]) # 设置好命令行后,用 parse_args() 来解析命令行 assert options.codes, "Please enter the stock code!" # 是否输入股票代码 if filter(lambda s: s[:-6] not in ('sh', 'sz', 's_sh', 's_sz'), options.codes.split(',')): # 股票代码输入是否正确 raise ValueError stock = Stock(options.codes, options.thread_num) while True: stock.del_params() time.sleep(options.sleep_time) # sleep() 用于使程序休眠 我的想法从学习 python 到现在也已经是不短的时间了,但最近觉得自己原地踏步了好久,一直没什么长进,于是想看一看真正有使用价值的代码是什么样子的。上面这部分内容,与其说是 具体解析 ,还不如说是做了所有的注释工作,但对于我这样已经了解 Python 的基本语法和较为初级并常用的第三方库的学习者来说,这样一个不到百行的项目,应该是比较合适的练手材料。对这个项目中所用到的方法和库,有些我只是了解,有些甚至以前还没有听说过,因而过一遍代码思路和细节部分,是比较值得的。
记录截止到东八区 2016 年 12 月 19 日 14:46 ,这个项目一共有 4 个 Watch,37 个 Star 和 37 个 Fork。
最后再次感谢原作者 felix。