requests 是用 python 語言編寫的一個開源的HTTP庫,可以通過 requests 庫編寫 python 代碼發送網絡請求,其簡單易用,是編寫爬蟲程序時必知必會的一個模塊。
requests 模塊的作用
發送網絡請求,獲取響應數據。
中文文檔:?https://docs.python-requests.org/zh_CN/latest/index.html
requests 模塊的安裝
安裝命令如下:
pip install requests或者pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple
具體安裝運行如下圖所示:
查看安裝好的 requests 模塊的信息
pip show requests
具體運行如下圖所示:
requests 模塊的基本使用
知識點:
- 掌握 requests 發送 GET 請求
- 掌握 response 對象的基本屬性
- 掌握 response.text 和 response.content 的區別
- 掌握 requests 發送自定義請求頭的方式
- 掌握 requests 發送帶參數的get請求
1. requests 發送 GET 請求
使用 requests 模塊發送 GET 請求的語法是:?requests.get(url), 調用完該方法之后會返回一個?response?響應對象。
需求:通過 requests 向百度首頁發送請求,獲取百度首頁的數據。
# 1. 導入request模塊
import requests# 2. 準備目標url地址
url = 'https://www.baidu.com'# 3. 向目標url地址發送get請求
response = requests.get(url)# 4. 打印響應內容
print(response.text)
運行代碼結果如下:
2. response 響應對象
觀察上邊代碼運行結果發現,有好多亂碼;這是因為編解碼使用的字符集不同早造成的;我們嘗試使用下邊的辦法來解決中文亂碼問題。
代碼如下:
# 1. 導入request模塊
import requests# 2. 準備目標url地址
url = 'https://www.baidu.com'# 3. 向目標url地址發送get請求
response = requests.get(url)# 4. 打印響應內容
# print(response.text)
print(response.content.decode()) # 原始響應內容,類型bytes:沒有進行解碼響應內容# 注意:爬蟲程序默認不會自動保存cookie,爬蟲程序請求時默認也不會自動攜帶cookie
運行代碼結果如下:
2.1 response.content 和 response.text 的區別
response.content
- 返回類型: bytes
- 解碼類型: 沒有指定,原始響應內容,沒有進行解碼
- 指定編碼方式:?
response.content.decode('指定編碼字符集')
- 注意:?
response.content.decode()
?默認使用 utf-8 編碼方式
response.text
- 返回類型: str
- 解碼類型: requests 模塊自動根據 HTTP 頭部對響應的編碼作出有根據的推測,推測的文本編碼
response.text = response.content.decode('推測出的編碼字符集')
獲取網頁源碼的方式:
- response.content.decode()
- response.content.decode('gbk')
- response.text
以上三種方法從前往后嘗試,能夠100%的解決所有網頁解碼的問題, 推薦優先使用:?response.content.decode()
2.2 response 響應對象其他屬性和方法
- response.url?: 響應的url地址,有時候響應的 url 地址和請求的 url 地址不一樣。
- response.status_code?: 獲取響應狀態碼。
- response.request.headers?: 獲取響應對應的請求頭信息。
- response.headers?: 獲取響應頭信息。
- response.request._cookies?: 響應對應請求攜帶的cookie,返回cookieJar類型。
- response.cookies?: 響應時設置的 cookie,返回cookieJar類型。
- response.json()?: 自動將 json 字符串類型的響應內容轉換為 python 對象(dict or list)。
示例代碼如下:
# 1. 導入request模塊
import requests# 2. 準備目標url地址
url = 'https://www.baidu.com'# 3. 向目標url地址發送get請求
response = requests.get(url)# 4. 打印響應內容
print(response.url) # 打印響應的url
print(response.status_code) # 打印響應的狀態碼
print(response.request.headers) # 打印響應對象的請求頭
print(response.headers) # 打印響應頭
print(response.request._cookies) # 打印請求攜帶的cookies
print(response.cookies) # 打印響應設置的cookies
示例代碼運行結果如下:
2.3 練習-保存網絡圖片
需求: 將圖片https://i-blog.csdnimg.cn/direct/d145c8acdfac46f0bf9ab2c9316c22e0.png?保存到本地。
思考:
- 以什么方式打開文件。
- 保存什么格式的內容。
分析:
- 圖片的url地址:?https://i-blog.csdnimg.cn/direct/d145c8acdfac46f0bf9ab2c9316c22e0.png
- 利用 requests 模塊發送請求,獲取到圖片的響應。
- 以二進制的方式打開文件,并將 response 響應的二進制內容寫入到文件。
完整代碼如下:
# 1. 導入request模塊
import requests# 2. 準備目標url地址
url = 'https://i-blog.csdnimg.cn/direct/d145c8acdfac46f0bf9ab2c9316c22e0.png'# 3. 向目標url地址發送get請求
response = requests.get(url)# 4. 打開文件,將數據寫入到文件中
with open('itcast.png', 'wb') as f:# 寫入響應內容的二進制數據f.write(response.content)
代碼運行結果如下:
圖片已保存在同一目錄下。?
2.4?練習-保存多張網絡圖片
?需求: 將網頁中的圖片保存到本地。
地址:https://www.tupianzj.com/meinv/20210219/224797.html
思考:
- 以什么方式打開文件。
- 保存什么格式的內容。
分析:
- ?① 先請求 https://www.tupianzj.com/meinv/20210219/224797.html,獲取響應內容
- ?② 從上一步的響應內容中提取所有圖片的地址
- ?③ 遍歷每一個圖片地址,向每個圖片地址發送請求,并將響應的內容保存成圖片文件
完整代碼如下:
import re
import requests# 思路
# ① 先請求 https://www.tupianzj.com/meinv/20210219/224797.html,獲取響應內容
# ② 從上一步的響應內容中提取所有圖片的地址
# ③ 遍歷每一個圖片地址,向每個圖片地址發送請求,并將響應的內容保存成圖片文件# ① 先請求 https://www.tupianzj.com/meinv/20210219/224797.html,獲取響應內容
url = 'https://www.tupianzj.com/meinv/20210219/224797.html'
# 發送請求
response = requests.get(url)# 獲取響應的內容
html_str = response.content.decode('')
HTML1 = response.text
print(html_str)
print(HTML1)
# ② 從上一步的響應內容中提取所有圖片的地址
image_url_list = re.findall('<img src="(.*?)"', html_str)
# 去除最后無效地址
image_url_list = image_url_list[:-1]
print(image_url_list)# ③ 遍歷每一個圖片地址,向每個圖片地址發送請求,并將響應的內容保存成圖片文件
for image_url in image_url_list:# 發送請求image_response = requests.get(image_url)# 將響應內容保存成本地圖片filename = image_url[-10:]with open(filename, 'wb') as f:f.write(image_response.content)print('保存圖片完畢!!!')
3. requests 請求時設置請求頭
在最開始,我們書寫了一下代碼,獲取了一下百度首頁的內容:
# 1. 導入request模塊
import requests# 2. 準備目標url地址
url = 'https://www.baidu.com'# 3. 向目標url地址發送get請求
response = requests.get(url)# 4. 打印響應內容
print(response.content.decode())
運行以上代碼的結果如下:
接下來,我們對比一下,使用瀏覽器查看的百度的源碼和我們代碼中拿到的源碼有什么區別。
打開瀏覽器,訪問百度首頁,然后 鼠標右鍵 --> 查看網頁源代碼
可以看到,很明顯使用瀏覽器獲取到的百度首頁的內容要比使用代碼獲取到的內容要多得多。這是為什么呢?
回顧爬蟲的概念:模擬瀏覽器,欺騙服務器,獲取和瀏覽器一致的內容, 所以在這里我們需要帶上一些請求頭信息。
查看一下瀏覽器的請求頭信息:鼠標右鍵 --> 檢查 --> 打開Network --> 地址欄訪問百度,抓包,查看請求信息
再在代碼中,使用?response.requests.headers?查看使用 requests 模塊發送請求時攜帶的請求頭信息:
# 1.導入request模塊
import requests# 2.準備目標url地址
url = 'http://www.baidu.com'# 3.向目標url地址
# 發送請求
response = requests.get(url)# 查看請求頭
print(response.request.headers)
對比一下,很明顯的,代碼中的 User-Agent 和 瀏覽器中的完全不一樣,前面我們也說過,User-Agent 是瀏覽器的身份標識,而代碼中直接發送的是?python-requets/2.25.1
?這樣服務器很明顯的就知道我們不是使用正常的瀏覽器訪問服務器,所以返回的數據就比較少。
3.1 設置請求頭的語法
response = requests.get(url, headers={})
- headers 參數接收的字典形式的請求頭
- 請求頭字段名作為字典的 key,字段名對應的值作為字典的 value。
例如:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36
作為 headers 的參數,可以寫為:
{"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"}
3.2 設置請求頭完整代碼實現
# 1. 導入request模塊
import requests# 2. 準備目標url地址
url = 'https://www.baidu.com'
# 準備請求頭字典
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}# 3. 向目標url地址發送get請求
response = requests.get(url, headers=headers)# 4. 查看請求頭信息
print(response.request.headers)# 5. 打印響應內容
print(response.content.decode())
運行結果如下圖所示:
很明顯,拿到的內容和使用瀏覽器拿到的網頁源碼內容就是一樣的了。
4. 發送帶查詢參數的請求
我們在使用百度搜索的時候,經常會發現 URL 地址中會有一個???,該問號后面的就是查詢參數,又叫做查詢字符串參數。
注意:查詢參數的格式為key=value的格式,如果有多個參數,每個參數之間使用 & 符號連接,例如:https://tieba.baidu.com/f?ie=utf-8&kw=python&fr=search
。
4.1 requests請求攜帶查詢參數的語法
語法格式如下:
response = requests.get(url, params={})
- params參數接收的是一個字典。
- 查詢參數中,等號左邊的內容作為字典的 key,等號右邊的內容作為字典的 value。
- 注意點:在url地址中, 很多查詢參數是沒有用的,比如百度搜索的url地址,其中參數只有一個字段有用,其他的都可以刪除。如何確定那些請求參數有用或者沒用:挨個嘗試!?對應的,在后續的爬蟲中,遇到很多參數的url地址,都可以嘗試刪除參數。
4.2 發送帶查詢參數請求的應用
需求:實現在百度中搜索 csdn
實現方式1:對?百度安全驗證?發起請求可以使用 requests.get(url, params=kw) 的方式
實現代碼如下所示:
# 1. 導入request模塊
import requests# 2. 準備目標url地址
url = 'https://www.baidu.com/s?'
# 準備請求頭字典
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}
# 準備請求參數的字典
params = {"wd": "南京"
}# 3. 向目標url地址發送get請求
response = requests.get(url, headers=headers, params=params)# 4. 打印響應的內容
print(response.content.decode())
上述代碼運行結果如下圖所示:
實現方式2:直接對?百度安全驗證?完整的url地址進行請求,不使用 params 參數
實現代碼如下:
# 1. 導入request模塊
import requests# 2. 準備目標url地址
url = 'https://www.baidu.com/s?wd=南京'
# 準備請求頭字典
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}# 3. 向目標url地址發送get請求
response = requests.get(url, headers=headers)# 4. 打印響應的內容
print(response.content.decode())
以上代碼運行結果如下所示:
總結
- requests 發送 GET 請求的語法:?requests.get(url)
- response 對象的屬性和方法
- response.text:?獲取響應內容的字符串類型,可能會亂碼。
- response.content:?獲取響應的二進制類型的數據,獲取字符串類型的響應數據可以使用?response.content.decode()。一般使用這種方式來獲取響應的數據內容。
- response.url?: 響應的url地址,有時候響應的 url 地址和請求的 url 地址不一樣。
- response.status_code?: 獲取響應狀態碼。
- response.request.headers?: 獲取響應對應的請求頭信息。
- response.headers?: 獲取響應頭信息。
- response.request._cookies?: 響應對應請求攜帶的cookie,返回cookieJar類型。
- response.cookies?: 響應時設置的 cookie,返回cookieJar類型。
- response.json()?: 自動將 json 字符串類型的響應內容轉換為 python 對象(dict or list)。
- 發送請求設置請求頭的目的: 為了更好的偽裝成瀏覽器,拿到正確的響應數據。
- 發送請求設置請求頭的語法:?requests.get(url, headers={})。
- 查詢參數的格式:?以 ? 開始,key=value 的格式,每個參數之間使用 & 符號連接。
- 發送帶查詢參數的請求語法:?requests.get(url, params={})
requests 模塊的深入使用
知識點:
- 了解 GET 和 POST 請求的區別
- 掌握 使用 requests 發送 POST 請求
- 了解 代理IP的分類
- 掌握 requests 模塊使用代理IP的方法
1. requests 模塊發送POST請求
思考: 那些地方會使用到 POST 請求?
- 登錄注冊:POST 比 GET 更安全。
- 需要傳輸大文本內容的時候,POST請求對數據長度沒有要求。
所以同樣的,我們的爬蟲也需要在這兩個地方會去模擬瀏覽器發送post請求。
補充:GET 請求和 POST 請求的區別
GET | POST | |
---|---|---|
后退按鈕/刷新 | 無害 | 數據會被重新提交(瀏覽器應該告知用戶數據會被重新提交)。 |
書簽 | 可收藏為書簽 | 不可收藏為書簽 |
歷史 | 參數保留在瀏覽器歷史中。 | 參數不會保存在瀏覽器歷史中。 |
對數據長度的限制 | 當發送數據時,GET 方法向 URL 添加數據;URL 的長度是受限制的(URL 的最大長度是 2048 個字符)。 | 無限制。 |
對數據類型的限制 | 只允許 ASCII 字符。 | 沒有限制。也允許二進制數據。 |
安全性 | 與 POST 相比,GET 的安全性較差,因為所發送的數據是 URL 的一部分。在發送密碼或者其他敏感信息的時候決不能使用GET。 | POST 比 GET 更安全,因為參數不會被保存在瀏覽器歷史或 web 服務器日志中。 |
可見性 | 數據在 URL 中對所有人都是可見的。 | 數據不會顯示在 URL 中。 |
1.1 requests 發送 POST 請求語法
語法格式如下:
response = requests.post(url, data={}, headers={}, params={})
- data參數接收的數據類型是字典。
- POST請求體(form data)中,冒號左邊的作為字典的 key,冒號右邊的作為字典的 value。
如下圖所示:
將請求體(form data)中的數據轉換為字典:
data = {"username": "admin","password": "admin"
}
1.2 案例-傳智健康登錄
登錄頁面 URL 地址:http://manager-health-java.itheima.net/login.do
案例分析
抓包分析,找到登錄請求的url地址
-
右鍵檢查 --> Network
-
用戶名和密碼輸入框輸入用戶名和密碼: 用戶名 admin,密碼 admin,然后點擊登錄,抓取請求
-
根據抓包發現,登錄的 URL 地址為:(登錄成功之后會進行重定向到首頁,所以響應狀態碼是302)
http://manager-health-java.itheima.net/login.do
-
請求體數據為:
-
最終分析出:
-
POST 請求 URL 地址為:?
http://manager-health-java.itheima.net/login.do
-
請求方式為:?
POST
-
請求體參數為:
data = {"username": "admin","password": "admin" }
-
案例代碼實現
完整代碼如下:
import requests# 1. 準備登錄的url地址,請求頭,請求體數據
login_url = "http://manager-health-java.itheima.net/login.do"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}
data = {"username": "admin","password": "admin"
}# 2. 發起登錄請求
response = requests.post(login_url, data=data, headers=headers)# 打印登錄成功之后的響應狀態碼和響應的內容
print(response.status_code)
print(response.content.decode())
上述代碼運行結果如下:
響應狀態碼:
200
響應內容部分截圖
2. requests 使用代理
每次在使用瀏覽器請求一個網站的時候,服務器是可以獲取到當前客戶端的 IP 地址的,使用爬蟲程序去請求服務器的速度和頻率是特別快的,這樣的話,我們使用同一臺電腦上的瀏覽器去請求別人的服務器,會被服務器識別到,這樣可能就會將我們的 IP 地址封掉。為了不讓服務器將我們的 IP 地址封掉,在發送請求的時候可以使用代理 IP。
2.1 使用代理 IP 的目的
- 讓服務器以為是不同的客戶端在請求
- 防止我們的真實地址被泄露,防止被追究
2.2 代理的使用過程
-
代理IP 是一個IP,指向的是一個代理服務器
-
代理服務器能夠幫我們向目標服務器轉發請求
2.3 代理 IP 的分類
根據代理 IP 的匿名程度,代理 IP 可以分為以下三類:
分類名稱 | 特點 | 服務器接收的請求頭信息 |
---|---|---|
透明代理 | 透明代理雖然可以直接“隱藏”你的IP地址,但是還是可以查到你是誰。 | REMOTE_ADDR = Proxy IP HTTP_VIA = Proxy IP HTTP_X_FORWARDED_FOR = Your IP |
匿名代理 | 使用匿名代理,別人只能知道你用了代理,無法知道你是誰。 | REMOTE_ADDR = Proxy IP HTTP_VIA = Proxy IP HTTP_X_FORWARDED_FOR = Proxy IP |
高匿代理 | 無法發現你是在用代理,所以是最好的選擇毫無疑問使用高匿代理效果最好。 | REMOTE_ADDR = Proxy IP HTTP_VIA = not determined HTTP_X_FORWARDED_FOR = not determined |
根據網站所使用的協議不同,需要使用相應協議的代理服務。從代理服務請求使用的協議可以分為:
- http代理:目標url為http協議
- https代理:目標url為https協議
- socks隧道代理(例如socks5代理)等:
- socks 代理只是簡單地傳遞數據包,不關心是何種應用協議(FTP、HTTP和HTTPS等)。
- socks 代理比http、https代理耗時少。
- socks 代理可以轉發http和https的請求
2.4 requests 模塊中使用代理
為了讓服務器以為是不同的客戶端在請求;為了防止頻繁向一個域名發送請求被封 IP,所以我們需要使用代理 IP;那么我們接下來要學習requests 模塊是如何使用代理 IP 的。
語法格式如下:
response = requests.get(url, proxies={})
proxies 參數接收的數據類型為字典。
字典的格式如下:
proxies = {"協議類型": "協議類型://代理IP地址:端口號"
}例如:
proxies = {# 目標地址為 http 協議,會使用 http 這個 key 對應的代理服務"http": "http://113.121.255.26:9999",# 目標地址為 https 協議,會使用 https 這個 key 對應的代理服務"https": "https://219.151.157.130:3128"
}
免費代理 IP 網站
- 快代理:?免費私密代理IP_IP代理_HTTP代理 - 快代理
- 89免費代理:89免費代理IP_優質HTTP代理服務器_免費IP代理服務平臺
在學習階段,我們直接在網上找一些免費的代理去使用即可,免費的代理的質量不是很好,真正在公司會去購買付費的高質量的代理,或者自己去搭建代理服務器。
2.5 案例 - 使用代理 IP 請求唱吧
完整代碼如下:
# 1. 導入request模塊
import requests# 2. 準備目標url地址
url = 'http://changba.com/now/stars/index.html'
# 準備請求頭字典
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"
}
# 準備代理IP的字典
proxies = {'http': 'http://113.108.190.50:8080'
}# 3. 向目標url地址發送get請求
response = requests.get(url, headers=headers, proxies=proxies)# 4. 打印響應的內容
print(response.content.decode())
運行結果如下:
總結
- GET 和 POST 的區別
- GET 請求發送數據的長度是有限制的,POST請求沒有限制
- POST 請求要比 GET 請求安全
- GET 請求傳遞的數據會顯示到 URL 地址中,POST請求不會顯示在 URL 地址中。
-
requests 發送 POST 請求的語法:?requests.post(url, data={})
-
在爬蟲中使用代理IP是為了不讓服務器將我們的IP地址封掉。
- requests 使用代理的語法:?requests.get(url, proxies={})
- proxies 字典的格式: key 為 協議的類型,value 為?
協議://代理IP:端口號
- proxies 字典的格式: key 為 協議的類型,value 為?
requests 請求攜帶 Cookie
知識點:
- 了解 爬蟲中為什么要使用 Cookie
- 掌握 在請求頭中攜帶 Cookie
- 掌握 使用cookies參數攜帶 Cookie
- 掌握 使用requests.session 進行狀態保持
- 掌握 RequestsCookieJar 類型和字典之間的相互轉換
1. 爬蟲中使用Cookie
為了能夠通過爬蟲獲取到登錄后的頁面,或者是解決通過cookie的反扒,需要使用request來處理cookie相關的請求
1.1 爬蟲中攜帶Cookie的利弊
- 帶上 Cookie 的好處
- 能夠訪問登錄后的頁面
- 能夠實現部分反反爬
- 帶上 Cookie 的弊端
- 一套cookie往往對應的是一個用戶的信息,請求太頻繁有更大的可能性被對方識別為爬蟲
- 那么上面的問題如何解決 ?使用多個賬號
1.2 requests中使用 Cookie的方法
使用requests攜帶cookie有三種方法:
- cookie字符串放在headers中,設置?Cookie?請求頭
- 把cookie字典放傳給請求方法的cookies參數接收
- 使用requests提供的session模塊發送請求
2. Cookie 添加在 headers 中
網站經常利用請求頭中的 Cookie 字段來做用戶訪問狀態的保持,那么我們可以在 headers 參數中設置 Cookie 請求頭,模擬普通用戶的請求。我們以傳智健康登陸為例:
2.1 抓包找到登錄之后的Cookie
-
打開瀏覽器,右鍵檢查,點擊 Network,勾選 Preserve log
-
訪問傳智健康登陸的url地址:?傳智健康
-
輸入賬號和密碼之后,點擊登錄。
-
訪問一個登錄之后才可以訪問的頁面,例如:?傳智健康
-
找到Network中對應的抓到的包,查看在訪問時攜帶的 User-Agent 和 Cookie 的信息
2.2 完成代碼
- 從瀏覽器中復制User-Agent和Cookie
- 瀏覽器中的請求頭字段和值與headers參數中必須一致
- headers請求參數字典中的Cookie鍵對應的值是字符串
完整代碼如下:
import requests# 1. 準備目標url地址
url = 'http://manager-health-java.itheima.net/pages/main.html'# 2. 準備請求頭信息,cookie 和 User-Agent,(從瀏覽器抓包復制)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36","Cookie": "JSESSIONID=4BBDB822771C712E3570B8AD3BED780F"
}# 3. 發送請求,獲取響應數據
response = requests.get(url, headers=headers)# 4. 查看響應的內容
print(response.content.decode())
運行代碼驗證結果:
登錄之后訪問該頁面,會顯示出來對應的左側的菜單信息,我們查看一下在代碼中獲取到的響應內容中,是否包含著對應的菜單信息即可。
3. 使用 Cookies 參數
上一小節中,我們在請求頭 headers 中攜帶著登錄之后的 Cookie 信息,我們也可以使用專門的 cookies 參數來攜帶 Cookie。
3.1 Cookies 參數語法以及格式
-
cookies參數的格式:字典
cookies = {"Cookie的name": "Cookie的Value"}
- 該字典對應請求頭中Cookie字符串,以分號、空格分割每一對字典鍵值對
- 等號左邊的是一個cookie的name,對應cookies字典的key
- 等號右邊對應cookies字典的value
-
cookies參數的使用方式:
response = requests.get(url, cookies={})
-
注意:?cookie一般是有過期時間的,一旦過期需要重新獲取
3.2 使用cookies參數完成登錄傳智健康
- 登錄傳智健康,獲取到登錄之后的cookie字符串
- 將瀏覽器抓包獲取到的cookie字符串,轉換成字典,以便用于發送請求時傳遞給cookies參數。
完整代碼如下:
import requests# 1. 準備目標url地址
url = 'http://manager-health-java.itheima.net/pages/main.html'# 2. 準備請求頭信息,User-Agent,(從瀏覽器抓包復制)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}
# 準備cookies字典
cookie_dict = {'JSESSIONID': '14C7DFBF244EB81DC281FC1B96EF88D6'
}# 3. 發送請求,獲取響應數據
response = requests.get(url, headers=headers, cookies=cookie_dict)# 4. 查看響應的內容
print(response.content.decode())
對比網頁內容和使用代碼獲取到的網頁響應內容:
我們發現,代碼中獲取到的響應內容和頁面中的內容是一致的,表示我們使用 cookies參數攜帶cookie登錄傳智健康是成功的。
cookie有過期時間 ,所以直接復制瀏覽器中的cookie可能意味著下一程序繼續運行的時候需要替換代碼中的cookie,對應的我們也可以通過一個程序專門來獲取cookie供其他程序使用;當然也有很多網站的cookie過期時間很長,這種情況下,直接復制cookie來使用更加簡單。
補充:將瀏覽器復制的含有多個Cookie的字符串轉換為字典
cookies_dict = {cookie.split('=')[0]:cookie.split('=')[1] for cookie in cookies_str.split('; ')
}
4. 使用 requests.session 攜帶Cookie
前面使用手動的方式使用cookie,那么有沒有更好的方法在requets中攜帶cookie呢?
requests 提供了一個叫做session類,來實現客戶端和服務端的會話保持
會話保持有兩個內涵:
- 自動保存cookie,下一次請求會帶上前一次的cookie
- 實現和服務端的長連接,加快請求速度
4.1 使用方式
session = requests.session() # 實例化session類對象
response = session.get(url, headers, ...)
response = session.post(url, data, ...)
- session對象發送get或post請求的參數,與requests模塊發送請求的參數完全一致
4.2 使用 requests.session 登錄傳智健康
-
登錄頁面 url 地址:?傳智健康
-
先抓包獲取到登錄傳智健康的登錄 url 地址,以及登錄需要的參數
- 先使用 request.session 完成登錄,獲取登錄之后的 cookie
- 在使用 request.session 對象請求登錄之后的頁面。
完整代碼如下:
import requests# 1. 準備登錄的url地址(抓包獲取到的登錄的post url地址)
login_url = "http://manager-health-java.itheima.net/login.do"# 準備請求頭User-Agent
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}
# 準備請求體字典
data = {"username": "admin","password": "admin"
}# 2. 創建session對象
session = requests.session()# 3. 使用session對象發送post請求 登錄傳智健康
session.post(login_url, headers=headers, data=data)# 使用 session對象再次請求登錄之后的url地址
url = "http://manager-health-java.itheima.net/pages/main.html"
# 發送請求 獲取響應
response = session.get(url, headers=headers)# 4. 打印響應內容
print(response.content.decode())
查看運行之后的結果:
5. requests 中 cookiejar 處理
在使用requests模塊發送完請求之后,獲取到的response對象中具有 cookies屬性,該屬性是一個 RequestsCookieJar 類型。包含了對方服務器設置在本地的cookie。我們如何將其轉換為cookies字典呢?
-
轉換方式
# 將cookiejar轉換成字典類型 cookies_dict = requests.utils.dict_from_cookiejar(response.cookies) # 將字典類型轉換成cookiejar類型 cookies_jar = requests.utils.cookiejar_from_dict(cookies_dict)
-
其中response.cookies返回的就是cookieJar類型的對象
-
requests.utils.dict_from_cookiejar
函數返回cookies字典
5.1 案例 RequestsCookieJar 的轉換
完整代碼如下:
import requests# 1. 準備url地址
url = "https://www.baidu.com"# 發送請求 獲取響應對象
response = requests.get(url)# <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
print(f"獲取到的cookiejar對象為:{response.cookies}")# 將cookiejar類型 轉換為字典
cookie_dict = requests.utils.dict_from_cookiejar(response.cookies)
print(f"將cookiejar轉換為字典:{cookie_dict}")
# 將cookie字典再轉換為cookiejar
cookie_jar = requests.utils.cookiejar_from_dict(cookie_dict)
print(f"將cookie_dict 轉換成cookiejar:{cookie_jar}")
代碼運行結果如下所示:
總結
-
爬蟲中使用 Cookie 的目的:
- 訪問登錄之后的頁面
- 實現反反爬
-
在請求頭中設置 Cookie
- 先在瀏覽器中登錄到目標網站。
- 使用瀏覽器抓包工具獲取到登錄之后的 Cookie。
- 將獲取到的 Cookie 設置到請求頭headers的字典中。
- 在發送請求的時候,攜帶請求頭headers字典:?
response = requests.get(url, headers=headers)
-
使用cookies參數攜帶 Cookie:
- 先在瀏覽器中登錄到目標網站。
- 使用瀏覽器抓包工具獲取到登錄之后的 Cookie。
- 將獲取到的 Cookie 字符串,通過字典推導式轉換為 Cookie 字典。
- 在發送請求的時候,將構造好的 Cookie 字典,傳遞給cookies參數:?
response = requests.get(url, cookies = {})
-
使用requests.session可以在每一次請求的時候,自動攜帶上一次獲取到的Cookie。
- 創建 requests.session 對象
- session對象的使用方式(session對象發送get或post請求的參數,與requests模塊發送請求的參數完全一致):
session = requests.session() # 實例化session對象 response = session.get(url, headers, ...) response = session.post(url, data, ...)
- 使用 session 對象發起POST請求,登錄到目標網站
- 后續的請求使用 session對象來發起,就可以實現狀態保持。
-
使用requests模塊發送請求獲取到的response對象中的cookies屬性獲取到的是一個 RequestsCookieJar 類型,該類型可以和字典進行相互轉換,轉換方式如下:
# 將cookiejar轉換成字典類型 cookies_dict = requests.utils.dict_from_cookiejar(response.cookies) # 將字典類型轉換成cookiejar類型 cookies_jar = requests.utils.cookiejar_from_dict(cookies_dict)
requests 模塊的其他用法
知識點:
- 掌握 requests解決https證書錯誤的問題
- 掌握 requests中超時參數的使用
- 掌握 retrying模塊的使用
1. requests 模塊處理證書錯誤
經常我們在網上沖浪時,經常能夠看到下面的提示:
- 原因:該網站的CA證書沒有經過【受信任的根證書頒發機構】的認證。
- 關于CA證書以及受信任的根證書頒發機構點擊了解更多,課上我們不做展開講解。
上述情況,我們在使用瀏覽器請求網址的時候,就會出現,那么我們在代碼中去請求CA證書有問題的網站的時候會怎么樣呢?
注意: 12306 完整大概在 18年10月份左右進行升級,現在已經不會出現證書錯誤的問題了,我們使用以下網址來演示該問題:
https://sam.huat.edu.cn:8443/selfservice/
1.1 代碼中發起請求演示
實例代碼如下:
import requests
# 準備目標url地址
url = "https://sam.huat.edu.cn:8443/selfservice/"
# 發送請求 獲取響應對象
response = requests.get(url)
以上代碼運行結果如下:
很明顯。我們發現,在代碼中發起請求,如果目標的url地址,存在 CA 證書問題是,會拋出?SSLError
?這么一個錯誤。
1.2 解決方案
為了在代碼中能夠正常的請求,我們使用
verify=False
參數,此時requests模塊發送請求將不做CA證書的驗證:verify參數能夠忽略CA證書的認證
代碼如下:
import requests# 注意:取消安全證書有InsecureRequestWarning 不安全的警告
# 取消警告
requests.packages.urllib3.disable_warnings()# 準備目標url地址
url = "https://sam.huat.edu.cn:8443/selfservice/"# 發送請求 獲取響應對象
response = requests.get(url, verify=False)print(response.content.decode('gbk'))
上述代碼運行結果如下:
注意:如果后續在請求的時候,程序拋出了 SSLError 的異常,那么就可以使用 verify=False 來解決 CA證書錯誤的問題。
2. 超時參數timeout的使用
在平時網上沖浪的過程中,我們經常會遇到網絡波動,這個時候,一個請求等了很久可能任然沒有結果。
在爬蟲中,一個請求很久沒有結果,就會讓整個項目的效率變得非常低,這個時候我們就需要對請求進行強制要求,讓他必須在特定的時間內返回結果,否則就報錯。
超時參數 timeout的使用方式
response = requests.get(url, timeout=3)
timeout=3表示:發送請求后,3秒鐘內返回響應,否則就拋出異常
示例代碼如下:
import requestsurl = 'https://twitter.com'
response = requests.get(url, timeout=3) # 設置超時時
上述代碼運行結果如下:
超出了設置設置的超時時間,程序中就會拋出: ConnectTimeout 異常。
3. retrying模塊的使用
使用超時參數能夠加快我們整體的請求速度,但是在正常的網頁瀏覽過成功,如果發生速度很慢的情況,我們會做的選擇是刷新頁面,那么在代碼中,我們是否也可以刷新請求呢?
對應的,retrying模塊就可以幫助我們解決
3.1 retrying模塊的使用
retrying模塊的地址:retrying · PyPI
安裝:
pip install retrying或者pip install retrying -i https://pypi.tuna.tsinghua.edu.cn/simple
安裝如下圖所示:
retrying 模塊的使用
- 用retrying模塊提供的retry模塊
- 通過裝飾器的方式使用,讓被裝飾的函數反復執行
- retry中可以傳入參數
stop_max_attempt_number
,讓函數報錯后繼續重新執行,達到最大執行次數的上限,如果每次都報錯,整個函數報錯,如果中間有一次成功,程序繼續往后執行
3.2 retrying和requests的簡單封裝
實現一個發送請求的函數,每次爬蟲中直接調用該函數即可實現發送請求,在其中
- 使用timeout實現超時報錯
- 使用retrying模塊實現重試
參考代碼如下:
import requests
from retrying import retryheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}# 最大重試3次,3次全部報錯,才會報錯
@retry(stop_max_attempt_number=3)
def _parse_url(url):print('-' * 30)# 超時的時候會報錯并重試response = requests.get(url, headers=headers, timeout=3)# 狀態碼不是200,也會報錯并重試assert response.status_code == 200return responsedef parse_url(url):try:# 進行異常捕獲response = _parse_url(url)except Exception as e:print(e)# 報錯返回Noneresponse = Nonereturn responseparse_url('https://twitter.com')
運行結果如下所示:
總結
-
如果在使用requests模塊請求某一個url地址的時候,拋出了?
SSLError
?的錯誤,可以使用 verify=False,來忽略CA證書的驗證。response = requests.get(url, verify=False)
-
可以在請求某一個url地址的時候,設置超時參數,超過超時參數設置的時間還沒有請求成功,就會拋出異常。
response = requests.get(url, timeout=秒數)
-
如果對于一個url地址,設置了超時參數,沒有請求成功,我們需要重復的再次的去請求,可以借助 retrying 模塊重復的去請求。
import requests from retrying import retry# 最大重試3次,3次全部報錯,才會報錯 @retry(stop_max_attempt_number=3) def _parse_url(url):print('-' * 30)# 將重復發生的請求的操作 放到封裝的函數中,使用裝飾器中的 stop_max_attempt_number 設置最大的重試次數 # 超時的時候回報錯并重試response = requests.get(url, headers=headers, timeout=3)# 狀態碼不是200,也會報錯并重試assert response.status_code == 200return response
案例-百度貼吧爬蟲
1. 需求
- 給定一個貼吧名字,給定要抓取的頁數。
- 將貼吧的每一頁數據保存到html中。
2. 需求分析
-
打開百度貼吧首頁:百度貼吧——全球領先的中文社區
-
搜索訪問進入某一個貼吧中,這里以傳智播客貼吧為例:?https://tieba.baidu.com/f?kw=傳智播客
-
上述過程中,我們可以分析出來,在url地址中的 kw 參數,對應的就是我們要訪問的貼吧的名字。我們可以根據給定的貼吧名字進行替換要抓取的貼吧的名字。
-
進入到傳智播客貼吧中,我們目前看到的就是第一頁的數據,需求中,我們是需要獲取到多頁的數據的,那么我們就需要去分析一下,每一頁的url地址的規律,從而構造出多頁的url地址。
-
頁面url地址規律分析
第一頁的url地址: https://tieba.baidu.com/f?kw=傳智播客 第二頁的url地址: https://tieba.baidu.com/f?kw=傳智播客&pn=50 第三頁的url地址: https://tieba.baidu.com/f?kw=傳智播客&pn=100 第四頁的url地址: https://tieba.baidu.com/f?kw=傳智播客&pn=150 第五頁的url地址: https://tieba.baidu.com/f?kw=傳智播客&pn=200
-
通過分析發現,每一頁的url地址中,都是在參數 pn 的基礎上 加 50。第一頁的url地址中,是沒有pn參數的,那么我們可以嘗試一下,第一頁的url地址中加上 pn=0,傳智播客吧-百度貼吧--傳智播客-國內口碑最好的IT培訓機構!--中國的軟件教育已經坑害了不少軟件工程師苗子,傳智播客自成立之日起就立志于改變中國的軟件教育,目前已經出版IT教程書籍十多本,教學視頻幾十套?, 看看能不能訪問成功
-
通過上一步的驗證,我們發現第一頁的 pn 值為 0
-
那么綜上所述,我們可以得出百度貼吧的url地址的規律為:
tb_name:要訪問的貼吧的名字 page_num: 當前要訪問的頁碼數 https://tieba.baidu.com/f?kw=tb_name&pn=50 * (page_num - 1)
-
比如我們需要抓取10頁的數據,可以先將所有頁的url地址構造好,放到一個列表中。
import requests# url地址的模板 base_url = "https://tieba.baidu.com/f?kw={}&pn={}"# 獲取要抓取的貼吧的名字 tb_name = input('請輸入要抓取的貼吧的名字:') # 獲取要抓取的頁數 page_num = int(input('請輸入要抓取的頁數:'))# 構造url地址 url_list = [base_url.format(tb_name, i * 50) for i in range(page_num)]print(url_list)
-
構造好的url地址如下圖所示:
-
遍歷構造好的url地址,分別對url地址去發送請求,獲取到響應數據,再分別將每一頁的數據保存到文件中。
# 準備headers中的User-Agent headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36" } # 遍歷每一頁的url地址,發送請求 獲取響應 for url in url_list:response = requests.get(url, headers=headers)# 將獲取到的響應的內容,保存到文件中# 構造文件名file_name = tb_name + "第{}頁.html".format(url_list.index(url) + 1)# 打開文件,將數據保存到文件中with open(file_name, 'w', encoding='utf-8') as f:f.write(response.content.decode())print(file_name, '保存成功')# 設置time.sleep() 防止訪問速度過快time.sleep(2)
3. 完整代碼
完整代碼如下:
import timeimport requests# url地址的模板
base_url = "https://tieba.baidu.com/f?kw={}&pn={}"# 獲取要抓取的貼吧的名字
tb_name = input('請輸入要抓取的貼吧的名字:')
# 獲取要抓取的頁數
page_num = int(input('請輸入要抓取的頁數:'))# 構造url地址
url_list = [base_url.format(tb_name, i * 50) for i in range(page_num)]# 準備headers中的User-Agent
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
}
# 遍歷每一頁的url地址,發送請求 獲取響應
for i, url in enumerate(url_list):response = requests.get(url, headers=headers)# 將獲取到的響應的內容,保存到文件中# 構造文件名file_name = tb_name + "第{}頁.html".format(i + 1)# 打開文件,將數據保存到文件中with open(file_name, 'w', encoding='utf-8') as f:f.write(response.content.decode())print(file_name, '保存成功')# 設置time.sleep() 防止訪問速度過快time.sleep(2)
程序運行結果如下: