目錄:
- Python網(wǎng)絡(luò)爬蟲(一)- 入門基礎(chǔ)
- Python網(wǎng)絡(luò)爬蟲(二)- urllib爬蟲案例
- Python網(wǎng)絡(luò)爬蟲(三)- 爬蟲進(jìn)階
- Python網(wǎng)絡(luò)爬蟲(四)- XPath
- Python網(wǎng)絡(luò)爬蟲(五)- Requests和Beautiful Soup
- Python網(wǎng)絡(luò)爬蟲(六)- Scrapy框架
- Python網(wǎng)絡(luò)爬蟲(七)- 深度爬蟲CrawlSpider
- Python網(wǎng)絡(luò)爬蟲(八) - 利用有道詞典實(shí)現(xiàn)一個簡單翻譯程序
1.簡介
Requests 是用Python語言編寫,基于 urllib,采用 Apache2 Licensed 開源協(xié)議的 HTTP 庫沼琉。它比 urllib 更加方便运沦,可以節(jié)約我們大量的工作浸须,完全滿足 HTTP 測試需求简十。
- Beautiful is better than ugly.(美麗優(yōu)于丑陋)
- Explicit is better than implicit.(清楚優(yōu)于含糊)
- Simple is better than complex.(簡單優(yōu)于復(fù)雜)
- Complex is better than complicated.(復(fù)雜優(yōu)于繁瑣)
- Readability counts.(重要的是可讀性)
2.安裝
- 利用 pip 安裝
pip install requests
- 或者利用 easy_install
easy_install requests
3.基本請求方式
requests為爬蟲開發(fā)者們提供了8種基本的請求方式硕勿,由于在web開發(fā)過程中鄙早,大家發(fā)現(xiàn)對于服務(wù)器數(shù)據(jù)的處理方式?jīng)]有一開始制定標(biāo)準(zhǔn)時設(shè)計的那么復(fù)雜弄慰,所以一般情況下都簡化成了get/post兩種常見的請求方式
req = requests.request(method,url, **kw)
req = requests.post(url, **kw)
req = requests.get(url, **kw)
req = requests.delete(url, **kw)
req = requests.put(url, **kw)
req = requests.head(url, **kw)
req = requests.options(url, **kw)
req = requests.patch(url, **kw)
1.基本GET請求
- 最基本的GET請求可以直接用get方法
#python開發(fā)人員常用的測試地址 http://httpbin.org/gets
r = requests.get("http://httpbin.org/get")
- 如果想要加參數(shù),可以利用 params 參數(shù)
import requests payload = {'key1': 'value1', 'key2': 'value2'} r = requests.get("http://httpbin.org/get", params=payload) print r.url
運(yùn)行結(jié)果:
http://httpbin.org/get?key2=value2&key1=value1
- 如果想請求JSON文件蝶锋,可以利用 json() 方法解析
寫一個JSON文件命名為a.json
["foo", "bar", { "foo": "bar" }]
利用如下程序請求并解析
import requests r = requests.get("a.json") print r.text print r.json()
運(yùn)行結(jié)果如下陆爽,其中一個是直接輸出內(nèi)容,另外一個方法是利用 json() >方法解析
["foo", "bar", { "foo": "bar" }] [u'foo', u'bar', {u'foo': u'bar'}]
- 如果想獲取來自服務(wù)器的原始套接字響應(yīng)扳缕,可以取得 r.raw 慌闭。 不過需要>在初始請求中設(shè)置 stream=True 。
r = requests.get('https://github.com/timeline.json', stream=True) r.raw <requests.packages.urllib3.response.HTTPResponse object at >0x101194810> r.raw.read(10) '\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
這樣就獲取了網(wǎng)頁原始套接字內(nèi)容躯舔。
- 如果想添加 headers驴剔,可以傳 headers 參數(shù)
import requests payload = {'key1': 'value1', 'key2': 'value2'} headers = {'content-type': 'application/json'} r = requests.get("http://httpbin.org/get", params=payload, headers=headers) print r.url
2.基本POST請求
- 對于 POST 請求來說,我們一般需要為它增加一些參數(shù)粥庄。那么最基本的傳參方法可以利用 data 這個參數(shù)丧失。
import requests payload = {'key1': 'value1', 'key2': 'value2'} r = requests.post("http://httpbin.org/post", data=payload) print r.text
運(yùn)行結(jié)果:
{ "args": {}, "data": "", "files": {}, "form": { "key1": "value1", "key2": "value2" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "23", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1" }, "json": null, "url": "http://httpbin.org/post" }
- 有時候我們需要傳送的信息不是表單形式的,需要我們傳JSON格式的數(shù)據(jù)過去惜互,所以我們可以用 json.dumps() 方法把表單數(shù)據(jù)序列化布讹。
import json import requests url = 'http://httpbin.org/post' payload = {'some': 'data'} r = requests.post(url, data=json.dumps(payload)) print r.text
運(yùn)行結(jié)果:
{ "args": {}, "data": "{\"some\": \"data\"}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "16", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1" }, "json": { "some": "data" }, "url": "http://httpbin.org/post" }
通過上述方法,我們可以POST JSON格式的數(shù)據(jù)
- 上傳文件训堆,那么直接用 file 參數(shù)即可
新建一個 a.txt 的文件描验,內(nèi)容寫上 Hello World!
import requests url = 'http://httpbin.org/post' files = {'file': open('test.txt', 'rb')} r = requests.post(url, files=files) print r.text
運(yùn)行結(jié)果:
{ "args": {}, "data": "", "files": { "file": "Hello World!" }, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "156", "Content-Type": "multipart/form-data; >boundary=7d8eb5ff99a04c11bb3e862ce78d7000", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1" }, "json": null, "url": "http://httpbin.org/post" }
這樣我們便成功完成了一個文件的上傳。
- requests 是支持流式上傳的坑鱼,這允許你發(fā)送大的數(shù)據(jù)流或文件而無需先把它們讀入內(nèi)存膘流。要使用流式上傳,僅需為你的請求體提供一個類文件對象即可
with open('massive-body') as f: response = requests.post('http://some.url/streamed', data=f) print(response.text)
4.Cookies
- 如果一個響應(yīng)中包含了cookie,那么我們可以利用 cookies 變量來拿到
import requests response = requests.get("http://www.baidu.com/") # 返回CookieJar對象: cookiejar = response.cookies #將CookieJar轉(zhuǎn)為字典: cookiedict = requests.utils.dict_from_cookiejar(cookiejar) print cookiejar print cookiedict
5.超時配置
- 用 timeout 變量來配置最大請求時間
requests.get('http://github.com', timeout=0.001)
注:timeout 僅對連接過程有效呼股,與響應(yīng)體的下載無關(guān)耕魄。
6.會話對象
在以上的請求中,每次請求其實(shí)都相當(dāng)于發(fā)起了一個新的請求彭谁。也就是相當(dāng)于我們每個請求都用了不同的瀏覽器單獨(dú)打開的效果吸奴。也就是它并不是指的一個會話,即使請求的是同一個網(wǎng)址马靠。比如
import requests requests.get('http://httpbin.org/cookies/set/sessioncookie/123456789') r = requests.get("http://httpbin.org/cookies") print(r.text)
結(jié)果:
{ "cookies": {} }
- 很明顯奄抽,這不在一個會話中,無法獲取 cookies甩鳄,那么在一些站點(diǎn)中逞度,我們需要保持一個持久的會話,就像用一個瀏覽器逛淘寶一樣,在不同的選項(xiàng)卡之間跳轉(zhuǎn)妙啃,這樣其實(shí)就是建立了一個長久會話档泽。
import requests s = requests.Session() s.get('http://httpbin.org/cookies/set/sessioncookie/123456789') r = s.get("http://httpbin.org/cookies") print(r.text)
在這里我們請求了兩次,一次是設(shè)置 cookies揖赴,一次是獲得 cookies
運(yùn)行結(jié)果{ "cookies": { "sessioncookie": "123456789" } }
- 發(fā)現(xiàn)可以成功獲取到 cookies 了馆匿,這就是建立一個會話到作用。
那么既然會話是一個全局的變量燥滑,那么我們肯定可以用來全局的配置了渐北。
import requests s = requests.Session() s.headers.update({'x-test': 'true'}) r = s.get('http://httpbin.org/headers', headers={'x-test2': 'true'}) print r.text
通過 s.headers.update 方法設(shè)置了 headers 的變量。兩個變量都傳送過>去了铭拧。
運(yùn)行結(jié)果:{ "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1", "X-Test": "true", "X-Test2": "true" } }
- get方法傳的headers 同樣也是 x-test赃蛛,它會覆蓋掉全局的配置
{ "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1", "X-Test": "true" } }
- 如果不想要全局配置中的一個變量設(shè)置為 None 即可
r = s.get('http://httpbin.org/headers', headers={'x-test': None})
運(yùn)行結(jié)果:
{ "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1" } }
7.SSL證書驗(yàn)證
現(xiàn)在隨處可見 https 開頭的網(wǎng)站,Requests可以為HTTPS請求驗(yàn)證SSL證書搀菩,就像web瀏覽器一樣呕臂。要想檢查某個主機(jī)的SSL證書,你可以使用 verify 參數(shù),比如12306的證書無效肪跋。
測試如下:
import requests r = requests.get('https://kyfw.12306.cn/otn/', verify=True) print r.text
結(jié)果:
requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)
- 如果我們想跳過剛才 12306 的證書驗(yàn)證歧蒋,把 verify 設(shè)置為 False 即可
在默認(rèn)情況下 verify 是 True,所以如果需要的話州既,需要手動設(shè)置下這個變量谜洽。import requests r = requests.get('https://kyfw.12306.cn/otn/', verify=False) print r.text
- 也可以引入python 關(guān)于證書的處理模塊SSL
# 1. 導(dǎo)入Python SSL處理模塊
import ssl
# 2. 表示忽略未經(jīng)核實(shí)的SSL證書認(rèn)證
context = ssl._create_unverified_context()
8.代理
- 如果需要使用代理,你可以通過為任意請求方法提供 proxies 參數(shù)來配置單個請求
import requests # 根據(jù)協(xié)議類型易桃,選擇不同的代理 proxies = { "https": "http://41.118.132.69:4433" "http": "http://41.118.132.69:4433" } r = requests.post("http://httpbin.org/post", proxies=proxies) print r.text
- 私密代理驗(yàn)證(特定格式) 和 Web客戶端驗(yàn)證(auth 參數(shù))
#私密代理
import requests
# 如果代理需要使用HTTP Basic Auth褥琐,可以使用下面這種格式:
proxy = { "http": "mr_mao_hacker:sffqry9r@61.158.163.130:16816" }
response = requests.get("http://www.baidu.com", proxies = proxy)
print response.text
- web客戶端驗(yàn)證
如果是Web客戶端驗(yàn)證,需要添加 auth = (賬戶名, 密碼)
import requests
auth=('test', '123456')
response = requests.get('http://192.168.199.107', auth = auth)
print response.text
- 也可以通過環(huán)境變量 HTTP_PROXY 和 HTTPS_PROXY 來配置代理
export HTTP_PROXY="http://10.10.1.10:3128" export HTTPS_PROXY="http://10.10.1.10:1080"
通過以上方式晤郑,可以方便地設(shè)置代理。
5.程序中的使用
安裝好request模塊之后,在程序中就可以通過import引入并且使用了
- 代碼操作(一)爬取天氣
# -*- coding:utf-8 -*-
# 引入requests模塊
import requests
# 發(fā)送請求造寝,得到服務(wù)器返回的響應(yīng)對象磕洪,通過encoding設(shè)置響應(yīng)中數(shù)據(jù)的編碼
response = requests.get("http://www.sojson.com/open/api/weather/json.shtml?city=%E9%83%91%E5%B7%9E")
response.encoding="utf-8"
# 通過text打印響應(yīng)中的文本數(shù)據(jù)
print(response.text)
結(jié)果:
{"message":"Success !",
"status":200,"city":"鄭州","count":26,
"data":{"shidu":"74%","pm25":71.0,"pm10":145.0,"quality":"良","wendu":"31",
"ganmao":"極少數(shù)敏感人群應(yīng)減少戶外活動",
"yesterday":{"date":"10日星期四","sunrise":"05:41","high":"高溫 37.0℃",
"low":"低溫 25.0℃","sunset":"19:20","aqi":141.0,"fx":"西風(fēng)","fl":"<3
級","type":"晴","notice":"lovely sunshine,盡情享受陽光的溫暖吧"},
"forecast":[{"date":"11日星期五","sunrise":"05:42","high":"高溫 37.0℃",
"low":"低溫 25.0℃","sunset":"19:19","aqi":100.0,"fx":"南風(fēng)",
"fl":"<3級","type":"晴","notice":"天氣干燥诫龙,請適當(dāng)增加室內(nèi)濕度"},
{"date":"12日星期六","sunrise":"05:42","high":"高溫 31.0℃","low":"低溫
24.0℃","sunset":"19:18","aqi":55.0,"fx":"東風(fēng)","fl":"<3級","type":"陣雨",
"notice":"突如其來的雨析显,總叫讓人措手不及"},{"date":"13日星期日",
"sunrise":"05:43","high":"高溫 30.0℃","low":"低溫 24.0℃",
"sunset":"19:17","aqi":60.0,"fx":"北風(fēng)","fl":"<3級","type":"陣雨","notice":"突如其來的雨,總叫讓人措手不及"},
{"date":"14日星期一","sunrise":"05:44","high":"高溫 33.0℃",
"low":"低溫 24.0℃","sunset":"19:16","aqi":62.0,"fx":"東北風(fēng)","fl":"<3級",
"type":"晴","notice":"lovely sunshine签赃,盡情享受陽光的溫暖吧"},
{"date":"15日星期二","sunrise":"05:45","high":"高溫 35.0℃",
"low":"低溫 24.0℃","sunset":"19:14","aqi":65.0,"fx":"南風(fēng)","fl":"<3級",
"type":"晴","notice":"天氣干燥谷异,請適當(dāng)增加室內(nèi)濕度"}]}}
- 代碼操作(二)- 爬取百度圖片你的名字
先獲取圖片鏈接地址
# -*- coding:utf-8 -*-
import requests,re,urllib,urllib2
#定義引擎函數(shù)模塊
def getImg(url):
content = load_url(url)
#數(shù)據(jù)獲取完成,定義正則表達(dá)式锦聊,篩選數(shù)據(jù)
reg = re.compile('"thumbURL":\s*"(.*?)"', re.S)
data_list = data_filter(content,reg)
#保存數(shù)據(jù)
for index,data_url in enumerate(data_list):
sava_data(data_url,"./images/" + str(index) + ".jpg")
#定義爬取數(shù)據(jù)的函數(shù)
def load_url(url):
"""
作用:爬取指定url地址中的數(shù)據(jù)
:param url: 指定的url地址
:return: 所有爬取到的數(shù)據(jù)
"""
print('開始爬取圖片')
my_headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',
}
response = requests.get(url,headers=my_headers,verify=False)
response.encoding = 'UTF-8'
content = response.text
print('數(shù)據(jù)爬取完成')
return content
#定義篩選數(shù)據(jù)的函數(shù)
def data_filter(data,reg):
"""
作用:進(jìn)行數(shù)據(jù)按照指定描述的正則篩選
:param data: 所有數(shù)據(jù)
:param reg: 正則表達(dá)式
:return: 返回篩選到的數(shù)據(jù)列表
"""
print('---開始篩選數(shù)據(jù)')
data_list = reg.findall(data)
print('篩選數(shù)據(jù)完成')
return data_list
#定義保存數(shù)據(jù)的函數(shù)
def sava_data(url_content,file_name):
"""
作用:保存數(shù)據(jù)
:param url_content:需要保存的數(shù)據(jù)
:param file_name: 文件名稱
:return:
"""
print('開始保存數(shù)據(jù)')
try:
urllib.urlretrieve(url_content, file_name)
#另外一種保存方式
# my_headers = {
# 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',
# }
# print (url_content)
# content = requests.get(url_content, headers=my_headers).content
# with open(file_name, "wb") as f:
# f.write(content)
print('圖片下載成功')
except Exception as result:
print('圖片下載失敗'+str(result))
print('數(shù)據(jù)保存完成')
#定義主程序入口
if __name__ == '__main__':
#定義獲取各種需要的參數(shù)數(shù)據(jù)
url = 'https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E4%BD%A0%E7%9A%84%E5%90%8D%E5%AD%97&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&word=%E4%BD%A0%E7%9A%84%E5%90%8D%E5%AD%97&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&pn=30&rn=30&gsm=1e&1502454817018='
#調(diào)用引擎對象歹嘹,執(zhí)行爬蟲
getImg(url)
結(jié)果:
寫到最后說些題外話,今天爬取一個網(wǎng)站的時候還發(fā)生了一件好玩的事孔庭,網(wǎng)站的反爬機(jī)制導(dǎo)致爬取的數(shù)據(jù)只是一個html網(wǎng)頁尺上,看到了頁面上的話,我想起了一句話圆到,對于爬蟲工程師來說每天都是不停地和對方的反爬工程師斗智斗勇怎抛。
4.BeautifulSoup4
1.Beautiful Soup的簡介
Beautiful Soup是python的一個庫芽淡,最主要的功能是從網(wǎng)頁抓取數(shù)據(jù)马绝。官方解釋如下:
- Beautiful Soup提供一些簡單的、python式的函數(shù)用來處理導(dǎo)航挣菲、搜索富稻、修改分析樹等功能。它是一個工具箱己单,通過解析文檔為用戶提供需要抓取的數(shù)據(jù)唉窃,因?yàn)楹唵危圆恍枰嗌俅a就可以寫出一個完整的應(yīng)用程序纹笼。
- Beautiful Soup自動將輸入文檔轉(zhuǎn)換為Unicode編碼纹份,輸出文檔轉(zhuǎn)換為utf-8編碼。你不需要考慮編碼方式廷痘,除非文檔沒有指定一個編碼方式蔓涧,這時,Beautiful Soup就不能自動識別編碼方式了笋额。然后元暴,你僅僅需要說明一下原始編碼方式就可以了。
- Beautiful Soup已成為和lxml兄猩、html6lib一樣出色的python解釋器茉盏,為用戶靈活地提供不同的解析策略或強(qiáng)勁的速度鉴未。
2. Beautiful Soup 安裝
Beautiful Soup 3 目前已經(jīng)停止開發(fā),推薦在現(xiàn)在的項(xiàng)目中使用Beautiful Soup 4鸠姨,不過它已經(jīng)被移植到BS4了铜秆,也就是說導(dǎo)入時我們需要 import bs4 。所以這里我們用的版本是 Beautiful Soup 4.3.2 (簡稱BS4)讶迁,另外據(jù)說 BS4 對 Python3 的支持不夠好连茧,不過我用的是 Python2.7.7,如果有小伙伴用的是 Python3 版本巍糯,可以考慮下載 BS3 版本啸驯。
- 可以利用 pip 或者 easy_install 來安裝,以下兩種方法均可
easy_install beautifulsoup4
pip install beautifulsoup4
- 另一個可供選擇的解析器是純Python實(shí)現(xiàn)的 html5lib , html5lib的解析方式與瀏覽器相同,可以選擇下列方法來安裝html5lib:
easy_install html5lib
pip install html5lib
Beautiful Soup支持Python標(biāo)準(zhǔn)庫中的HTML解析器,還支持一些第三方的解析器罚斗,如果我們不安裝它,則 Python 會使用 Python默認(rèn)的解析器搀愧,lxml 解析器更加強(qiáng)大惰聂,速度更快,推薦安裝咱筛。
解析器 | 使用方法 | 優(yōu)勢 | 劣勢 |
---|---|---|---|
Python標(biāo)準(zhǔn)庫 | BeautifulSoup(markup, “html.parser”) | Python的內(nèi)置標(biāo)準(zhǔn)庫搓幌,執(zhí)行速度適中,文檔容錯能力強(qiáng) | Python 2.7.3 or 3.2.2)前的版本中文檔容錯能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, “l(fā)xml”) | 速度快迅箩,文檔容錯能強(qiáng) | 需要安裝C語言庫 |
lxml XML 解析器 | BeautifulSoup(markup, [“l(fā)xml”,“xml”])BeautifulSoup(markup, “xml”) | 速度快溉愁,唯一支持XML的解析器 | 需要安裝C語言庫 |
html5lib | BeautifulSoup(markup, “html5lib”) | 最好的容錯性,以瀏覽器的方式解析文檔饲趋,生成HTML5格式的文檔 | 速度慢不依賴外部擴(kuò)展 |
- BeautifulSoup 主要用來遍歷子節(jié)點(diǎn)及子節(jié)點(diǎn)的屬性拐揭,通過點(diǎn)取屬性的方式只能獲得當(dāng)前文檔中的第一個 tag,例如奕塑,soup.li堂污。如果想要得到所有的<li> 標(biāo)簽,或是通過名字得到比一個 tag 更多的內(nèi)容的時候,就需要用到 find_all(),find_all() 方法搜索當(dāng)前 tag 的所有 tag 子節(jié)點(diǎn),并判斷是否符合過濾器的條件find_all() 所接受的參數(shù)如下:
find_all( name , attrs , recursive , string , **kwargs )
-
find_all()
幾乎是 Beautiful Soup中最常用的搜索方法,也可以使用其簡寫方法龄砰,以下代碼等價:
soup.find_all("a")
soup("a")
- 如果只想得到 tag 中包含的文本內(nèi)容,那么可以只用
get_text()
方法,這個方法獲取到 tag 中包含的所有文版內(nèi)容包括子孫 tag 中的內(nèi)容,并將結(jié)果作為 Unicode 字符串返回:
tag.p.a.get_text()
參考文檔:Requests官方文檔及崔慶才老師的個人博客盟猖、 Beautiful Soup 4.4.0 文檔 (中文文檔)
如果你覺得我的文章還可以,可以關(guān)注我的微信公眾號:Python攻城獅