文章目錄
requests
作用:發送網絡請求,返回相應數據 中文文檔API
requests使用
亂碼解決方式
文本亂碼
- 方法一
使用.text
方法讀取內容,使用.encoding
修改編碼方式
r = requests.get('http://www.baidu.com/')
r.encoding = 'utf-8'
print(r.text)
- 方法二
使用.content
方法讀取內容,使用.decode
修改編碼方式
r = requests.get('http://www.baidu.com/')
print(r.content.decode('utf8'))
如果是圖片、視頻、音頻等內容直接使用.content
方法讀取即可,如果使用decode
方法修改編碼會報錯
僞裝瀏覽器
我們先來模仿瀏覽器獲取百度內容
- headers: 請求頭,User-Agent用於模擬瀏覽器(小知識:每個瀏覽器的信息中都有Mozilla,Mozilla是網景瀏覽器的內核,因爲一些歷史原因網景已經消失在歷史的長河中,但網景對瀏覽器發展的巨大影響尚未消失)
- get: 使用get請求獲取網站內容
- encoding:定義網站使用的編碼(國內常用utf-8,也可能使用gbk)
import requests
class ReptileTest:
def __init__(self):
self.request_header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0'}
def main(self):
# 使用get方法連接百度
r = requests.get('http://www.baidu.com', headers=self.request_header)
# 定義編碼
r.encoding = 'utf-8'
# 輸出內容
print(r.text)
if __name__ == '__main__':
reptile = ReptileTest()
reptile.main()
發送帶參請求
URL知識,URL中可以傳遞給服務器產生,參數一般是靈活多變的,有時候我們並不會直接將參數寫死,這時候我們可以將參數以字典的形式動態傳入。
import requests
# http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=50
class ReptileTest:
def __init__(self):
self.request_header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.3992.4 Safari/537.36'}
def main(self):
"""主函數"""
data = {
'kw': 'python',
'pn': 50
}
r = requests.get('http://tieba.baidu.com/f', headers=self.request_header, params=data)
r.encoding = 'utf-8'
print(r.text)
if __name__ == '__main__':
reptile = ReptileTest()
reptile.main()
實戰測試抓取
百度貼吧
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# @Author : 尋覓
# @File : requests庫入門.py
# @Time : 2020/1/9 1:12
# @Software: PyCharm
import requests
# 抓取百度貼吧的Python論壇頁面
# http://tieba.baidu.com/f?kw=python&pn=50
# http://tieba.baidu.com/f?kw=python&pn=100
class ReptileTest:
def __init__(self, name, page):
self.request_url = 'http://tieba.baidu.com/f'
self.request_header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0'}
self.name = {'kw': name, 'pn': 0}
self.page = page
def parse_url(self):
"""構造連接列表"""
if (self.page-1)*50 > self.name['pn']:
self.name['pn'] += 50
else:
self.page = False
def save_html(self):
"""保存頁面"""
r = requests.get('http://tieba.baidu.com/f', headers=self.request_header, params=self.name)
r.encoding = 'utf-8'
with open(f'{self.name["kw"]}貼吧第{self.name["pn"] // 50 + 1}頁.html', 'wb') as w:
w.write(r.content)
def run(self):
"""運行"""
while self.page:
self.save_html()
self.parse_url()
if __name__ == '__main__':
tieba_name = input('請輸入爬取貼吧的名稱')
tieba_page = int(input('需要爬取的頁數'))
reptile = ReptileTest(tieba_name, tieba_page)
reptile.run()
胡蘿蔔周
class ReptileTest:
def __init__(self, page):
self.request_url = 'http://www.carrotchou.blog/page/{}'
self.request_header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0'}
self.page = page
self.cur = 1
def parse_url(self):
"""構造連接列表"""
if self.page > self.cur:
self.cur += 1
else:
self.page = False
def save_html(self):
"""保存頁面"""
r = requests.get(self.request_url.format(self.cur), headers=self.request_header)
r.encoding = 'utf-8'
with open(f'胡蘿蔔周第{self.cur}頁.html', 'wb') as w:
w.write(r.content)
def run(self):
"""運行"""
while self.page:
self.save_html()
self.parse_url()
if __name__ == '__main__':
carrot_page = int(input('需要爬取的頁數'))
reptile = ReptileTest(carrot_page)
reptile.run()
帶參post請求
POST因爲其隱式傳輸(傳輸內容不會再URL中顯示),傳輸內容大小無限制等特性,常用於登陸註冊或者大文件傳輸時使用
有道翻譯
大多數網站的PC端反扒已經較爲完善,這時候我們可以嘗試對網站的手機端進行數據爬取,有道的PC端存在反扒措施,但是手機端可以直接爬取,下面我們就來嘗試使用有道翻譯進行post請求測試,有道翻譯手機版在2020年1月還未有反扒措施。
這裏首先我們使用.post方法請求,而不是.get方法請求,之後我們需要在請求中添加請求數據(data),這裏的數據也就是我們需要翻譯的內容。我們可以在瀏覽器控制檯中查看
將此時數據填入data中即可。進行帶參數的post訪問
import requests
# 使用post請求
class ReptileTest:
def __init__(self):
self.request_url = 'http://m.youdao.com/translate'
self.request_header = {'User-Agent': 'Mozilla/5.0 (Linux; Android 7.0; SM-G892A Build/NRD90M; wv) '
'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/67.0.3396.87 '
'Mobile Safari/537.36'}
self.data = {"inputtext": "測試", "type": "AUTO"}
def main(self):
"""主函數"""
r = requests.post(self.request_url, data=self.data, headers=self.request_header)
with open('翻譯.html', 'wb') as w:
w.write(r.content)
if __name__ == '__main__':
reptile = ReptileTest()
reptile.main()
使用代理IP
代理的作用在這裏就不做贅述了,以防被和諧
用法:.get方法
或.post方法
中添加proxies
屬性
proxies(代理):字典映射協議到代理的URL。
class ReptileTest:
def __init__(self):
self.request_header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.3992.4 Safari/537.36'}
def main(self):
"""主函數"""
# 我這裏使用的免費ip可能只有幾分鐘的存活時間
proxies = {
'http': 'http://220.135.165.38:8080'
}
r = requests.get('http://www.baidu.com/', headers=self.request_header, proxies=proxies)
r.encoding = 'utf-8'
print(r.text)
if __name__ == '__main__':
reptile = ReptileTest()
reptile.main()
cookie 與 session
cookie與session都是用於保存數據的,他們最大的區別是cookie是在本地,也就是用戶的客戶端中保存數據,而session則是在服務器中保存數據他們相比起來各有優劣。
- cookie將數據保存在本地,可以將用戶信息保存在本地,可以簡化用戶再次操作的流程,而且可以記錄一些用戶習慣,可以更好的服務於用戶,但是這樣的做法導致數據安全性較低,cookie中保存的密碼私鑰等重要信息容易遭到泄露,同時本地的cookie可以被有心之人利用,進行cookie欺詐等行爲,且保存數據只能小於4k,大多數瀏覽器也規定一個站點最多隻能保存20個cookie。
- session將數據保存在服務器,雖然解決了安全性問題,但是如果有大量用戶,session會佔用服務器大量資源,使服務器性能下降。
爬蟲利用cookie與session
帶上cookie,我們可以模擬登陸後的用戶,但是帶上cookie後我們如果過於頻繁的去訪問服務器,也容易暴露出我們是爬蟲,在Python中,下面的我們都將使用人人網做測試。
模擬登陸方法一
思路:我們可以利用resquests庫中的.session()
方法來保持用戶的登陸狀態。
- 特點:
- 適用於cookie保存時效短,易失效的網站,可以及時獲取最新的cookie,
- 每次都要程序運行都要重新獲取新的cookie操作較爲麻煩。
- 思路
-
首先我們打開人人網,找到表單提交的地址,在控制檯中,我們很容易就找到人人網登陸表單的提交地址爲:http://www.renren.com/PLogin.do
-
調用requests庫中的
.session()
方法 -
用使用過
.session()
方法的對象對登陸表單提交地址進行post請求提交登陸數據,進行登陸操作,在使用post請求登陸後,會自動將cookie保存 -
使用帶cookie的對象用get請求,這時候就可以正常訪問個人用戶界面了
-
class ReptileCookie:
def __init__(self):
# 調用session方法
self.session = requests.session()
# 人人網登陸地址
self.request_url = 'http://www.renren.com/PLogin.do'
# 人人網個人信息頁面(每個人的個人信息頁面都不同,這裏需要改成你自己的頁面才能看到效果)
self.request_login = 'http://www.renren.com/973399342/profile'
self.request_header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4029.0 Safari/537.36'}
self.login_data = {
'email': '登陸郵箱',
'password': '登陸密碼'
}
def main(self):
# 使用session方法後,用戶將處於持續登陸狀態
self.session.post(self.request_url, data=self.login_data, headers=self.request_header)
# 這時候的session對象中已經保存了登陸的cookie信息,可以訪問登陸後才能訪問的頁面
data = self.session.get(self.request_login, headers=self.request_header)
return data
if __name__ == '__main__':
reptile = ReptileCookie()
items = reptile.main()
items.encoding = 'utf-8'
with open('人人.html', 'wb') as w:
w.write(items.content)
模擬登陸方法二
使用cookie直接登陸
- 特點:
- 適用於cookie保存時效長或有專門獲取cookie工具的情況下使用。
- 需要在cookie過期前拿到所以數據。
- 思路
- 這時候我們需要進入已經登陸後的人人網,然後在開發者工具中找到cookie,並放在請求頭中。
- 使用get請求直接請求對應的頁面
- 這時候我們需要進入已經登陸後的人人網,然後在開發者工具中找到cookie,並放在請求頭中。
class ReptileCookie:
def __init__(self):
self.requests_header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4029.0 Safari/537.36',
'Cookie': '填寫你的cookie'
}
self.requests_url = 'http://www.renren.com/973399342/profile'
def main(self):
return requests.get(self.requests_url, headers=self.requests_header)
if __name__ == '__main__':
data = ReptileCookie()
html_data = data.main()
html_data.encoding = 'utf-8'
with open('人人網.html', 'wb') as w:
w.write(html_data.content)
模擬登陸方法三
第三方法和第二種類似,只是我們將請求頭中的cookie取出,利用get或post中的cookie屬性將cookie單獨傳入,但需要注意的是,這裏的cookie屬性只支持字典.
data = {i.split('=')[0]: i.split('=')[1] for i in cookie.split(';')}
使用此字典推導式可以將方法二中的cookie轉換爲字典
class ReptileCookie:
def __init__(self):
self.requests_header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4029.0 Safari/537.36'}
self.requests_cookie = {dict格式的cookie}
self.requests_url = 'http://www.renren.com/973399342/profile'
def main(self):
return requests.get(self.requests_url, cookies=self.requests_cookie, headers=self.requests_header)
if __name__ == '__main__':
items = ReptileCookie()
data = items.main()
data.encoding = 'utf-8'
with open('人人網.html', 'wb') as w:
w.write(data.content)
requests小技巧
將cookie對象轉化爲字典
上述模擬登陸時,我們多次使用了cookie,我們可以使用,requests庫中的.utils.dict_from_cookiejar()
方法將cookie對象轉化爲字典,使用.utils.cookiejar_from_dict()
方法在將字典轉化爲cookie對象。
import requests
class RequestsTest:
def __init__(self):
self.baidu_url = 'http://www.baidu.com'
self.header = 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0'
def geturl(self):
demo = requests.get(self.baidu_url, self.header)
# 將cookie對象轉化成字典的方法
dic = requests.utils.dict_from_cookiejar(demo.cookies)
# 將字典轉化爲cookie對象
items = requests.utils.cookiejar_from_dict(dic)
print(demo.cookies, dic, items)
def main(self):
"""主函數"""
self.geturl()
if __name__ == '__main__':
RequestsTest().main()
判斷是否請求超時以及設置超時
當以及完成get或者post請求後,我們可以使用.status_code
屬性查看連接狀態,如果是200即爲成功。
get和post請求中有一個timeout
屬性可以設置超時(單位:秒),一旦超過指定時間就會報錯。
import requests
class RequestsTest:
def __init__(self):
self.baidu_url = 'http://www.baidu.com'
self.google_url = 'http://www.google.com'
self.header = 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0'
def geturl(self, url):
"""判斷是否成功連接"""
try:
# 使用timeout方法連接
demo = requests.get(url, self.header, timeout=1)
# 如果超時就報超時錯誤
except requests.exceptions.ConnectTimeout:
data = '連接超時'
# 其他錯誤則返回錯誤
except Exception as e:
data = e
else:
data = demo.status_code
finally:
# 判斷是否連接失敗
if data == 200:
demo.encoding = 'utf-8'
return True, demo
else:
data = str(data)
return False, f'連接出現錯誤,錯誤信息爲:{data}'
def main(self):
"""主函數"""
item = self.geturl(self.google_url)
if item[0]:
print(item[1].text)
else:
print(item[1])
if __name__ == '__main__':
RequestsTest().main()
url中的中文的編碼與解碼
在url中的中文需要對其編碼和解碼才能正常看到(當下大多數瀏覽器都會自動進行編碼以及解碼)在Python中也提供了手動編碼以及解碼的方法,使用requests.utils.quote()
方法編碼,使用requests.utils.unquote()
方法解碼。
# 編碼
print(requests.utils.quote('尋覓'))
# 解碼
print(requests.utils.unquote('%E5%AF%BB%E8%A7%85'))