有了这个,网页反爬限制请求频率易如反掌!

2024-05-15 11:38

本文主要是介绍有了这个,网页反爬限制请求频率易如反掌!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这是「进击的Coder」的第 548 篇技术分享

作者:kingname

来源:未闻 Code

阅读本文大概需要 6 分钟。

在以前的文章里面,我给大家介绍了使用 Python 自带的 LRU 缓存实现带有过期时间的缓存:实现有过期时间的 LRU 缓存。也讲过倒排索引:使用倒排索引极速提高字符串搜索效率。但这些代码对初学者来说比较难,写起来可能会出错。

实际上,这些功能其实都可以使用 Redis 来实现,而且每个功能只需要 1 分钟就能做出来。全文搜索功能在搜索英文的时候,甚至可以智能识别拼写错误的问题。

要实现这些功能,只需要做两件事:

  1. 安装 Redis

  2. Python 安装第三方库:walrus

安装完成以后,我们来看看它有多简单:

带过期时间的缓存装饰器

我们想实现一个装饰器,它装饰一个函数。让我在 1 分钟内多次访问函数的时候,使用缓存的数据;超过 1 分钟以后才重新执行函数的内部代码:

import time
import datetime
from walrus import Databasedb = Database()
cache = db.cache()@cache.cached(timeout=60)
def test():print('函数真正运行起来')now = datetime.datetime.now()return nownow = test()
print('函数返回的数据是:', now)
time.sleep(10) # 等待10秒,此时会使用缓存
print('函数返回的数据是:', test())
time.sleep(5) # 等待5秒,此时依然使用缓存
print('函数返回的数据是:', test())time.sleep(50)  # 让时间超过缓存的时间
print('函数返回的数据是:', test())

运行效果如下图所示:

30fb2dd6c94dbd16a175590ca40ab087.png

全文搜索

我们再来看看全文搜索功能,实现起来也很简单:

from walrus import Databasedb = Database()
search = db.Index('xxx')  # 这个名字随便取
poem1 = 'Early in the day it was whispered that we should sail in a boat, only thou and I, and never a soul in the world would know of this our pilgrimage to no country and to no end.'
poem2 = 'Had I the heavens’ embroidered cloths,Enwrought with golden and silver light'
poem3 = 'to be or not to be, that is a question.'search.add('docid1', poem1) # 第一个参数不能重复
search.add('docid2', poem2)
search.add('docid3', poem3)for doc in search.search('end'):print(doc['content'])

运行效果如下图所示:

86fb4dc62cdef19cc32a05ebf1e139c6.png

如果你想让他兼容拼写错误,那么可以把search = db.Index('xxx')改成search = db.Index('xxx’, metaphone=True),运行效果如下图所示:

19e5a177107630da6708555fe6ec521f.png

不过遗憾的是,这个全文搜索功能只支持英文。

频率限制

我们有时候要限制调用某个函数的频率,或者网站的某个接口要限制 IP 的访问频率。这个时候,使用walrus也可以轻松实现:

import time
from walrus import Databasedb = Database()
rate = db.rate_limit('xxx', limit=5, per=60) # 每分钟只能调用5次for _ in range(35):if rate.limit('xxx'):print('访问频率太高!')else:print('还没有触发访问频率限制')time.sleep(2)

运行效果如下图所示:

269764a4c9064d549326c64adfb305b4.png

其中参数limit表示能出现多少次,per表示在多长时间内。

rate.limit只要传入相同的参数,那么就会开始检查这个参数在设定的时间内出现的频率。

你可能觉得这个例子并不能说明什么问题,那么我们跟 FastAPI 结合一下,用来限制 IP 访问接口的频率。编写如下代码:

from walrus import Database, RateLimitException
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponsedb = Database()
rate = db.rate_limit('xxx', limit=5, per=60) # 每分钟只能调用5次app = FastAPI()@app.exception_handler(RateLimitException)
def parse_rate_litmit_exception(request: Request, exc: RateLimitException):msg = {'success': False, 'msg': f'请喝杯茶,休息一下,你的ip: {request.client.host}访问太快了!'}return JSONResponse(status_code=429, content=msg)@app.get('/')
def index():return {'success': True}@app.get('/important_api')
@rate.rate_limited(lambda request: request.client.host)
def query_important_data(request: Request):data = '重要数据'return {'success': True, 'data': data}

上面代码定义了一个全局的异常拦截器:

@app.exception_handler(RateLimitException)
def parse_rate_litmit_exception(request: Request, exc: RateLimitException):msg = {'success': False, 'msg': f'请喝杯茶,休息一下,你的ip: {request.client.host}访问太快了!'}return JSONResponse(status_code=429, content=msg)

在整个代码的任何地方抛出了RateLimitException异常,就会进入这里的逻辑中。

使用装饰器@rate.rate_limited装饰一个路由函数,并且这个装饰器要更靠近函数。路由函数接收什么参数,它就接收什么参数。在上面的例子中,我们只接收了request参数,用于获取访问者的 IP。发现这个 IP 的访问频率超过了限制,就抛出一个RateLimitException。于是前面定义好的全局拦截器就会拦截RateLimitException异常,拦截到以后返回我们定义好的报错信息。

在频率范围内访问页面,返回正常的 JSON 数据:

424faeb2621c53f620f8a2bf8e81afc5.png

频率超过设定的值以后,访问页面就会报错,如下图所示:

a146423930a307d80ae672d18c4e39cb.png

总结

walrusredis-py进行了很好的二次封装,用起来非常顺手。除了上面我提到的三个功能外,它还可以实现几行代码生成布隆过滤器,实现自动补全功能,实现简易图数据库等等。大家可以访问它的官方文档了解详细使用说明[1]。

参考文献

[1] 官方文档了解详细使用说明: https://walrus.readthedocs.io/en/latest/getting-started.html

8eb1597398a2bc88f48bb3381a8b7183.png

End

崔庆才的新书《Python3网络爬虫开发实战(第二版)》已经正式上市了!书中详细介绍了零基础用 Python 开发爬虫的各方面知识,同时相比第一版新增了 JavaScript 逆向、Android 逆向、异步爬虫、深度学习、Kubernetes 相关内容,‍同时本书已经获得 Python 之父 Guido 的推荐,目前本书正在七折促销中!

内容介绍:《Python3网络爬虫开发实战(第二版)》内容介绍

6fe0c931bce3db7552f07fc4d6d370f7.png

扫码购买

ad2390fa56293f2046c9a1896495b5af.png

好文和朋友一起看~

这篇关于有了这个,网页反爬限制请求频率易如反掌!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/991732

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

poj 2135 有流量限制的最小费用最大流

题意: 农场里有n块地,其中约翰的家在1号地,二n号地有个很大的仓库。 农场有M条道路(双向),道路i连接着ai号地和bi号地,长度为ci。 约翰希望按照从家里出发,经过若干块地后到达仓库,然后再返回家中的顺序带朋友参观。 如果要求往返不能经过同一条路两次,求参观路线总长度的最小值。 解析: 如果只考虑去或者回的情况,问题只不过是无向图中两点之间的最短路问题。 但是现在要去要回

poj 3422 有流量限制的最小费用流 反用求最大 + 拆点

题意: 给一个n*n(50 * 50) 的数字迷宫,从左上点开始走,走到右下点。 每次只能往右移一格,或者往下移一格。 每个格子,第一次到达时可以获得格子对应的数字作为奖励,再次到达则没有奖励。 问走k次这个迷宫,最大能获得多少奖励。 解析: 拆点,拿样例来说明: 3 2 1 2 3 0 2 1 1 4 2 3*3的数字迷宫,走两次最大能获得多少奖励。 将每个点拆成两个

poj 2195 bfs+有流量限制的最小费用流

题意: 给一张n * m(100 * 100)的图,图中” . " 代表空地, “ M ” 代表人, “ H ” 代表家。 现在,要你安排每个人从他所在的地方移动到家里,每移动一格的消耗是1,求最小的消耗。 人可以移动到家的那一格但是不进去。 解析: 先用bfs搞出每个M与每个H的距离。 然后就是网络流的建图过程了,先抽象出源点s和汇点t。 令源点与每个人相连,容量为1,费用为

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了

EasyPlayer.js网页H5 Web js播放器能力合集

最近遇到一个需求,要求做一款播放器,发现能力上跟EasyPlayer.js基本一致,满足要求: 需求 功性能 分类 需求描述 功能 预览 分屏模式 单分屏(单屏/全屏) 多分屏(2*2) 多分屏(3*3) 多分屏(4*4) 播放控制 播放(单个或全部) 暂停(暂停时展示最后一帧画面) 停止(单个或全部) 声音控制(开关/音量调节) 主辅码流切换 辅助功能 屏

禁止复制的网页怎么复制

禁止复制的网页怎么复制 文章目录 禁止复制的网页怎么复制前言准备工作操作步骤一、在浏览器菜单中找到“开发者工具”二、点击“检查元素(inspect element)”按钮三、在网页中选取需要的片段,锁定对应的元素四、复制被选中的元素五、粘贴到记事本,以`.html`为后缀命名六、打开`xxx.html`,优雅地复制 前言 在浏览网页的时候,有的网页内容无法复制。比如「360

火语言RPA流程组件介绍--浏览网页

🚩【组件功能】:浏览器打开指定网址或本地html文件 配置预览 配置说明 网址URL 支持T或# 默认FLOW输入项 输入需要打开的网址URL 超时时间 支持T或# 打开网页超时时间 执行后后等待时间(ms) 支持T或# 当前组件执行完成后继续等待的时间 UserAgent 支持T或# User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器

起点中文网防止网页调试的代码展示

起点中文网对爬虫非常敏感。如图,想在页面启用调试后会显示“已在调试程序中暂停”。 选择停用断点并继续运行后会造成cpu占用率升高电脑卡顿。 经简单分析网站使用了js代码用于防止调试并在强制继续运行后造成电脑卡顿,代码如下: function A(A, B) {if (null != B && "undefined" != typeof Symbol && B[Symbol.hasInstan

Java http请求示例

使用HttpURLConnection public static String httpGet(String host) {HttpURLConnection connection = null;try {URL url = new URL(host);connection = (HttpURLConnection) url.openConnection();connection.setReq