先來複習複習字符串的編碼方式
字符串複習
字符串符合兩種類型
- byte:二進制
互聯網上數據的都是以二進制的方式傳輸的 - str:unicode的呈現方式
兩者是可以相互轉換的,但必須編碼解碼方式一樣,否則就會出錯
str使用encode方法轉化爲bytes
bytes通過decode轉化成str
a = '你好,爬蟲'
print(type(a))
b = a.encode()
print(type(b))
print(b)
還可以加上編碼解碼方式
b = a.encode("UTF-8")
後續如果爬蟲網頁源代碼出現亂碼,可能就是編碼方式不一致
那麼正題要開始了
requests使用入門
如何請求頁面,並獲取頁面的內容
import requests
# 請求網頁
r = requests.get("http://www.baidu.com")
# r是一個response對象,我們想到得到的信息都可以從它獲取
print(r)
# 得到r的內容
print(r.text)
如果打印r.text
出現亂碼,則可以先指定編碼方式,再打印看看
# 指定編碼方式
r.encoding = 'utf-8'
print(r.text)
還有一種方法可以獲取網頁的內容
那就是r.content
但它是二進制形式打印出來
需要解碼,可以指定編碼方式(默認是utf-8),例如gbk,gb2312
r.content.decode()
更推薦這種方式獲取網頁內容
動動手-保存圖片到本地
不僅圖片,mp4,gif,pdf等常見的格式文件都可以這樣做
import requests
# 圖片地址
page_url = 'https://i0.hdslb.com/bfs/archive/cb110cffbf66ae37169e1b8bca68d138a9de97e5.png@880w_440h.png'
# 發送get請求
r = requests.get(page_url)
# 保存,寫入二進制文件
with open('a.png','wb') as f:
f.write(r.content)
獲取更多的數據
- response.status_code 狀態碼,當爲200的時候請求某一個url成功,並不一定是訪問我們想要的url成功,因爲登入的時候網頁有重定向
- response.request.url 請求的url
發送帶headers的請求
爲什麼要發送帶headers的請求?
發送帶headers的請求的目的就爲了模仿瀏覽器訪問網頁,欺騙服務器,獲取和瀏覽器一致的內容
那headers從何而來呢?
在瀏覽器上面,按F12,點擊network
最常用的是User-Agent
import requests
# 請求頭
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0"
}
# 發送get請求,帶請求頭
r = requests.get("http://www.baidu.com",headers=headers)
# 打印內容
print(r.content.decode())
發送帶參數的請求
什麼是參數?
比如這個url
https://www.baidu.com/s?wd=python&c=b
?
後面以 &
分開的都是參數
那麼這個?
可不可以去掉呢?
我們來嘗試嘗試
先不去試試看
import requests
# 請求頭
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0"
}
# 參數
p = {"wd":"python"}
# 發送get請求,帶請求頭, 帶參數
r = requests.get("http://www.baidu.com/s?",headers=headers,params=p)
# 打印狀態碼
print(r.status_code)
# 打印請求的url
print(r.request.url)
可以請求成功
把?
去掉看看
import requests
# 請求頭
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0"
}
# 參數
p = {"wd":"python"}
# 發送get請求,帶請求頭, 帶參數
r = requests.get("http://www.baidu.com/s",headers=headers,params=p)
# 打印狀態碼
print(r.status_code)
# 打印請求的url
print(r.request.url)
發現是一樣的
如果不確定的話,可以用字符串拼接
# 發送get請求,帶請求頭, 帶參數
url = "http://www.baidu.com/s?{}".format("python")
r = requests.get(url,headers=headers,params=p)
動動手-任意貼吧的爬蟲
想要實現任意貼吧的爬取,就要分析url的規律
例如 https://tieba.baidu.com/f?kw=李毅
kw
後面的值爲什麼,就是什麼吧
然後我們想爬取這個貼吧的1000頁頁面的內容
1000頁!這也得要找出規律
當我們點擊下一頁的時候,url發生了這樣的變化
https://tieba.baidu.com/f?kw=李毅&pn=50
,後面多了個pn=50
繼續點擊下一頁,發現每點擊下一頁,pn就多加50
那pn=0
,當令它爲0,發現就爲第一頁,於是就是這麼個規律
我們定義一個貼吧類
import requests
class TiebaSpider:
def __init__(self,tieba_name):
self.tieba_name = tieba_name
self.url = "https://tieba.baidu.com/f?kw="+tieba_name+"&ie=utf-8&pn={}"
self.headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0"}
def get_url_list(self):
url_list = []
for i in range(1000):
url_list.append(self.url.format(i*50))
return url_list
def parse_url(self,url): #發送請求,獲取響應
print(url)
r = requests.get(url,headers=self.headers)
return r.content.decode()
def save_html(self,html_str,page_num): # 保存html字符串
file_path = "{}-第{}頁.html".format(self.tieba_name,page_num)
with open(file_path,"w") as f:
f.write(html_str)
def run(self): #實現主要邏輯
# 1.構造url列表
url_list = self.get_url_list()
# 2.遍歷,發送請求,獲取響應
for url in url_list:
html_str = self.parse_url(url)
# 3.保存
page_num = url_list.index(url) + 1
self.save_html(html_str,page_num)
if __name__ == '__main__':
tieba_spider = TiebaSpider("李毅")
tieba_spider.run()
當我們運行的時候卻出錯了
看看提示什麼錯誤
似乎是編碼問題
其實是寫入文件的編碼方式不對
改改這裏就行了,加一個編碼方式寫入文件
def save_html(self,html_str,page_num): # 保存html字符串
file_path = "{}-第{}頁.html".format(self.tieba_name,page_num)
with open(file_path,"w",encoding="utf-8") as f:
f.write(html_str)
在來運行運行,發現可以用了(中途終止了程序)
既然是任意貼吧的爬蟲,我們來試試爬取別的,看看能不能看到效果
比如爬取lol的頁面
只要實例化時傳入相應的參數就行
if __name__ == '__main__':
tieba_spider = TiebaSpider("lol")
tieba_spider.run()
來看看結果
發現是可以用的
那麼程序就寫完了
這段程序裏面的run
方法主要實現邏輯,更好的可以方便觀察,提高編程技巧
其實有個地方可以寫的更簡單
def get_url_list(self):
url_list = []
for i in range(1000):
url_list.append(self.url.format(i*50))
return url_list
可以改成這樣的,一句就可以解決
def get_url_list(self):
# url_list = []
# for i in range(1000):
# url_list.append(self.url.format(i*50))
# return url_list
return [self.url.formate(i*50) for i in range(1000)]
發送post請求
一些地方我們會使用post請求
- 登入註冊(
post
比get
更安全) - 需要傳輸大文本內容的時候(post請求對數據長度沒有要求)
相對與get請求多了一個data
r = requests.pot(url,data=data,headers=headers)
百度翻譯就是一個典型的post請求,因爲翻譯時候往往是輸入很多內容,我們來抓包,來獲取百度翻譯的posturl和postdata
這很簡單,一個一個往下找就是了 ~ _ ~
我們需要找到post的url,和需要post的數據
需要點擊翻譯按鈕,然後進行抓包,點擊翻譯按鈕相當於發送了一次post請求
雖然這data裏面的內容後面幾個還不清楚是什麼意思,我們可以寫如下程序,來檢測一下
import requests
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0"}
data={
"from": "zh",
"to": "en",
"query": "你好",
"transtype": "translang",
"simple_means_flag": "3",
"sign": "232427.485594",
"token": "8e3e547a6eeb51b0e15dadf70e7e01f0"
}
post_url = "https://fanyi.baidu.com/v2transapi"
r = requests.post(post_url,data=data,headers=headers)
print(r)
打印後發現狀態碼是200,但是200只是意味着我們請求某一個url成功,並不代表請求我們想要的url成功,因爲可能網頁有跳轉
我們再打印內容看看
print(r.content.decode())
發現並沒有得到我們想要得數據
到底是什麼原因導致我們拿不到我們想要得數據?
有兩點問題
第一,headers
不全,可能需要加入更多得鍵值對
第二,是data
裏面得鍵值對,是不是都需要,還是要不要某個鍵值對要變,不是固定值
其實問題是出在第二點
我們也不知道哪個鍵值對需不需要,哪個鍵值對變不變化
我們可以換一個方式
不用和它正面槓,我們可以使用手機版得百度翻譯,繞行方法
我們需要再來看看手機版的,進行再一次抓包
點擊這個切換到,手機模式