Requests二次封裝教届,構(gòu)造通用的請(qǐng)求函數(shù)

本章將告訴你該如何去對(duì)request模塊進(jìn)行二次封裝,暫時(shí)并不會(huì)告訴你HTTP協(xié)議及原理依啰、URL等相關(guān)乎串。當(dāng)然你會(huì)使用然后在來(lái)閱讀此文章一定會(huì)另有所獲。我已經(jīng)迫不及待要告訴你這個(gè)小秘密孔飒,以及想與你交流了灌闺。沒(méi)時(shí)間解釋了,快來(lái)一起和我一起探討相關(guān)的內(nèi)容吧

官方文檔對(duì)requests的定義為:Requests 唯一的一個(gè)非轉(zhuǎn)基因的 Python HTTP 庫(kù)坏瞄,人類可以安全享用桂对。

使用Python寫(xiě)做爬蟲(chóng)的小伙伴一定使用過(guò)requests這個(gè)模塊,初入爬蟲(chóng)的小伙伴也一定寫(xiě)過(guò)N個(gè)重復(fù)的requests鸠匀,這是你的疑問(wèn)蕉斜。當(dāng)然也一直伴隨著我,最近在想對(duì)requests如何進(jìn)行封裝一下缀棍,讓他支持支持通用的函數(shù)宅此。若需要使用,直接調(diào)用即可爬范。

那么問(wèn)題來(lái)了父腕,如果要寫(xiě)個(gè)供自己使用通用的請(qǐng)求函數(shù)將會(huì)有幾個(gè)問(wèn)題

  • requests的請(qǐng)求方式(GET\POST\INPUT等等)
  • 智能識(shí)別網(wǎng)站的編碼,避免出現(xiàn)亂碼
  • 支持文本青瀑、二進(jìn)制(圖片璧亮、視頻等為二進(jìn)制內(nèi)容)
  • 以及還需要傻瓜一點(diǎn)萧诫,那就是網(wǎng)站的Ua(Ua:User-Agent,基本上網(wǎng)站都會(huì)驗(yàn)證接受到請(qǐng)求的Ua枝嘶。來(lái)初步判斷是爬蟲(chóng)還是用戶)

那么咱們就針對(duì)以上問(wèn)題開(kāi)干吧

Requests的安裝

在確保python環(huán)境搭建完成后直接使用pip或者conda命令進(jìn)行安裝帘饶,安裝命令如下:

pip install requests
conda install requests

# 或者下載過(guò)慢點(diǎn)話,可以使用國(guó)內(nèi)的pip鏡像源群扶,例如:
pip install requests -i  https://pypi.tuna.tsinghua.edu.cn/simple/

安裝完成后及刻,效果圖如下:

image

初探requests基本使用

HTTP 中最常見(jiàn)的請(qǐng)求之一就是 GET 請(qǐng)求,下面我們來(lái)詳細(xì)了解利用 requests 庫(kù)構(gòu)建 GET 請(qǐng)求的方法竞阐。

import requests

response = requests.get('http://httpbin.org/get')

# 響應(yīng)狀態(tài)碼
print("response.status_code:", response.status_code)
# 響應(yīng)頭
print("response.headers:", response.headers)
# 響應(yīng)請(qǐng)求頭
print("response.request.headers:", response.request.headers)
# 響應(yīng)二進(jìn)制內(nèi)容
print("response.content:", response.content)
# 響應(yīng)文本
print("response.text", response.text)

# 返回如下
response.status_code: 200
response.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基本使用已經(jīng)經(jīng)過(guò)簡(jiǎn)單的測(cè)試了缴饭,是否有一點(diǎn)點(diǎn)feel呢?接下來(lái)我們直接將它封裝為一個(gè)函數(shù)以供隨時(shí)調(diào)用

示例如下

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)

# 返回效果如上所示馁菜,此處省略

以上我們就把茴扁,請(qǐng)求方法封裝成了一個(gè)函數(shù)。將基本的url汪疮,headers以形參的方式暴露出來(lái)峭火,我們只需傳入需要請(qǐng)求的url即可發(fā)起請(qǐng)求,至此一個(gè)簡(jiǎn)單可復(fù)用的請(qǐng)求方法咱們就完成啦智嚷。

完~~~

以上照顧新手的就基本完成了卖丸,接下來(lái)我們搞點(diǎn)真家伙。

二次封裝

請(qǐng)求函數(shù)的封裝

由于請(qǐng)求方式并不一定(有可能是GET也有可能是POST)盏道,所以我們并不能智能的確定它是什么方式發(fā)送請(qǐng)求的稍浆。

Requests中request方法以及幫我們實(shí)現(xiàn)了這個(gè)方法。我們將他的請(qǐng)求方式暴露出來(lái)猜嘱,寫(xiě)法如下:

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方法衅枫,所以我們定義了一個(gè)默認(rèn)的請(qǐng)求方式。如果需要修改請(qǐng)求方式朗伶,只需在調(diào)用時(shí)傳入相對(duì)應(yīng)的方法即可弦撩。例如我們可以這樣

downloader(urls, method="POST")

文本編碼問(wèn)題

解決由于request的誤差判斷而造成解碼錯(cuò)誤,而得到亂碼论皆。

此誤差造成的原因是可能是響應(yīng)頭的Accept-Encoding益楼,另一個(gè)是識(shí)別錯(cuò)誤

# 查看響應(yīng)編碼
response.encoding

此時(shí)我們需要借用Python中C語(yǔ)言編寫(xiě)的cchardet這個(gè)包來(lái)識(shí)別響應(yīng)文本的編碼。安裝它

pip install cchardet -i  https://pypi.tuna.tsinghua.edu.cn/simple/
# 如果pip直接安裝失敗的話点晴,直接用清華源的鏡像感凤。
# 實(shí)現(xiàn)智能版的解碼:如下
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)

區(qū)分二進(jìn)制與文本的解析

在下載圖片、視頻等需獲取到其二進(jìn)制內(nèi)容粒督。而下載網(wǎng)頁(yè)文本需要進(jìn)行encode陪竿。

同理,我們只需要將一個(gè)標(biāo)志傳進(jìn)去屠橄,從而達(dá)到分辨的的效果萨惑。例如這樣

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)

默認(rèn)Ua

在很多時(shí)候捐康,我們拿ua又是復(fù)制。又是加引號(hào)構(gòu)建key-value格式庸蔼。這樣有時(shí)候僅僅用requests做個(gè)測(cè)試。就搞的麻煩的很贮匕。而且請(qǐng)求過(guò)多了姐仅,直接就被封IP了。沒(méi)有自己的ip代理刻盐,沒(méi)有錢(qián)又時(shí)候還真有點(diǎn)感覺(jué)玩不起爬蟲(chóng)掏膏。

為了減少被封禁IP的概率什么的,我們添加個(gè)自己的Ua池敦锌。Ua池的原理很簡(jiǎn)單馒疹,內(nèi)部就是采用隨機(jī)的Ua,從而減少被發(fā)現(xiàn)的概率.至于為什么可以達(dá)到這這樣的效果乙墙,在這里僅作簡(jiǎn)單介紹颖变。詳細(xì)可能要從計(jì)算機(jī)網(wǎng)絡(luò)原理說(shuō)起。

結(jié)論就是你一個(gè)公司里大多采用的都是同一個(gè)外網(wǎng)ip去訪問(wèn)目標(biāo)網(wǎng)址听想。那么就意味著可能你們公司有N個(gè)人使用同一個(gè)ip去訪問(wèn)目標(biāo)網(wǎng)址腥刹。而封禁做區(qū)分的一般由ip訪問(wèn)頻率和瀏覽器的指紋和在一起的什么鬼東東。簡(jiǎn)單理解為Ua+ip訪問(wèn)頻率達(dá)到峰值汉买,你IP就對(duì)方關(guān)小黑屋了衔峰。

構(gòu)建自己的ua池,去添加默認(rèn)的請(qǐng)求頭蛙粘,

Ua有很多垫卤,這里就不放出來(lái)了,如果有興趣可以直接去源碼里面拿出牧。直接說(shuō)原理:構(gòu)造很多個(gè)Ua穴肘,然后隨機(jī)取用。從而降低這個(gè)同一訪問(wèn)頻率:同時(shí)也暴露端口方便你自己傳入header

from powerspider.tools.Ua import ua
import requests

def 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)

那么基本的文件都已經(jīng)解決了崔列,不過(guò)還不完美梢褐。異常處理,錯(cuò)誤重試赵讯,日志什么都沒(méi)盈咳。這怎么行呢”咭恚活既然干了鱼响,那就干的漂漂亮亮的。

來(lái)讓我們加入進(jìn)來(lái)這些東西

import cchardet
from retrying import retry
from powerspider import logger
from powerspider.tools.Ua import ua
from 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"))

至此组底,我們的對(duì)Requests二次封裝丈积,構(gòu)造通用的請(qǐng)求函數(shù)就已經(jīng)完成了筐骇。

源碼地址:https://github.com/PowerSpider/PowerSpider/tree/dev
公眾號(hào):積跬Coder

期待下次再見(jiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市江滨,隨后出現(xiàn)的幾起案子铛纬,更是在濱河造成了極大的恐慌,老刑警劉巖唬滑,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件告唆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡晶密,警方通過(guò)查閱死者的電腦和手機(jī)擒悬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)稻艰,“玉大人懂牧,你說(shuō)我怎么就攤上這事∽鹞穑” “怎么了僧凤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)运怖。 經(jīng)常有香客問(wèn)我拼弃,道長(zhǎng),這世上最難降的妖魔是什么摇展? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任吻氧,我火速辦了婚禮,結(jié)果婚禮上咏连,老公的妹妹穿的比我還像新娘盯孙。我一直安慰自己,他們只是感情好祟滴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布振惰。 她就那樣靜靜地躺著,像睡著了一般垄懂。 火紅的嫁衣襯著肌膚如雪骑晶。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天草慧,我揣著相機(jī)與錄音桶蛔,去河邊找鬼。 笑死漫谷,一個(gè)胖子當(dāng)著我的面吹牛仔雷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼碟婆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼电抚!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起竖共,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蝙叛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后公给,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體甥温,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年妓布,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宋梧。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匣沼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捂龄,到底是詐尸還是另有隱情释涛,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布倦沧,位于F島的核電站唇撬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏展融。R本人自食惡果不足惜窖认,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望告希。 院中可真熱鬧扑浸,春花似錦、人聲如沸燕偶。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)指么。三九已至酝惧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伯诬,已是汗流浹背晚唇。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留姑廉,地道東北人缺亮。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親萌踱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子葵礼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容