0. 請(qǐng)寫下這一節(jié)課你學(xué)習(xí)到的內(nèi)容:格式不限,回憶并復(fù)述是加強(qiáng)記憶的好方式!
上節(jié)課我們說(shuō)過(guò)了,有一些網(wǎng)站比較痛恨爬蟲(chóng)程序,它們不喜歡被程序所訪問(wèn),所以它們會(huì)檢查鏈接的來(lái)源,如果說(shuō)來(lái)源不是正常的途徑,那么它就會(huì)把你給屏蔽掉,所以呢,要讓我們的程序可以持續(xù)的干活,要可以投入生產(chǎn),我們就需要對(duì)代碼進(jìn)行隱藏,讓它看起來(lái)更像是普通人瀏覽器的正常點(diǎn)擊。
我們知道,服務(wù)器檢查鏈接是通過(guò)檢查 鏈接中的 Headers 中的 User Agent 來(lái)判斷你是來(lái)自于代碼還是來(lái)自于瀏覽器,像我們的Python,你用Python默認(rèn)的Headers 中的 User Agent 是Python 加上版本號(hào),服務(wù)器一檢查是Python,就會(huì)把你屏蔽掉。我們可以修改 Headers 來(lái)模擬正常的瀏覽器訪問(wèn)。
先看一下文檔:
urllib.request.Request?有一個(gè) headers 的參數(shù),通過(guò)修改 headers 參數(shù),你可以設(shè)置自己的 headers,這個(gè)參數(shù)是一個(gè)字典,你可以通過(guò)兩種途徑來(lái)設(shè)置:一種是你直接設(shè)置一個(gè)字典,然后作為參數(shù)傳給 Request,或者第二種,在Request 生成之后,調(diào)用 add_header() 將其加進(jìn)去。我們使用上節(jié)課的例子來(lái)嘗試一下:
第一種 代碼清單:
-
#translation.py
-
import urllib.request
-
import urllib.parse
-
import json
-
print("---------這是一個(gè)Python翻譯器---------")
-
content = input('請(qǐng)輸入需要翻譯的內(nèi)容:')
-
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
-
#直接從審查元素中copy過(guò)來(lái)的url會(huì)報(bào)錯(cuò),必須把translate_o中的_o 刪除才可以
-
#url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
-
head = {}
-
head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
-
data = {} #這里就是把 Form Data 中的內(nèi)容貼過(guò)來(lái)
-
data['i'] = content
-
data['from'] = 'AUTO'
-
data['to'] = 'AUTO'
-
data['smartresult'] = 'dict'
-
data['client'] = 'fanyideskweb'
-
data['salt'] = '15445124815349'
-
data['sign'] = 'a824eba4c23c6f541ffadfee26b1e500'
-
data['ts'] = '1544512481534'
-
data['bv'] = 'bbb3ed55971873051bc2ff740579bb49'
-
data['doctype'] = 'json'
-
data['version'] = '2.1'
-
data['keyfrom'] = 'fanyi.web'
-
data['action'] = 'FY_BY_REALTIME'
-
data['typoResult'] = 'false'
-
#需要使用urllib.parse.urlencode() 把data轉(zhuǎn)換為需要的形式
-
data = urllib.parse.urlencode(data).encode('utf-8')
-
req = urllib.request.Request(url, data, head)
-
response = urllib.request.urlopen(url, data)
-
html = response.read().decode('utf-8')
-
target = json.loads(html)
-
print('翻譯結(jié)果:%s' %(target['translateResult'][0][0]['tgt']))
第二種 代碼清單:
-
#translation.py
-
import urllib.request
-
import urllib.parse
-
import json
-
print("---------這是一個(gè)Python翻譯器---------")
-
content = input('請(qǐng)輸入需要翻譯的內(nèi)容:')
-
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
-
#直接從審查元素中copy過(guò)來(lái)的url會(huì)報(bào)錯(cuò),必須把translate_o中的_o 刪除才可以
-
#url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
-
#head = {}
-
#head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
-
data = {} #這里就是把 Form Data 中的內(nèi)容貼過(guò)來(lái)
-
data['i'] = content
-
data['from'] = 'AUTO'
-
data['to'] = 'AUTO'
-
data['smartresult'] = 'dict'
-
data['client'] = 'fanyideskweb'
-
data['salt'] = '15445124815349'
-
data['sign'] = 'a824eba4c23c6f541ffadfee26b1e500'
-
data['ts'] = '1544512481534'
-
data['bv'] = 'bbb3ed55971873051bc2ff740579bb49'
-
data['doctype'] = 'json'
-
data['version'] = '2.1'
-
data['keyfrom'] = 'fanyi.web'
-
data['action'] = 'FY_BY_REALTIME'
-
data['typoResult'] = 'false'
-
#需要使用urllib.parse.urlencode() 把data轉(zhuǎn)換為需要的形式
-
data = urllib.parse.urlencode(data).encode('utf-8')
-
req = urllib.request.Request(url, data)
-
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36')
-
response = urllib.request.urlopen(url, data)
-
html = response.read().decode('utf-8')
-
target = json.loads(html)
-
print('翻譯結(jié)果:%s' %(target['translateResult'][0][0]['tgt']))
運(yùn)行結(jié)果:
-
=========== RESTART: C:\Users\XiangyangDai\Desktop\translation.py ===========
-
---------這是一個(gè)Python翻譯器---------
-
請(qǐng)輸入需要翻譯的內(nèi)容:愛(ài)
-
翻譯結(jié)果:love
-
>>> req.headers
-
{'User-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
我們來(lái)回顧一下,修改 headers 有兩個(gè)途徑:
- 通過(guò) Request 的 headers 參數(shù)修改
- 通過(guò) Request.add_header() 方法修改
修改 User-Agent 可以算是最簡(jiǎn)單的隱藏方法了,也是切實(shí)可行的,不過(guò)呢,如果是用 Python 抓取網(wǎng)頁(yè),例如批量下載 圖片,你一個(gè) IP 地址短時(shí)間內(nèi)連續(xù)的進(jìn)行訪問(wèn),那么這是不符合正常人類的標(biāo)準(zhǔn)的,而且對(duì)服務(wù)器帶來(lái)的壓力不小,所以服務(wù)器合情合理會(huì)把你屏蔽掉,屏蔽的做法也很簡(jiǎn)單,只需要記錄每個(gè)IP的訪問(wèn)頻率,在單位時(shí)間內(nèi),如果訪問(wèn)頻率超過(guò)一個(gè)閾值,那么服務(wù)器就會(huì)認(rèn)為這個(gè)IP很有可能是一個(gè)爬蟲(chóng),那么服務(wù)器就會(huì)把不管它的User-Agent是什么了,服務(wù)器就會(huì)返回一個(gè)驗(yàn)證碼的界面,因?yàn)橛脩魰?huì)填寫驗(yàn)證碼,但是爬蟲(chóng)不會(huì),這就會(huì)合理的把你的訪問(wèn)給屏蔽掉。
就我們目前的學(xué)習(xí)水平來(lái)說(shuō),有兩種做法可以解決這種問(wèn)題,一種就是延遲提交的時(shí)間,讓我們的爬蟲(chóng)看起來(lái)更像是一個(gè)正常的人類在瀏覽(這是沒(méi)有辦法的辦法);還有一種就是使用代理。
首先說(shuō)第一種方法,我們可以使用 time 模塊來(lái)完成延遲操作:(這種方法的工作效率太慢了)
-
#translation.py
-
import urllib.request
-
import urllib.parse
-
import json
-
import time
-
print("---------這是一個(gè)Python翻譯器---------")
-
while True:
-
content = input("請(qǐng)輸入需要翻譯的內(nèi)容(輸入'Q'退出程序):")
-
if content == 'Q':
-
break
-
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
-
#直接從審查元素中copy過(guò)來(lái)的url會(huì)報(bào)錯(cuò),必須把translate_o中的_o 刪除才可以
-
#url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
-
#head = {}
-
#head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
-
data = {} #這里就是把 Form Data 中的內(nèi)容貼過(guò)來(lái)
-
data['i'] = content
-
data['from'] = 'AUTO'
-
data['to'] = 'AUTO'
-
data['smartresult'] = 'dict'
-
data['client'] = 'fanyideskweb'
-
data['salt'] = '15445124815349'
-
data['sign'] = 'a824eba4c23c6f541ffadfee26b1e500'
-
data['ts'] = '1544512481534'
-
data['bv'] = 'bbb3ed55971873051bc2ff740579bb49'
-
data['doctype'] = 'json'
-
data['version'] = '2.1'
-
data['keyfrom'] = 'fanyi.web'
-
data['action'] = 'FY_BY_REALTIME'
-
data['typoResult'] = 'false'
-
#需要使用urllib.parse.urlencode() 把data轉(zhuǎn)換為需要的形式
-
data = urllib.parse.urlencode(data).encode('utf-8')
-
req = urllib.request.Request(url, data)
-
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36')
-
response = urllib.request.urlopen(url, data)
-
html = response.read().decode('utf-8')
-
target = json.loads(html)
-
print('翻譯結(jié)果:%s' %(target['translateResult'][0][0]['tgt']))
-
time.sleep(5) #暫停5秒鐘
第二個(gè)方案:使用代理
首先需要知道,代理是什么?
嘿,兄弟,哥們兒訪問(wèn)這個(gè)網(wǎng)址有點(diǎn)困難,幫忙解決一下唄!
然后你就把需要訪問(wèn)的網(wǎng)址告訴代理先生,代理幫你訪問(wèn),然后把他看到的所有內(nèi)容原封不動(dòng)的轉(zhuǎn)發(fā)給你。
這就是代理。
代理的工作原理就是這么簡(jiǎn)單,因此呢,服務(wù)器看到的IP地址就是代理的IP地址,而不是你的IP地址,這樣子你用很多個(gè)代理同時(shí)發(fā)起訪問(wèn),服務(wù)器也沒(méi)有辦法。使用代理的步驟如下:
- 1.參數(shù)是一個(gè)字典 {‘類型’ :‘代理ip: 端口號(hào)’}
? ? ? ? ?proxy_support = urllib.request.ProxyHandler({})
? ? ? ? ?它這個(gè)參數(shù)就是一個(gè)字典,字典的鍵就是代理的類型(如:http,https...),值就是對(duì)應(yīng)的 ip 或者 域名 +端口號(hào)
- 2.定制、創(chuàng)建 一個(gè) opener
(opener可以看做是私人訂制,當(dāng)你使用 urlopen 打開(kāi)一個(gè)普通網(wǎng)頁(yè)的時(shí)候,你就是在使用默認(rèn)的 opener 來(lái)工作,而這個(gè)opener 是可以有我們定制的,例如我們可以給它加上特殊的 headers,或者指定 代理,我們使用下面的語(yǔ)句定制、創(chuàng)建一個(gè)? ? opener)
opener = urllib.request.build_opener(proxy_support)
- 3a.安裝opener
? ? ?我們使用 urllib.request.install_opener(opener),把它安裝到系統(tǒng)中,這是一勞永逸的做法,因?yàn)樵诖酥?,你只要使用普通?urlopen() 函數(shù),就是使用定制好的 opener 進(jìn)行工作,如果你不想替換掉默認(rèn)的 opener,你可以使用下面的語(yǔ)句調(diào)用 opener。
- 3b.調(diào)用opener
? ? ? opener.open(url)
我們來(lái)舉個(gè)例子:
我們需要代理ip,直接在網(wǎng)上搜索即可。
-
import urllib.request
-
url = 'http:/www.whatismyip.com.tw' #查詢ip的網(wǎng)站
-
proxy_support = urllib.request.ProxyHandler({'http': '123.206.56.247:80'})
-
opener = urllib.request.build_opener(proxy_support)
-
urllib.request.install_opener(opener)
-
response = urllib.request.urlopen(url)
-
html = response.read().decode('utf-8')
-
print(html)
測(cè)試題
0. 服務(wù)器是如何識(shí)訪問(wèn)來(lái)自瀏覽器還是非瀏覽器的?
答:通過(guò)發(fā)送的 HTTP 頭中的 User-Agent 來(lái)進(jìn)行識(shí)別瀏覽器與非瀏覽器,服務(wù)器還以 User-Agent 來(lái)區(qū)分各個(gè)瀏覽器。
1. 明明代碼跟視頻中的栗子一樣,一運(yùn)行卻出錯(cuò)了,但在不修改代碼的情況下再次嘗試運(yùn)行卻又變好了,這是為什么呢?
答: 在網(wǎng)絡(luò)信息的傳輸中會(huì)出現(xiàn)偶然的“丟包”現(xiàn)象,有可能是你發(fā)送的請(qǐng)求服務(wù)器沒(méi)收到,也有可能是服務(wù)器響應(yīng)的信息不能完整送回來(lái)……尤其在網(wǎng)絡(luò)阻塞的時(shí)候。所以,在設(shè)計(jì)一個(gè)“稱職”的爬蟲(chóng)時(shí),需要考慮到這偶爾的“丟包”現(xiàn)象。
2. Request 是由客戶端發(fā)出還是由服務(wù)端發(fā)出?
答:我們之前說(shuō) HTTP 是基于“請(qǐng)求-響應(yīng)”模式,Request 即請(qǐng)求的意思,而 Response 則是響應(yīng)的意思。由客戶端首先發(fā)出 Request,服務(wù)器收到后返回 Response。
3. 請(qǐng)問(wèn)如何為一個(gè) Request 對(duì)象動(dòng)態(tài)的添加 headers?
答:add_header() 方法往 Request 對(duì)象添加 headers。
4. 簡(jiǎn)單來(lái)說(shuō),代理服務(wù)器是如何工作的?他有時(shí)為何不工作了?
答: 將信息傳給代理服務(wù)器,代理服務(wù)器替你向你要訪問(wèn)的服務(wù)器發(fā)送請(qǐng)求,然后在將服務(wù)器返回的內(nèi)容返回給你。
因?yàn)橛小皝G包”現(xiàn)象發(fā)生,所以多了一個(gè)中間人就意味著會(huì)多一層發(fā)生“丟包”的幾率,且大多數(shù)代理并不只為一個(gè)人服務(wù),尤其是免費(fèi)代理。
PS:大家想做“壞壞”的事情時(shí)可以考慮多幾層代理,一般來(lái)說(shuō)路由器日志并不會(huì)保存很長(zhǎng)時(shí)間,幾層代理后,基本很難查到是誰(shuí)請(qǐng)求的。
5. HTTP 有好幾種方法(GET,POST,PUT,HEAD,DELETE,OPTIONS,CONNECT),請(qǐng)問(wèn)你如何曉得 Python 是使用哪種方法訪問(wèn)服務(wù)器呢?
答:使用 get_method() 方法獲取 Request 對(duì)象具體使用哪種方法訪問(wèn)服務(wù)器。最常用的無(wú)非就是 GET 和 POST 了,當(dāng) Request 的 data 參數(shù)被賦值的時(shí)候,get_method() 返回 'POST',否則一般情況下返回 'GET'。
6. 上一節(jié)課后題中有涉及到登陸問(wèn)題,辣么,你還記得服務(wù)器是通過(guò)什么來(lái)確定你是登陸還是沒(méi)登陸的么?他會(huì)持續(xù)到什么時(shí)候呢?
答: 是 cookie,服務(wù)器通過(guò)判斷你提交的 cookie 來(lái)確定訪問(wèn)是否來(lái)自”熟人“。
簡(jiǎn)單來(lái)說(shuō) cookie 可以分成兩類:
- 一類是即時(shí)過(guò)期的 cookies,稱為“會(huì)話” cookies,當(dāng)瀏覽器關(guān)閉時(shí)(這里是 python 的請(qǐng)求程序)自動(dòng)清除
- 另一類是有期限的 cookies,由瀏覽器進(jìn)行存儲(chǔ),并在下一次請(qǐng)求該網(wǎng)站時(shí)自動(dòng)附帶(如果沒(méi)過(guò)期或清理的話)
動(dòng)動(dòng)手
小甲魚(yú)打算在這里先給大家介紹一個(gè)壓箱底的模塊 —— Beautiful Soup 4
翻譯過(guò)來(lái)名字有點(diǎn)詭異:漂亮的湯?美味的雞湯?呃……
好吧,只要你寫出一個(gè)普羅大眾都喜歡的模塊,你管它叫“Beautiful Shit”大家也是能接受的……
?
Beautiful Soup 是一個(gè)可以從 HTML 或 XML 文件中提取數(shù)據(jù)的 Python 庫(kù)。它能夠通過(guò)你喜歡的轉(zhuǎn)換器實(shí)現(xiàn)慣用的文檔導(dǎo)航,查找,修改文檔的方式。Beautiful Soup 會(huì)幫你節(jié)省數(shù)小時(shí)甚至數(shù)天的工作時(shí)間。
這玩意兒到底怎么用?
看這 ->?傳送門
上邊鏈接是官方的快速入門教程(不用懼怕,這次有中文版了),請(qǐng)大家花差不多半個(gè)小時(shí)的時(shí)間自學(xué)一下,然后完成下邊題目。
噢,對(duì)了,大家可以使用 pip 安裝(Python3.4 以上自帶的神一般的軟件包管理系統(tǒng),有了它 Python 的模塊安裝、卸載全部一鍵搞定?。?/p>
打開(kāi)命令行窗口(CMD) -> 輸入 pip install BeautifulSoup4 命令) -> 搞定。
0. 編寫一個(gè)爬蟲(chóng),爬百度百科“網(wǎng)絡(luò)爬蟲(chóng)”的詞條
(鏈接 ->?網(wǎng)絡(luò)爬蟲(chóng)_百度百科),將所有包含“view”的鏈接按下邊格式打印出來(lái):
提示:題目中需要使用到簡(jiǎn)單的正則表達(dá)式(在官方的快速入門教程有演示),如果你希望馬上就深入學(xué)習(xí)正則表達(dá)式,當(dāng)然可以給你預(yù)支一下后邊的知識(shí) ->?Python3 如何優(yōu)雅地使用正則表達(dá)式
代碼清單:
-
import urllib.request
-
import re
-
from bs4 import BeautifulSoup
-
def main():
-
url = "http://baike.baidu.com/view/284853.htm"
-
response = urllib.request.urlopen(url)
-
html = response.read()
-
soup = BeautifulSoup(html, "html.parser") # 使用 Python 默認(rèn)的解析器
-
for each in soup.find_all(href=re.compile("view")):
-
print(each.text, "->", ''.join(["http://baike.baidu.com", each["href"]]))
-
# 上邊用 join() 不用 + 直接拼接,是因?yàn)?join() 被證明執(zhí)行效率要高很多
-
if __name__ == "__main__":
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-601250.html -
main()
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-601250.html
1. 直接打印詞條名和鏈接不算什么真本事兒,這題要求你的爬蟲(chóng)允許用戶輸入搜索的關(guān)鍵詞。
然后爬蟲(chóng)進(jìn)入每一個(gè)詞條,然后檢測(cè)該詞條是否具有副標(biāo)題(比如搜索“豬八戒”,副標(biāo)題就是“(中國(guó)神話小說(shuō)《西游記》的角色)”),如果有,請(qǐng)將副標(biāo)題一并打印出來(lái):
程序?qū)崿F(xiàn)效果如下:
代碼清單:
-
import urllib.request
-
import urllib.parse
-
import re
-
from bs4 import BeautifulSoup
-
def main():
-
keyword = input("請(qǐng)輸入關(guān)鍵詞:")
-
keyword = urllib.parse.urlencode({"word":keyword})
-
response = urllib.request.urlopen("http://baike.baidu.com/search/word?%s" % keyword)
-
html = response.read()
-
soup = BeautifulSoup(html, "html.parser")
-
for each in soup.find_all(href=re.compile("view")):
-
content = ''.join([each.text])
-
url2 = ''.join(["http://baike.baidu.com", each["href"]])
-
response2 = urllib.request.urlopen(url2)
-
html2 = response2.read()
-
soup2 = BeautifulSoup(html2, "html.parser")
-
if soup2.h2:
-
content = ''.join([content, soup2.h2.text])
-
content = ''.join([content, " -> ", url2])
-
print(content)
-
if __name__ == "__main__":
-
main()
2. 嘩啦啦地丟一堆鏈接給用戶可不是什么好的體驗(yàn),我們應(yīng)該先打印 10 個(gè)鏈接,然后問(wèn)下用戶“您還往下看嗎?
來(lái),我給大家演示下:
然后為了增加用戶體驗(yàn),代碼需要捕獲未收錄的詞條,并提示:
提示:希望你還記得有生成器這么個(gè)東東
代碼清單:
-
import urllib.request
-
import urllib.parse
-
import re
-
from bs4 import BeautifulSoup
-
def test_url(soup):
-
result = soup.find(text=re.compile("百度百科尚未收錄詞條"))
-
if result:
-
print(result[0:-1]) # 百度這個(gè)碧池在最后加了個(gè)“符號(hào),給它去掉
-
return False
-
else:
-
return True
-
def summary(soup):
-
word = soup.h1.text
-
# 如果存在副標(biāo)題,一起打印
-
if soup.h2:
-
word += soup.h2.text
-
# 打印標(biāo)題
-
print(word)
-
# 打印簡(jiǎn)介
-
if soup.find(class_="lemma-summary"):
-
print(soup.find(class_="lemma-summary").text)
-
def get_urls(soup):
-
for each in soup.find_all(href=re.compile("view")):
-
content = ''.join([each.text])
-
url2 = ''.join(["http://baike.baidu.com", each["href"]])
-
response2 = urllib.request.urlopen(url2)
-
html2 = response2.read()
-
soup2 = BeautifulSoup(html2, "html.parser")
-
if soup2.h2:
-
content = ''.join([content, soup2.h2.text])
-
content = ''.join([content, " -> ", url2])
-
yield content
-
def main():
-
word = input("請(qǐng)輸入關(guān)鍵詞:")
-
keyword = urllib.parse.urlencode({"word":word})
-
response = urllib.request.urlopen("http://baike.baidu.com/search/word?%s" % keyword)
-
html = response.read()
-
soup = BeautifulSoup(html, "html.parser")
-
if test_url(soup):
-
summary(soup)
-
print("下邊打印相關(guān)鏈接:")
-
each = get_urls(soup)
-
while True:
-
try:
-
for i in range(10):
-
print(next(each))
-
except StopIteration:
-
break
-
command = input("輸入任意字符將繼續(xù)打印,q退出程序:")
-
if command == 'q':
-
break
-
else:
-
continue
-
if __name__ == "__main__":
-
main()
到了這里,關(guān)于《零基礎(chǔ)入門學(xué)習(xí)Python》第055講:論一只爬蟲(chóng)的自我修養(yǎng)3:隱藏的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!