urllib-訪問網頁的兩種方式:GET與POST


學習自:https://www.jianshu.com/p/4c3e228940c8

使用參數、關鍵字訪問服務器

訪問網絡的兩種方法:

1、GET

  • 利用參數給服務器傳遞信息
  • 參數datadict類型,然后用parse.urlencode()編碼為str類型,用編碼后data+baseURL構成完整的URL
  • GET中不需要用encode
  • 打開網頁
  • 讀取頁面內容
  • 內容編碼轉換

2、POST

  • 一般向服務器傳遞參數使用
  • post把信息自動加密處理
  • 如果想使用post信息,需要用到data參數
  • 使用post,意味着HTTP的請求頭請求需要修改:
    • Content-Type:application/x-www.form-urlencode
    • Content-Length:數據長度
    • 簡而言之,一旦修改請求方法,請注意與其它請求的頭部信息相適應
  • urllib.parse.urlencode可以將dict類型轉化為str,encode將str轉化為bytes
  • 如果想要設置更多的頭部信息,那么urlopen()是無法滿足要求的,因此可以用request.Request().add_header(Header信息名,Header信息值)方法
  • request.Request()用來創建Request實例,它包含了所有信息,如url、data、headers、method等。最后只需要用request.urlopen()打開這個實例即可。

3、urllib.error

當使用request進行爬蟲時,盡量采用try...except代碼塊,並把爬蟲代碼放入try中,避免錯誤。

  • URLError產生的原因
    • 沒網絡
    • 服務器鏈接失敗
    • OSError的子類

 

  • HTTPError
    • URLError的子類

 

  • 兩者區別
    • HTTPError是對應的HTTP請求返回的錯誤
    • URLError對應的是一般的網絡問題,包括URL錯誤
    • 繼承關系有:OSError - URLError - HTTPError

 

GET訪問方式

1、parse.urlencode()的簡單應用

在使用搜索引擎時,如搜索“學習Python”,當搜索結果出來后,可以在URL地址欄發現如下的URL字符串:

https://www.baidu.com/s?wd=學習Python&...#后邊省略

如果我們只保留其中主要的部分

https://www.baidu.com/s?wd=學習Python

其搜索結果還是一樣的。

 

這就說明,在使用搜索引擎時,前面的 'https://www.baidu.com/s?' 是固定不變的,為baseURL,只需要輸入關鍵字即可返回結果。

此時,在瀏覽器中按F12進入開發者模式,查看Network->Name欄的Headers部分。這里就是HTTP請求頭Header的信息內容。

 

 可以看到,“學習”二字變成了一串由16進制字符組成的字符串%E5%AD%A6%E4%B9%A0,說明瀏覽器在發送請求時對URL進行了編碼

①利用parse模塊模擬GET請求

在使用爬蟲時,可以只需要輸入關鍵字,並且將關鍵字進行編碼,將其轉換成服務器識別的形式,最后把關鍵字baseURL連接起來即可實現訪問。

這里的編碼采用的方法就是parse模塊下的parse.urlencode()方法。

from urllib import parse,request

baseURL='https://www.baidu.com/s?'

wd=input('Input your keyword:')

#這里必須是wd,不能是其他的字符串
data={'wd':wd}
data=parse.urlencode(data)

URL=baseURL+data

with request.urlopen(URL) as f:
    html=f.read().decode('utf-8')

print(html)

運行程序,輸入“學習Python”,結果:

最終的URL: https://www.baidu.com/s?wd=%E5%AD%A6%E4%B9%A0Python
<!DOCTYPE html>
        <html>
        <head>
        </head>
        <body>
        </body>
</html>

打印出來的結果就是html文檔文件,也就是搜索“學習Python”后的網頁內容。

同時也能看到最終提交到服務器端的URL地址,其中“學習Python”中的漢字已經轉化為bytes類型了,這就是parse.urlencode()的作用。

注意:

1、baseURL最后是/s?而不是/?s

2、輸入的關鍵字,必須存儲為wd,不能自定義為其他的變量名

3、編碼前要先把關鍵字wd添加到一個dict對象中去,對得到的dict對象編碼

data={'wd':wd}

4、兩個關鍵語句:

data=parse.urlencode(data)

with request.urlopen(URL) as res:
    html=res.read().decode('utf-8')

其中,①parse.urlencode的參數必須是dict對象

request.urlopen(URL)獲取URL句柄,標記為對象res

對句柄res調用read()獲取最原始的信息,res.read()

對原始信息進行解碼后顯示:res.read().decode()

5、從服務器端讀取的內容為bytes類型,要用decode將之轉化為str類型,才能打印出來。

 

POST訪問方式

post方式訪問,是提交表單類的數據到服務器。

 

request.urlopen()中的data參數

百度翻譯舉例使用data參數:

 

利用parse模塊模擬POST請求:

  • 首先進入百度翻譯首頁,並用F12進入開發者模式,查看Network欄
  • 輸入單詞girl,可以發現每輸入一個字母后邊都有請求

 

 

  • 請求地址是:https://fanyi.baidu.com/sug
  • 請求方式是:POST

 

  • Form data的值是kw:girl,所以字典的Key名為kw(這點注意與get中的Key名為wd相區分)
  • 查看返回內容格式,content-length是返回內容長度;content-type是返回內容格式——返回的是json格式內容,需要用到json包

 

 

利用parse模擬POST請求

步驟:

  • 利用data參數構造內容,這里的內容就是之前所說的關鍵字內容Form data
  • 使用request.urlopen(url , data)打開url,並傳入內容data;baseURL不需要data連接以構成一個完整的URL
  • 返回的JSON數據類型,用json.loads()轉換為str字符串類型
  • 返回的結果就是搜索詞的釋義
from urllib import parse,request,error

#返回內容為JSON格式,用json.loads()轉換為str
import json

#基本URL
baseURL='https://fanyi.baidu.com/sug'

#為防止報錯,使用try...except語句塊,使用error.URLerror
try:
    #輸入Form data
    kw=input('Input your keyword:')

    #用data存放Form data;Request傳入的數據必須是字典類型
    #瀏覽器中開發模式下,Form data下字典的Key為kw,所以data的Key為kw
    data={'kw':kw}

    data=parse.urlencode(data).encode()
    print('即將發送的data數據的類型:',type(data))

    #打開網頁,傳入data參數
    #urlopen的參數為baseURL和data參數
    with request.urlopen(baseURL,data=data) as res:
        json_data=res.read().decode()
        print('返回數據的類型:',type(json_data))
        json_data=json.loads(json_data)
        print('轉換后的數據類型:',type(json_data))

        for i in json_data['data']:
            print(i)
except error.URLError as e:
    print(e)

 結果:

Input your keyword:>? Girl
即將發送的data數據的類型: <class 'bytes'>
返回數據的類型: <class 'str'>
轉換后的數據類型: <class 'dict'>
{'k': 'girl', 'v': 'n. 女孩; 姑娘; 女兒; 年輕女子; 女郎;'}
{'k': 'girls', 'v': 'n. 女孩; 姑娘; 女兒; 年輕女子; 女郎;  girl的復數;'}
{'k': 'girlfriend', 'v': 'n. 女朋友; 女情人; (女子的)女伴,女友;'}
{'k': 'girl friend', 'v': ' 未婚妻; 女性朋友;'}
{'k': "Girls' Generation", 'v': ' 少女時代(韓國SM娛樂有限公司於2007年推出的九名女子少女組合);'}

這就是使用parse模擬瀏覽器訪問服務器的用法。

補充:

1、最開始傳入的data為dict類型,最后返回的數據也是轉化為dict類型

2、Post的傳入關鍵字構建dataKey必須命名為爬蟲頁開發者模式下的Form data

3、url.urlencode(data),將原始的dict轉化為str類型;encode('utf-8'),將str類型轉化為bytes類型;服務器接收的類型為bytes類型,所以中間將data進行類型轉化的語句是:

data=parse.urlencode(data).encode() #encode參數缺省時默認為'utf-8'

此時的data為bytes類型,可以向服務器發送了

4、發送——request.urlopen(baseURL,data=data)

with request.urlopen(baseURL,data=data) as res:
                json_data=res.read()

用json_data接收返回信息。此時json_data為bytes類型,要通過decode()解碼為str類型,再通過json.loads()函數變為dict類型。

(實際應用時,我發現不用decode()解碼,直接對read()的結果進行json.loads(),同樣可以返回一個正常的dict對象,不知道以后有沒有影響?)

5、為防止報錯,可以用try...except程序塊

6、這里的urlopen中的URLbaseURL,與data各自為urlopen兩個參數不用連接成為一個完整的URL

 7、有的網站返回的數據不是json類型,這時候就不能用json.load了,具體類型應去網頁中通過F12開發者模式header->Content-Type獲知。

 

request.Request(url= , data= , heads=  , method='POST'/'GET')

request.urlopen()的功能有限,如果我們想為請求頭部添加更多的設置信息,如“Content-Length”,那么用urlopen()是無法實現的。

因此,在此基礎上,我們可以使用request.Request(),它的功能與urlopen()類似,可以打開網頁,但比urlopen()優秀的是,它可以為請求頭部自定義信息,無限擴展功能。

於是,在上面代碼的基礎上,可以修改一番:定義一個headers用以存放自定義的頭部信息,然后用request.Request()創建一個Request實例,該實例將所有信息全部包括,到最后只需要用urlopen()打開這個實例即可。

另外,如果想要往該Request實例中補充headers,假設該實例名為req,則可以用方法add_header()實現:

req.add_header(Key , Value)

但需要注意的是add_header的Key-Value必須是合法的,不合法的header會導致其他錯誤!

from urllib import request,error,parse
import json

baseURL='https://fanyi.baidu.com/sug'

kw=input('Input your keyword:')

data={'kw':kw}

data=parse.urlencode(data).encode()

#構造headers,這里的header是自定義要訪問的,如模擬瀏覽器訪問服務器
#構造的headers中,至少包含Content-Length
#傳入Request參數的data和headers都是dict類型
headers={'Content-Length':len(data)}

#用request.Request模擬瀏覽器訪問服務器
#requset.Request()構造了一個Request實例,其中可以包含大量header內容
#URL為baseURL,data為輸入的關鍵字參數,這里不用連接成一個完整的URL
req=request.Request(baseURL,data=data,headers=headers,method='POST')


#添加一個User-Agent的Header信息 req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25') #最后用urlopen()打開該實例即可 try : with request.urlopen(req) as res: json_data=res.read() rec=json.loads(json_data) for k in rec['data']: print(k) except error.URLError as e: print(e)

 

結果與之前的相同。

比urlopen()好的是,Request可以無限擴展功能,模擬瀏覽器訪問服務器,不僅如此,對於之后的身份隱藏等操作,也只需在headers中設置即可。

Post爬蟲的基本操作流程:

①使用data參數創建內容(需要搜索的內容)

②用parse.urlencode和encode進行轉碼

③用urlopen或者request.Request打開URL,並傳入data

④返回結果

⑤對結果進行其他操作

 

總結:

1、關鍵字參數(需要搜索的內容比如data)必須是dict類型,發送請求時,首先用parse.urlencode(data),將dict類型轉化為str,再用encode()將str轉化為服務器接收的bytes類型。

而把接收到的數據轉化時,需要先read(),再decode(),再json.loads()。(實際應用時,發現不用decode()一樣可以得到相同結果,推測可能是json.loads()的作用。暫不知道這樣做有沒有副作用)

2、想要對某個網頁進行爬蟲操作,需要用F12打開開發者模式,查看這個頁面的信息。主要信息是:請求URL訪問服務器的方式返回數據的格式(如果是JSON,需要用json模塊轉化)Form data 如果有該項,要確定參數的Key名

3、dict類型:Request參數的data、headers;urlopen參數的data

4、GET與POST在程序中的最大區別是:

GET中的urlopen的參數只有URL(可能有headers)

POST中的urlopen的參數是URL+data(可能有headers)dataPOST請求的數據

#GET
req=request.Request(URL)
with request.urlopen(req) as res:
        ...

#POST
req=request.Request(baseURL , data=data , headers=headers , method = 'POST')
with request.urlopen(req) as res:
        ...

5、header不是GET與POST的區別,這兩種請求均可以加header

方式是:

 

req=request.Request(URL,headers=headers)

 

header的目的是通過加首部信息模擬瀏覽器服務器訪問

 

6、有的網站返回的數據不是json類型,這時候就不能json.load了,具體類型應去網頁中通過F12開發者模式header->Content-Type獲知。

比如:訪問百度首頁時的請求,就是html類型

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM