requests模塊介紹
相對於python自帶的urllib模塊,requests模塊提供了相對更高層的api來進行網頁訪問的工作。
對於requests模塊,使用很簡單,一般我們會用到兩個函數:
- requests.get(url,params=None,**kwargs) 這裏的params是我們要傳入的query,它的格式是dict。
- requests.post(url,data=None,**kwargs) 這裏的data就是我們要提交的表單data,也是直接傳入dict就好。
以上兩個函數分別對應http協議中的"GET"方法與"POST"方法,除了這兩者,還有如"PUT"、"DELETE"等方法,在requests模塊中有一個統一的函數來發起不同“方法”的http請求報文:
- requests.request(method,url,**kwargs) 可以看到該函數的第一個參數method的取值就是"GET"、"POST"等。
- 該方法與上文提到的兩個方法,返回值都是requests.Response對象,後面我們會對該對象與requests.Request對象進行介紹
- 較常用的關鍵字參數:params,data,headers,proxies,stream等。
- 其實上文所介紹的兩個函數get和post,或是對應其他方法的函數,它們的實現就是使用request.requests函數的:
def get(url, params=None, **kwargs):
kwargs.setdefault('allow_redirects', True)
#這裏可見request.get的實質
return request('get', url, params=params, **kwargs)
這裏來詳細介紹一下headers,proxies和stream關鍵字參數的用途:
- headers參數就是http請求報文的頭部,它的格式是一個dict,其中最為常用的headers元素就是User-Agent,模仿瀏覽器訪問網頁。
- proxies參數就是代理,它的格式也是一個dict,每一個鍵值對是這樣的形式:"協議":"ip:port"。
- stream參數是相對前兩者較陌生的一個參數,該參數默認為False,意味着我們會一下子把網頁內容都下載,但如果主動設置為True的話,則不會立刻下載網頁內容,而是等到使用requests.Response的iter_content才會迭代地把數據下載並讀進內存中。
requests.Request&requests.Response
這兩個對象詳細對爬蟲有過了解的朋友們都很熟悉了,它們是在爬蟲邏輯中很關鍵的兩個對象,簡單來説:發出Request,返回Response。
requests.Request
我們在使用requests時一般不會直接創建Request對象,所以這裏我們大致瞭解一下即可:
requests.Request(method=None, url=None, headers=None, data=None, params=None) 我們列出Request類構造時所需的一些常用參數,並且前文我們提到requests.get等函數的實質是requests.request函數,那麼其實研究該函數的源碼:
def request(method, url, **kwargs):
with sessions.Session() as session:
#可以看到在request函數內調用了session.request方法
return session.request(method=method, url=url, **kwargs)
#這個是session.request方法的定義
def request(self, method, url,
params=None, data=None, headers=None, cookies=None, files=None,
auth=None, timeout=None, allow_redirects=True, proxies=None,
hooks=None, stream=None, verify=None, cert=None, json=None):
#可以看到這裏其實使用傳入參數
#創建了一個requests.Request實例
req = Request(
method=method.upper(),
url=url,
headers=headers,
files=files,
data=data or {},
json=json,
params=params or {},
auth=auth,
cookies=cookies,
hooks=hooks,
)
#進一步處理,得到對應的PreparedRequest對象
prep = self.prepare_request(req)
proxies = proxies or {}
settings = self.merge_environment_settings(
prep.url, proxies, stream, verify, cert
)
# Send the request.
send_kwargs = {
'timeout': timeout,
'allow_redirects': allow_redirects,
}
send_kwargs.update(settings)
#這裏是真正的send Request,並返回一個Response對象
resp = self.send(prep, **send_kwargs)
return resp
由以上代碼可知,其實requests.request方法的實質就是創建一個Request實例,在對其進行一定預處理後將其send,然後得到Response。
requests.Response
我們之前的requests.get、requests.post或是requests.request函數的返回對象就是一個requests.Response實例。對於Response類,我們主要介紹幾個常用屬性與方法:
- Response.content 以bytes的形式得到返回Response的內容,其實也就是未解碼的html文件
- Response.text 文本形式的Response內容,也就是解碼了的html文件,且如Response.encoding屬性為None的話,那麼會以chardet去猜測bytes內容的編碼方式。當然我們也可以在access這個屬性前人為指定一種編碼方式。
- Response.encoding 指定以何種方式來解碼,Response內容的編碼完全基於HTTP報頭,遵循RFC2616文件。
- Response.url 即Response的url
- Response.status_code 相應的狀態碼,如成功的話該值就是200
- Response.request 得到對應於這個Response的Request對象,其實是(PreparedRequest),通過這個request對象我們可以得到當時訪問時的url、method、headers等屬性。
- Response.iter_content(chunk_size=1),該函數返回一個generator,其中的chunk_size決定我們每次下載並讀進內存中多少個字節,一般使用方法為for item in Response.iter_content(256)這樣的for循環遍歷即可。
BeautifulSoup
BeautifulSoup是一個可以從HTML或XML文件中提取數據的Python庫,通常我們使用requests得到html文件(Response.text),然後我們再使用BeautifulSoup來處理。從而提取到我們需要的信息。
如何使用BeautifulSoup
from bs4 import BeautifulSoup
#其中html是返回的網頁文本,也就是response.text
#而lxml是BeautifulSoup使用的文檔解析器,需要我們
#已經預先pip install好lxml這個模塊,或者我們也可
#使用python自帶的html.parser,不過它的速度較慢些
#而soup就是一個BeautifulSoup對象,它承載了一個
#由html文檔內部各個元素所形成的樹形結構。
soup=BeautifulSoup(html,"lxml")
#以下就是幾個最簡單基本的使用
#直接以屬性引用的方式得到html文檔中的第一個a標籤
print(soup.a)
#進一步得到html文檔中第一個a標籤的中的字符串部分(如果存在的話)
print(soup.a.string)
#拿到html文檔中第一個a標籤的href屬性的值
print(soup.a["href"])
以上大致介紹了BeautifulSoup的簡單實用,接下來我們進行更詳細地分析:
BeautifulSoup將HTML文檔轉換成一個複雜的樹形結構,該樹形結構中的每個節點都是Python對象,所有對象可分為4種: Tag、NavigableString、BeautifulSoup、Comment。
- Tag對象 對應的就是html文檔中的標籤,它有很多屬性與方法,這裏先介紹它最重要的兩個屬性:1.tag.name返回的就是該tag標籤的名字(比如tag對應a標籤,那麼tag.name返回的就是"a")。2.tag.attrs以字典的形式返回該標籤所有的屬性,如{"herf":"www.baidu.com"}。而我們想拿到屬性值就可以用tag.attrs["href"],不過上文也看到了,這裏其實可以直接簡寫為tag["href"]。
- NavigableString對象 它其實就是我們使用soup.a.string時真正返回的對象,它是對python自帶的string對象進行了一個包裝,我們可以就把它當作string使用,不需要在意其它。
- BeautifulSoup對象 它對應我們文檔的全部內容,也就是上文的soup對象,大部分時間我們可以把它當作tag對象一樣來使用方法,不過它沒有attrs屬性,並且它的name屬性的值只為:["document"]。
- Comment對象 它對應html文檔中的註釋標籤:<!-- 此處寫註釋 -->,該標籤很特別的是它不會被瀏覽器顯示,只是一個對程序員註釋的作用。該對象在實際應用中很少使用,這裏不作更進一步的介紹。
接下來我們要來對tag對象以及BeautifulSoup對象在使用method上進行更進一步的介紹:
而所謂的method使用,我們着眼的就是在得到的BeautifulSoup對象的樹形結構中對所需要的信息進行搜索的工作。
這樣的搜索工作根據對節點本身信息和節點之間在樹形結構中的關係的應用不同而分為兩種。
第一種,由節點本身信息對節點進行搜索:
所謂tag.a其實就是tag.find("a"),該方法的具體函數頭如下
find(name,attrs,recursive,string,**kwargs)
name就是標籤名,它的值是一個“過濾器”。
attrs就是該name對應標籤的屬性,同樣值也是一個“過濾器”。
recursive是一個bool值,默認為True。它的意思是搜索當前tag的所有子孫節點,如果為False,則只搜索當前tag的直接子節點
string就是該name對應的string值,也是一個“過濾器”。
**kwargs一般使用不用理會。
當然上面的tag.a或是tag.find("a")都只能得到tag下的第一個a標籤,
這太侷限了,如果我們想要的是後面的第三個a標籤呢?於是就有了
tag.find_all("a")方法,返回一個列表,來得到所有的a標籤,簡寫為tag("a")。
find_all(name,attrs,recursive,string,**kwargs)
參數的意義和find函數一樣
下面我們來講解一下這個所謂的“過濾器”到底是什麼東西
具體的代碼實現有點繁瑣,總之我們可以把它理解為一種
對象,我們允許這個對象有多種值。
(1)字符串值 最簡單的就是傳入字符串值,如之前的tag.a
(2)正則表達式值 即re.compile(r"\d+")這樣的形式
(3)列表值 如name=["a","div"],則find只會返回其中的後者,
find_all會返回一個列表,包含tag下的所有a和div標籤。
(4)True 意思不做過濾,對於find是返回tag下符合要求的標籤的第一個,對於find_all是返回所有。比如name=True,那麼就不對name
過濾,對其他attrs或string繼續篩選過濾。
第二種,根據節點所在樹形結構中的關係對其它節點進行搜索:
直接子節點:
tag.childern和tag.contents是tag對象的兩個屬性,注意不是對應標籤的屬性!!!它們返回當前tag節點在樹形結構中的直接子節點。
tag.childern返回一個生成器
tag.contents返回一個列表
子孫節點:
tag.descendants返回一個生成器,對它進行遍歷可以得到當前tag節點的所有子孫節點的循環遍歷結果。
直接父節點:
tag.parent獲取當前tag的直接父節點
所以父節點:
tag.parents返回一個生成器,可以獲取當前tag的所有父輩節點
next的兄弟節點:
tag.next_sibling和tag.next_siblings,返回值類型不用贅述。
previous的兄弟節點:
tag.previous_sibling和tag.previous_siblings,同樣返回類型不用贅述。
以上大概就是BeautifulSoup在搜索信息時所需的知識,其它如兩種方式結合的tag.find_parent(name,attrs,recursive,string,**kwargs)等方法,之後可以慢慢了解。