共计 6529 个字符,预计需要花费 17 分钟才能阅读完成。
在做爬虫的时候,咱们往往可能这些状况:
- 网站比较复杂,会碰到很多反复申请。
- 有时候爬虫意外中断了,但咱们没有保留爬取状态,再次运行就须要从新爬取。
还有诸如此类的问题。
那怎么解决这些反复爬取的问题呢?大家很可能都想到了“缓存”,也就是说,爬取过一遍就间接跳过爬取。
那个别怎么做呢?
比方我写一个逻辑,把曾经爬取过的 URL 保留到文件或者数据库外面,每次爬取之前检查一下是不是在列表或数据库外面就好了。
是的,这个思路没问题,但有没有想过这些问题:
- 写入到文件或者数据库可能是永久性的,如果我想管制缓存的无效工夫,那就还得有个过期工夫管制。
- 这个缓存依据什么来判断?如果仅仅是 URL 自身够吗?还有 Request Method、Request Headers 呢,如果它们不一样了,那还要不要用缓存?
- 如果咱们有好多我的项目,难道都没有一个通用的解决方案吗?
确实是些问题,实现起来的确要思考很多问题。
不过不必放心,明天给大家介绍一个神器,能够帮忙咱们统统解决如上的问题。
介绍
它就是 requests-cache,是 requests 库的一个扩大包,利用它咱们能够十分不便地实现申请的缓存,间接失去对应的爬取后果。
上面咱们来介绍下它的应用。
装置
装置非常简单,应用 pip3 即可:
pip3 install requests-cache
装置结束之后咱们来理解下它的根本用法。
根本用法
上面咱们首先来看一个根底实例:
import requests
import time
start = time.time()
session = requests.Session()
for i in range(10):
session.get('http://httpbin.org/delay/1')
print(f'Finished {i + 1} requests')
end = time.time()
print('Cost time', end - start)
这里咱们申请了一个网站,是 http://httpbin.org/delay/1,这个网站模仿了一秒提早,也就是申请之后它会在 1 秒之后才会返回响应。
这里申请了 10 次,那就至多得须要 10 秒能力齐全运行结束。
运行后果如下:
Finished 1 requests
Finished 2 requests
Finished 3 requests
Finished 4 requests
Finished 5 requests
Finished 6 requests
Finished 7 requests
Finished 8 requests
Finished 9 requests
Finished 10 requests
Cost time 13.17966604232788
能够看到,这里一共用了 13 秒。
那如果咱们用上 requests-cache 呢?后果会怎么?
代码改写如下:
import requests_cache
import time
start = time.time()
session = requests_cache.CachedSession('demo_cache')
for i in range(10):
session.get('http://httpbin.org/delay/1')
print(f'Finished {i + 1} requests')
end = time.time()
print('Cost time', end - start)
这里咱们申明了一个 CachedSession,将本来的 Session 对象进行了替换,还是申请了 10 次。
运行后果如下:
Finished 1 requests
Finished 2 requests
Finished 3 requests
Finished 4 requests
Finished 5 requests
Finished 6 requests
Finished 7 requests
Finished 8 requests
Finished 9 requests
Finished 10 requests
Cost time 1.6248838901519775
能够看到,一秒多就爬取结束了!
产生了什么?
这时候咱们能够发现,在本地生成了一个 demo_cache.sqlite 的数据库。
咱们关上之后能够发现外面有个 responses 表,外面多了一个 key-value 记录,如图所示:
咱们能够能够看到,这个 key-value 记录中的 key 是一个 hash 值,value 是一个 Blob 对象,外面的内容就是 Response 的后果。
能够猜到,每次申请都会有一个对应的 key 生成,而后 requests-cache 把对应的后果存储到了 SQLite 数据库中了,后续的申请和第一次申请的 URL 是一样的,通过一些计算它们的 key 也都是一样的,所以后续 2-10 申请就立马返回了。
是的,利用这个机制,咱们就能够跳过很多反复申请了,大大节俭爬取工夫。
Patch 写法
然而,方才咱们在写的时候把 requests 的 session 对象间接替换了。有没有别的写法呢?比方我不影响以后代码,只在代码后面加几行初始化代码就实现 requests-cache 的配置呢?
当然是能够的,代码如下:
import time
import requests
import requests_cache
requests_cache.install_cache('demo_cache')
start = time.time()
session = requests.Session()
for i in range(10):
session.get('http://httpbin.org/delay/1')
print(f'Finished {i + 1} requests')
end = time.time()
print('Cost time', end - start)
这次咱们间接调用了 requests-cache 库的 install_cache 办法就好了,其余的 requests 的 Session 照常应用即可。
咱们再运行一遍:
Finished 1 requests
Finished 2 requests
Finished 3 requests
Finished 4 requests
Finished 5 requests
Finished 6 requests
Finished 7 requests
Finished 8 requests
Finished 9 requests
Finished 10 requests
Cost time 0.018644094467163086
这次比上次更快了,为什么呢?因为这次所有的申请都命中了 Cache,所以很快返回了后果。
后端配置
方才咱们晓得了,requests-cache 默认应用了 SQLite 作为缓存对象,那这个能不能换啊?比方用文件,或者其余的数据库呢?
天然是能够的。
比方咱们能够把后端换成本地文件,那能够这么做:
import time
import requests
import requests_cache
requests_cache.install_cache('demo_cache', backend='filesystem')
start = time.time()
session = requests.Session()
for i in range(10):
session.get('http://httpbin.org/delay/1')
print(f'Finished {i + 1} requests')
end = time.time()
print('Cost time', end - start)
这里咱们增加了一个 backend 参数,而后指定为 filesystem,这样运行之后本地就会生成一个 demo_cache 的文件夹用作缓存,如果不想用缓存的话把这个文件夹删了就好了。
当然咱们还能够更改缓存文件夹的地位,比方:
requests_cache.install_cache('demo_cache', backend='filesystem', use_temp=True)
这里增加一个 use_temp
参数,缓存文件夹便会应用零碎的长期目录,而不会在代码区创立缓存文件夹。
当然也能够这样:
requests_cache.install_cache('demo_cache', backend='filesystem', use_cache_dir=True)
这里增加一个 use_cache_dir
参数,缓存文件夹便会应用零碎的专用缓存文件夹,而不会在代码区创立缓存文件夹。
另外除了文件系统,requests-cache 也反对其余的后端,比方 Redis、MongoDB、GridFS 甚至内存,但也须要对应的依赖库反对,具体能够参见下表:
Backend | Class | Alias | Dependencies |
---|---|---|---|
SQLite | SQLiteCache |
'sqlite' |
|
Redis | RedisCache |
'redis' |
redis-py |
MongoDB | MongoCache |
'mongodb' |
pymongo |
GridFS | GridFSCache |
'gridfs' |
pymongo |
DynamoDB | DynamoDbCache |
'dynamodb' |
boto3 |
Filesystem | FileCache |
'filesystem' |
|
Memory | BaseCache |
'memory' |
比方应用 Redis 就能够改写如下:
backend = requests_cache.RedisCache(host='localhost', port=6379)
requests_cache.install_cache('demo_cache', backend=backend)
Filter
当然,咱们有时候也想指定有些申请不缓存,比方只缓存 POST 申请,不缓存 GET 申请,那能够这样来配置:
import time
import requests
import requests_cache
requests_cache.install_cache('demo_cache2', allowable_methods=['POST'])
start = time.time()
session = requests.Session()
for i in range(10):
session.get('http://httpbin.org/delay/1')
print(f'Finished {i + 1} requests')
end = time.time()
print('Cost time for get', end - start)
start = time.time()
for i in range(10):
session.post('http://httpbin.org/delay/1')
print(f'Finished {i + 1} requests')
end = time.time()
print('Cost time for post', end - start)
这里咱们增加了一个 allowable_methods
指定了一个过滤器,只有 POST 申请会被缓存,GET 申请就不会。
看下运行后果:
Finished 1 requests
Finished 2 requests
Finished 3 requests
Finished 4 requests
Finished 5 requests
Finished 6 requests
Finished 7 requests
Finished 8 requests
Finished 9 requests
Finished 10 requests
Cost time for get 12.916549682617188
Finished 1 requests
Finished 2 requests
Finished 3 requests
Finished 4 requests
Finished 5 requests
Finished 6 requests
Finished 7 requests
Finished 8 requests
Finished 9 requests
Finished 10 requests
Cost time for post 1.2473630905151367
这时候就看到 GET 申请因为没有缓存,就花了 12 多秒才完结,而 POST 因为应用了缓存,一秒多就完结了。
另外咱们还能够针对 Response Status Code 进行过滤,比方只有 200 会缓存,则能够这样写:
import time
import requests
import requests_cache
requests_cache.install_cache('demo_cache2', allowable_codes=(200,))
当然咱们还能够匹配 URL,比方针对哪种 Pattern 的 URL 缓存多久,则能够这样写:
urls_expire_after = {'*.site_1.com': 30, 'site_2.com/static': -1}
requests_cache.install_cache('demo_cache2', urls_expire_after=urls_expire_after)
这样的话,site_1.com 的内容就会缓存 30 秒,site_2.com/static 的内容就永远不会过期。
当然,咱们也能够自定义 Filter。
Cache Headers
除了咱们自定义缓存,requests-cache 还反对解析 HTTP Request / Response Headers 并依据 Headers 的内容来缓存。
比如说,咱们晓得 HTTP 外面有个 Cache-Control
的 Request / Response Header,它能够指定浏览器要不要对本次申请进行缓存,那 requests-cache 怎么来反对呢?
实例如下:
import time
import requests
import requests_cache
requests_cache.install_cache('demo_cache3')
start = time.time()
session = requests.Session()
for i in range(10):
session.get('http://httpbin.org/delay/1',
headers={'Cache-Control': 'no-store'})
print(f'Finished {i + 1} requests')
end = time.time()
print('Cost time for get', end - start)
start = time.time()
这里咱们在 Request Headers 外面加上了 Cache-Control
为 no-store
,这样的话,即便咱们申明了缓存那也不会失效。
当然 Response Headers 的解析也是反对的,咱们能够这样开启:
requests_cache.install_cache('demo_cache3', cache_control=True)
如果咱们配置了这个参数,那么 expire_after
的配置就会被笼罩而不会失效。
总结
好了,到当初为止,一些根本配置、过期工夫配置、后端配置、过滤器配置等根本常见的用法就介绍到这里啦!
如果你感觉文章还不错,欢送关注公众号:Python 编程学习圈 ,或是返回编程学习网,理解更多编程技术常识,还有大量干货学习材料能够支付!