好吧,我知道是大半夜……,但我还是觉得赶紧花上半个小时,把这最新的想法分享出来是值得的~直接进入正题~ 我们来模拟一个场景,需要你去抓去一个页面,然后这个页面有好多url也要分别去抓取,而进入这些子url后,还有数据要抓取。简单点,我们就按照三层来看,那我们的代码就是如下:
def func_top(url): data_dict= {} #在页面上获取到子url sub_urls = xxxx data_list = [] for it in sub_urls: data_list.append(func_sub(it)) data_dict['data'] = data_list return data_dict def func_sub(url): data_dict= {} #在页面上获取到子url bottom_urls = xxxx data_list = [] for it in bottom_urls: data_list.append(func_bottom(it)) data_dict['data'] = data_list return data_dict def func_bottom(url): #获取数据 data = xxxx return data
func_top是上层页面的处理函数,func_sub是子页面的处理函数,func_bottom是最深层页面的处理函数,func_top会在取到子页面url后遍历调用func_sub,func_sub也是同样。 如果正常情况下,这样确实已经满足需求了,但是偏偏这个你要抓取的网站可能极不稳定,经常链接不上,导致数据拿不到。 于是这个时候你有两个选择:
- 1.遇到错误就停止,之后重新从断掉的位置开始重新跑
- 2.遇到错误继续,但是要在之后重新跑一遍,这个时候已经有的数据不希望再去网站拉一次,而只去拉没有取到的数据
对第一种方案基本无法实现,因为如果别人网站的url调整顺序,那么你记录的位置就无效了。那么只有第二种方案,说白了,就是要把已经拿到的数据cache下来,等需要的时候,直接从cache里面取。 OK,目标已经有了,怎么实现呢? 如果是在C++中的,这是个很麻烦的事情,而且写出来的代码必定丑陋无比,然而庆幸的是,我们用的是python,而python对函数有装饰器。 所以实现方案也就有了: 定义一个装饰器,如果之前取到数据,就直接取cache的数据;如果之前没有取到,那么就从网站拉取,并且存入cache中. 代码如下:
import os import hashlib def deco_args_recent_cache(category='dumps'): ''' 装饰器,返回最新cache的数据 ''' def deco_recent_cache(func): def func_wrapper(*args, **kargs): sig = _mk_cache_sig(*args, **kargs) data = _get_recent_cache(category, func.__name__, sig) if data is not None: return data data = func(*args, **kargs) if data is not None: _set_recent_cache(category, func.__name__, sig, data) return data return func_wrapper return deco_recent_cache def _mk_cache_sig(*args, **kargs): ''' 通过传入参数,生成唯一标识 ''' src_data = repr(args) + repr(kargs) m = hashlib.md5(src_data) sig = m.hexdigest() return sig def _get_recent_cache(category, func_name, sig): full_file_path = '%s/%s/%s' % (category, func_name, sig) if os.path.isfile(full_file_path): return eval(file(full_file_path,'r').read()) else: return None def _set_recent_cache(category, func_name, sig, data): full_dir_path = '%s/%s' % (category, func_name) if not os.path.isdir(full_dir_path): os.makedirs(full_dir_path) full_file_path = '%s/%s/%s' % (category, func_name, sig) f = file(full_file_path, 'w+') f.write(repr(data)) f.close()
然后,我们只需要在每个func_top,func_sub,func_bottom都加上deco_args_recent_cache这个装饰器即可~~ 搞定!这样做最大的好处在于,因为top,sub,bottom,每一层都会dump数据,所以比如某个sub层数据dump之后,是根本不会走到他所对应的bottom层的,减少了大量的开销! OK,就这样~ 人生苦短,我用python!
感谢依云的提醒,python3居然原生支持了这种功能!链接如下: http://docs.python.org/py3k/whatsnew/3.2.html#functools
依云 on #
嗯,我就喜欢这样的装饰品,所以毫不犹豫地装了 Python 3.2——
http://docs.python.org/py3k/whatsnew/3.2.html#functools
Reply
依云 on #
s/品/器
Reply
Dante on #
python居然原生支持了了,太强了!
啊,开始纠结是不是要往python3上迁移了。。。
Reply
依云 on #
迁移吧,Vim 的 Python3 支持的 bug 我都搞定了,一直都没人测试呢。。。
Reply
Dante on #
唉,django一直不肯支持python3,也是我一直不敢迁移的原因。。
Reply
== on #
请问,win 上的 gvim 可以设置 程序半透明 或者 背景图片 吗?谢谢。网上的都是 linux 下在终端中用 vim 并设置终端透明
Reply
saalihmao on #
对python不是很熟,不过读读代码看上去就是装饰器模式嘛~ 这个所有带有一点函数式味道的语言都可以实现的,c++也是可以的,函数对象+装饰器模式就可以~ 不丑陋的 : )
Reply
saalihmao on #
大概是这样
class base
{
public:
int operator()(char* some_para) = 0;
};
class deco:public base
{
base* pBase;
public:
explicit deco(const base* p){...;}
int operator()(char* some_para)
{
//do something
...
(*pBase)(some_para);
....
//still do something
}
}
Reply
saalihmao on #
呃 deco类的构造函数参数类型应该把const去掉~ : ) 失误失误
Reply
Dante on #
不过总感觉c++用某些设计模式会比较别扭,包括语法,效率,之类的,之前写bayonet项目时,c++用到状态模式,到处是指针,挺郁闷。。
还有,博客支持pre标签哦,代码显示会友好一些,留言页面有说明~
Reply
saalihmao on #
c++的面向对象方面确实是这样~~ 一旦用到继承之类的就满天指针,不方便,而且没有GC内存分配的效率也很成问题~~
不过也不一定非要用对象嘛~ 类型也是可以的,就像这个问题这样可能更好些
<pre lang="cpp" line="14">
//方便起见, 约定这些函数/函数类型参数都是char*, 返回类型都是int
template
class deco
{
public:
int operator()(char* some_para)
{
//do sth
func(some_para);
//still do sth
}
};
</pre>
Reply
saalihmao on #
呵呵pre标签不支持template ...?
Reply
Dante on #
呃,你是说尖括号转义了吗?
Reply
saalihmao on #
嗯= = 尖括号被吃掉了~ 应该怎么写?~
Reply
Dante on #
<pre lang="cpp" line="1">
template <T>
</pre>
我试一下
Reply
Dante on #
好像把lang写上就可以了,我写的是cpp类型。
Reply
saalihmao on #
测试下...
<pre lang="cpp">
template
class deco
{
public:
int operator()(char* some_para)
{
//do sth
func(some_para);
//still do sth
}
};
</pre>
Reply
saalihmao on #
还是不行... 换个浏览器
<pre lang="cpp">
template
template
template
</pre>
Reply
虫子的海飞丝 on #
def _get_recent_cache(category, func_name, sig):这个装饰器有缺陷啊,func_name这个参数还不如去掉。
Reply
Dante on #
对,确实冗余了,以前还不熟悉
Reply