二次封装requests,构造通用的请求函数

简介: 本章将告诉你该如何去对request模块进行二次封装,暂时并不会告诉你HTTP协议及原理、URL等相关。当然你会使用然后在来阅读此文章一定会另有所获。我已经迫不及待要告诉你这个小秘密,以及想与你交流了。没时间解释了,快来一起和我一起探讨相关的内容吧

官方文档对requests的定义为:Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用。


使用Python写做爬虫的小伙伴一定使用过requests这个模块,初入爬虫的小伙伴也一定写过N个重复的requests,这是你的疑问。当然也一直伴随着我,最近在想对requests如何进行封装一下,让他支持支持通用的函数。若需要使用,直接调用即可。


那么问题来了,如果要写个供自己使用通用的请求函数将会有几个问题


  • requests的请求方式(GET\POST\INPUT等等)


  • 智能识别网站的编码,避免出现乱码


  • 支持文本、二进制(图片、视频等为二进制内容)


  • 以及还需要傻瓜一点,那就是网站的Ua(Ua:User-Agent,基本上网站都会验证接受到请求的Ua。


来初步判断是爬虫还是用户)


那么咱们就针对以上问题开干吧


Requests的安装


在确保python环境搭建完成后直接使用pip或者conda命令进行安装,安装命令如下:


pip install requests
conda install requests# 或者下载过慢点话,可以使用国内的pip镜像源,例如:pip install requests -i  https://pypi.tuna.tsinghua.edu.cn/simple/


安装完成后,效果图如下:


640 (1).jpg


初探requests基本使用


HTTP 中最常见的请求之一就是 GET 请求,下面我们来详细了解利用 requests 库构建 GET 请求的方法。


import requests
response = requests.get('http://httpbin.org/get')# 响应状态码print("response.status_code:", response.status_code)# 响应头print("response.headers:", response.headers)# 响应请求头print("response.request.headers:", response.request.headers)# 响应二进制内容print("response.content:", response.content)# 响应文本print("response.text", response.text)# 返回如下response.status_code: 200response.headers: {'Date': 'Thu, 12 Nov 2020 13:38:05 GMT', 'Content-Type': 'application/json', 'Content-Length': '306', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true'}
response.request.headers: {'User-Agent': 'python-requests/2.24.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
response.content: b'{\n  "args": {}, \n  "headers": {\n    "Accept": "*/*", \n    "Accept-Encoding": "gzip, deflate", \n    "Host": "httpbin.org", \n    "User-Agent": "python-requests/2.24.0", \n    "X-Amzn-Trace-Id": "Root=1-5fad3abd-7516d60b3e951824687a50d8"\n  }, \n  "origin": "116.162.2.166", \n  "url": "http://httpbin.org/get"\n}\n'{  "args": {}, 
  "headers": {    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.24.0", 
    "X-Amzn-Trace-Id": "Root=1-5fad3abd-7516d60b3e951824687a50d8"
  }, 
  "origin": "116.162.2.166", 
  "url": "http://httpbin.org/get"}


requests基本使用已经经过简单的测试了,是否有一点点feel呢?接下来我们直接将它封装为一个函数以供随时调用


示例如下


import requests
urls = 'http://httpbin.org/get'def downloader(url, headers=None):
    response = requests.get(url, headers=headers)    return response
print("downloader.status_code:", downloader(url=urls).status_code)
print("downloader.headers:", downloader(url=urls).headers)
print("downloader.request.headers:", downloader(url=urls).request.headers)
print("downloader.content:", downloader(url=urls).content)
print("downloader.text", downloader(url=urls).text)# 返回效果如上所示,此处省略


以上我们就把,请求方法封装成了一个函数。将基本的url,headers以形参的方式暴露出来,我们只需传入需要请求的url即可发起请求,至此一个简单可复用的请求方法咱们就完成啦。


完~~~


以上照顾新手的就基本完成了,接下来我们搞点真家伙。


二次封装


请求函数的封装


由于请求方式并不一定(有可能是GET也有可能是POST),所以我们并不能智能的确定它是什么方式发送请求的。


Requests中request方法以及帮我们实现了这个方法。我们将他的请求方式暴露出来,写法如下:


urls = 'http://httpbin.org/get'def downloader(url, method=None, headers=None):
    _method = "GET" if not method else method
    response = requests.request(url, method=_method, headers=headers)    return response
print("downloader.status_code:", downloader(url=urls).status_code)
print("downloader.headers:", downloader(url=urls).headers)
print("downloader.request.headers:", downloader(url=urls).request.headers)
print("downloader.content:", downloader(url=urls).content)
print("downloader.text", downloader(url=urls).text)


由于大部分都是GET方法,所以我们定义了一个默认的请求方式。如果需要修改请求方式,只需在调用时传入相对应的方法即可。例如我们可以这样


downloader(urls, method="POST")


文本编码问题


解决由于request的误差判断而造成解码错误,而得到乱码。


此误差造成的原因是可能是响应头的Accept-Encoding,另一个是识别错误


# 查看响应编码response.encoding


此时我们需要借用Python中C语言编写的cchardet这个包来识别响应文本的编码。安装它


pip install cchardet -i  https://pypi.tuna.tsinghua.edu.cn/simple/# 如果pip直接安装失败的话,直接用清华源的镜像。


# 实现智能版的解码:如下encoding = cchardet.detect(response.content)['encoding']def downloader(url, method=None, headers=None):
    _method = "GET" if not method else method
    response = requests.request(url, method=_method, headers=headers)
    encoding = cchardet.detect(response.content)['encoding']    return response.content.decode(encoding)


区分二进制与文本的解析


在下载图片、视频等需获取到其二进制内容。而下载网页文本需要进行encode。


同理,我们只需要将一个标志传进去,从而达到分辨的的效果。例如这样


def downloader(url, method=None, headers=None, binary=False):
    _method = "GET" if not method else method
    response = requests.request(url, method=_method, headers=headers)
    encoding = cchardet.detect(response.content)['encoding']    return response.content if binary else response.content.decode(encoding)


默认Ua


在很多时候,我们拿ua又是复制。又是加引号构建key-value格式。这样有时候仅仅用requests做个测试。就搞的麻烦的很。而且请求过多了,直接就被封IP了。没有自己的ip代理,没有钱有时候还真有点感觉玩不起爬虫。


为了减少被封禁IP的概率什么的,我们添加个自己的Ua池。Ua池的原理很简单,内部就是采用随机的Ua,从而减少被发现的概率.至于为什么可以达到这这样的效果,在这里仅作简单介绍。详细可能要从计算机网络原理说起。


结论就是你一个公司里大多采用的都是同一个外网ip去访问目标网址。那么就意味着可能你们公司有N个人同一个ip去访问目标网址。而封禁做区分的一般由ip访问频率和浏览器的指纹和在一起的什么鬼东东。简单理解为Ua+ip访问频率达到峰值,你IP就对方关小黑屋了。


构建自己的ua池,去添加默认的请求头,

Ua有很多,这里就不放出来了,如果有兴趣可以直接去源码里面拿。直接说原理:构造很多个Ua,然后随机取用。从而降低这个同一访问频率:同时也暴露端口方便你自己传入header


from powerspider.tools.Ua import uaimport requestsdef downloader(url, method=None, header=None, binary=False):
    _headers = header if header else {'User-Agent': ua()}
    _method = "GET" if not method else method
    response = requests.request(url, method=_method, headers=_headers)
    encoding = cchardet.detect(response.content)['encoding']    return response.content if binary else response.content.decode(encoding)


那么基本的文件都已经解决了,不过还不完美。异常处理,错误重试,日志什么都没。这怎么行呢。活既然干了,那就干的漂漂亮亮的。


来让我们加入进来这些东西


import cchardetfrom retrying import retryfrom powerspider import loggerfrom powerspider.tools.Ua import uafrom requests import request, RequestException@retry(stop_max_attempt_number=3, retry_on_result=lambda x: x is None, wait_fixed=2000)def downloader(url, method=None, header=None, timeout=None, binary=False, **kwargs):
    logger.info(f'Scraping {url}')
    _header = {'User-Agent': ua()}
    _maxTimeout = timeout if timeout else 5
    _headers = header if header else _header
    _method = "GET" if not method else method    try:
        response = request(method=_method, url=url, headers=_headers, **kwargs)
        encoding = cchardet.detect(response.content)['encoding']        if response.status_code == 200:            return response.content if binary else response.content.decode(encoding)        elif 200 < response.status_code < 400:
            logger.info(f"Redirect_URL: {response.url}")
        logger.error('Get invalid status code %s while scraping %s', response.status_code, url)    except RequestException as e:
        logger.error(f'Error occurred while scraping {url}, Msg: {e}', exc_info=True)if __name__ == '__main__':
    print(downloader("https://www.baidu.com/", "GET"))


至此,我们的对Requests二次封装,构造通用的请求函数就已经完成了。


源码地址:https://github.com/PowerSpider/PowerSpider/tree/dev


目录
相关文章
|
传感器 监控 API
基于STM32的智能灌溉系统设计与实现
基于STM32的智能灌溉系统设计与实现
1523 1
|
JSON Linux 网络安全
一文搞定:whois数据库查询域名信息(WHOIS)
一文搞定:whois数据库查询域名信息(WHOIS)
6682 1
一文搞定:whois数据库查询域名信息(WHOIS)
|
15天前
|
存储 弹性计算 运维
什么是阿里云服务器ECS,及其优势、购买、使用方式和部署建议与实操命令教程
在云计算技术普及的时代,传统自建机房模式面临硬件采购成本高、部署周期漫长、硬件维护繁琐、资源弹性不足等诸多现实痛点。企业想要搭建业务系统,需要采购服务器硬件、搭建机房环境、部署供电与网络设施,前期投入巨大,业务流量波动时,硬件资源很难快速扩缩容。而云服务器ECS(Elastic Compute Service)作为IaaS层级的核心云计算服务,把计算资源转变为可按需取用的公共基础设施,如同日常使用水电燃气,用户无需采购实体IT硬件,就可以获取虚拟服务器能力,实现资源分钟级交付、弹性伸缩,广泛适配个人开发者、中小企业、大型企业的各类业务场景。本文将完整讲解ECS产品定义、核心优势、产品架构组件、
265 1
Debian 官方源换为国内的源的操作方法
apt-get update 报错,采用更换源的方式解决问题。
60547 0
|
11天前
|
弹性计算 人工智能 运维
新手选购阿里云便宜云服务器解析,38元秒杀轻量/99元ECS/199企业款优缺点深度解析
很多新手第一次接触云服务器,面对琳琅满目的实例规格、计费模式、产品系列很容易陷入选择困难,不清楚轻量应用服务器与ECS之间的本质差别,看到低价活动机型就盲目抢购,最后出现性能不足、带宽受限、架构不匹配业务需求、后期扩容困难等一系列问题。目前阿里云入门级服务器有三款广受关注的活动机型,分别是38元一年轻量应用服务器、99元一年ECS经济型实例、199元一年企业专享ECS实例。这三款产品价格梯度清晰,硬件配置、底层架构、网络模型、运维能力差异明显,适配的业务场景完全不同。本文将从产品底层定位、硬件参数拆解、性能优劣对比、适用业务场景、扩容能力、运维复杂度等维度完整解析三款机型,附带服务器初始化实操
191 0
|
6月前
|
人工智能 NoSQL Java
大模型应用开发2-SpringAI实战
本文介绍了SpringAI框架如何整合大语言模型,并详细讲解了应用开发的关键技术。主要内容包括: 核心功能 支持OpenAI、Ollama等主流平台 封装对话模型、向量计算等功能 提供同步/异步调用方式 关键技术实现 会话记忆管理(内存/Redis) 工具调用(Function Calling) 知识增强(RAG)架构 多模态交互(文本/图像) 典型应用场景 文献阅读助手实现 智能客服系统 文档知识库问答 开发实践 配置向量数据库 处理PDF文档 实现工具调用 兼容阿里云平台 该框架显著简化了大模型应用开发
|
缓存 前端开发 大数据
虚拟列表在Vue3中的具体应用场景有哪些?
虚拟列表在 Vue3 中通过仅渲染可视区域内容,显著提升大数据列表性能,适用于 ERP 表格、聊天界面、社交媒体、阅读器、日历及树形结构等场景,结合 `vue-virtual-scroller` 等工具可实现高效滚动与交互体验。
1217 1
|
存储 算法 关系型数据库
(二十二)全解MySQL之分库分表后带来的“副作用”一站式解决方案!
上篇《分库分表的正确姿势》中已经将分库分表的方法论全面阐述清楚了,总体看下来用一个字形容,那就是爽!尤其是分库分表技术能够让数据存储层真正成为三高架构,但前面爽是爽了,接着一起来看看分库分表后产生一系列的后患问题,注意我这里的用词,是一系列而不是几个,也就是分库分表虽然好,但你要解决的问题是海量的。
1660 4
|
算法 测试技术 Swift
Kimi开源Moonlight-16B-A3B:基于Muon优化器的高效大模型,性能与训练效率双突破!
Kimi开源Moonlight-16B-A3B:基于Muon优化器的高效大模型,性能与训练效率双突破!
868 5
|
SQL 关系型数据库 MySQL
如何确认SQL查询是否使用了索引:详细步骤与技巧
在数据库管理和优化中,确认SQL查询是否有效利用了索引是提升性能的关键步骤
1958 0