鸡仔说:在程序设计中,有很多名词,比如面向对象编程,面向切面编程,面向钩子编程等等。其实他们被设计出来核心作用都是相通的,提高代码的可拓展性,可维护性
>>>import string>>>import random>>>a = [(random.randint(1, 100), _) for _ in string.ascii_lowercase]>>>a[(87, 'a'), (64, 'b'), (60, 'c'), (32, 'd'), (72, 'e'), (37, 'f'), (67, 'g'), (15, 'h'), (76, 'i'), (44, 'j'), (20, 'k'), (54, 'l'), (69, 'm'), (14, 'n'), (30, 'o'), (18, 'p'), (66, 'q'), (57, 'r'), (4, 's'), (91, 't'), (98, 'u'), (87, 'v'), (45, 'w'), (73, 'x'), (47, 'y'), (23, 'z')]>>>sorted(a, key=lambda x: x[0]) # 根据可迭代对象a中第一项进行排序[(4, 's'), (14, 'n'), (15, 'h'), (18, 'p'), (20, 'k'), (23, 'z'), (30, 'o'), (32, 'd'), (37, 'f'), (44, 'j'), (45, 'w'), (47, 'y'), (54, 'l'), (57, 'r'), (60, 'c'), (64, 'b'), (66, 'q'), (67, 'g'), (69, 'm'), (72, 'e'), (73, 'x'), (76, 'i'), (87, 'a'), (87, 'v'), (91, 't'), (98, 'u')]
├── fetch.py├── hooks│ ├── __init__.py│ ├── cookies_hook.py│ └── headers_hook.py└── main.py

class CookiesHook:__COOKIE_MAP = {"www.baidu.com": "BIDUPSID=3D72E33E74AB0913CF112ABEDC1E3464; ",}@classmethoddef process(cls, req):req.cookie = cls.__COOKIE_MAP.get(req.host, "")return req
class HeadersHook:__REFER_MAP = {"www.baidu.com": "https://www.baidu.com/","www.mzitu.com": "https://www.mzitu.com/"}__COMMON_HEADERS = {"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_0) AppleWe""bKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36"}@classmethoddef process(cls, req):req.headers["user-agent"] = req.headers.get('user-agent') or cls.__COMMON_HEADERS["user-agent"]req.headers["referer"] = cls.__REFER_MAP.get(req.host, "")return req
import osimport importlibimport sysimport requestssys.path.append("./hooks")all_files = os.listdir("./hooks")all_hook_files = list(filter(lambda x: x.endswith("_hook.py"), all_files))all_hook_module = list(map(lambda x: x.replace(".py", ""), all_hook_files))# 动态加载 hooks 文件夹下的所有 hookhooks = []for module_name in all_hook_module:hooks.append(importlib.import_module(module_name))def fetch_req(req):# 校验 req 是否存在必须的参数if not req.host or not req.url:raise ValueError(f"req={req} host or url missed")# 这一步就是动态加载所有的 hook 类,这里面方法可以在 hook 文件夹下进行动态增删改,比如你想统计每一个请求的数量,可以增# 加一个 statistic_hook.py 文件,并实现其中的 process 函数for per_hook in hooks:hook_class = getattr(per_hook, dir(per_hook)[0])# 动态执行 process 函数getattr(hook_class, "process")(req)headers = req.headerscookie = req.cookieif cookie:headers["cookie"] = cookieif req.method not in ["post", "POST"]:print("req get headers>>>>", headers)res = requests.get(req.url, headers=headers)else:res = requests.post(req.url, headers=headers)return res.text
from fetch import fetch_reqfrom copy import deepcopyclass Req:def __init__(self):self.host = Noneself.url = Noneself.headers = {}self.cookie = Noneself.method = "get"def __repr__(self):return f'<{self.host}>({self.url})'base_req = Req()baidu_req = deepcopy(base_req)baidu_req.host = "www.baidu.com"baidu_req.url = "https://www.baidu.com/"baidu_res = fetch_req(baidu_req)print(baidu_req)print("baidu_res>>>", baidu_res)# test http_orghttp_org_req = deepcopy(base_req)http_org_req.host = "httpbin.org"http_org_req.url = "https://httpbin.org/"http_org_res = fetch_req(http_org_req)print(http_org_req)print("http_org_res>>>", http_org_res)
钩子编程在软件设计中应用广泛,pyhtoner 不一定听说过这个概念,但一定用到过该技术,比如 python 内置模块 sorted 等
python 中使用钩子函数非常简单,这得益于在 python 中,函数是一等公民
从动手实现 hook 的案例中我们发现,这种基于 AOP 思想的实现,让代码非常易于拓展。符合高内聚,低耦合的设计原则,应该在我们的实际项目中推广

好看的人都点了在看
文章转载自鸡仔说,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。






