0
点赞
收藏
分享

微信扫一扫

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

MaxWen 2021-09-28 阅读 78

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

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

1.requests 的请求方式 (GET\POST\INPUT 等等)
2.智能识别网站的编码,避免出现乱码
3.支持文本、二进制 (图片、视频等为二进制内容)
4.以及还需要傻瓜一点,那就是网站的 Ua (Ua:User-Agent,基本上网站都会验证接受到请求的 Ua。来初步判断是爬虫还是用户)

下面对requests库的使用进行一个简单的介绍
    import requests
    response = requests.get("http://www.baidu.com")

   # 响应状态码
    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)
    # 查看响应编码,此方法判断的编码格式可能会存在误差
    print("response.encoding", response.encoding)
文本编码问题

解决由于 request 的误差判断而造成解码错误,而得到乱码。此误差造成的原因是可能是响应头的 Accept-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 fake_useragent import UserAgent
import requests
import random

def downloader(url, method=None, header=None, binary=False):
    _headers = header if header else {'User-Agent': UserAgent().random}
    _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)
我们将代码进行整合并增加异常处理和日志输出
from loguru import logger
from fake_useragent import UserAgent
from retrying import retry
import random
import requests
import cchardet


@retry(stop_max_attempt_number=3, retry_on_result=lambda x: x is None, wait_fixed=2000)
def downloader(url, method=None, headers=None, timeout=None, binary=False, **kwargs):
    logger.info("Scraping: {}".format(url))
    _header = {"User-Agent": UserAgent().random}
    _maxTimeout = timeout if timeout else 5
    _headers = headers if headers else _header
    _method = method if method else "GET"
    try:
        response = requests.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("Redirect_URL: {}" .format(response.url))
        logger.error('Get invalid status code {status_code} while scraping {url}'.format(status_code=response.status_code, url=url))
    except Exception as e:
        logger.error("Error occurred while scraping {url}, Msg: {e}".format(url=url, e=e))


if __name__ == "__main__":
    # test()
    print(downloader(url="http://www.baidu.com", method="GET"))

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

举报

相关推荐

0 条评论