網絡參數
1.header下
(1)user-agent
:這個是爬蟲header里可以說必須的東西粥喜,基本網站肯定第一個都是判斷這個
(2)referer
:代表你是從哪來訪問到這個界面的唱捣,也是比較經常拿來判斷是否為爬蟲的參數
(3)accept-encoding
:代表用什么進行數據壓縮箕肃,有時候如果壓縮了數據會變化笋额,甚至亂碼淋纲,這時候可能header里面刪掉這個比較好
(4)cookie
:這個也是非常重要的內容啄巧,但經常都是有時間限制
2.data
下
(1)salt
:js加密
一般都是在服務器對數據加密后傳輸,而加密的內容可能原來就幾位數胸囱,如果準備了一個字典進行暴力破解有時候也不是難事祷舀,為了應對這種情況,有時候就會用salt旺矾,也就是一大串很長的字符串蔑鹦,和加密結合,從而做到一定程度的反爬蟲
(2)sign
:簽名箕宙,也是一種安全認證機制嚎朽,和salt一樣一般加密方法都寫在js里,此時去js找到后翻譯成python代碼模擬生成其內容柬帕,js代碼不懂什么意思可以在瀏覽器中按f12
進入console
中運行
接入網絡
導入urllib
包哟忍,里面包括4個模塊——request
、error
陷寝、parse
锅很、robotparser
,連接要用的就是request
模塊
urllib下模塊詳解
1.request
負責一些網絡連接的請求
(1)urlopen(url, data=none, [timeout,])
用此函數連接凤跑,第一個參數是網址
爆安,第二個參數默認none
,說明GET
方法仔引,如果賦值了就是以input
的方式提交扔仓,舉例:
import urllib.request
response = urllib.request.urlopen("http://www.whatismyip.com.tw")
#連接網頁,并保存到response咖耘,該網頁代碼少翘簇,不會卡...
html = response.read() #讀取頁面內容
html = html.decode("utf-8") #將編碼改成utf-8
print(html) #輸出內容,即網頁源代碼
(2)geturl()
返回你連接的url
(3)info()
返回遠程服務器的header
信息
(4)getcode()
返回http狀態(tài)
信息儿倒,比如200
說明沒問題版保,404
是not found等等
(5)urlretrieve(url, filename=None, reporthook=None, data=None)
直接將遠程數據下載到本地,url
表示你要下載的東西地址(這個地址打開得是一個文件夫否,比如圖片彻犁、壓縮包之類的,如果只是個頁面就下載不了)凰慈,finename
表示你要給下載的文件取啥名字汞幢,reporthook
是一個回調函數,可以顯示當前的下載進度溉瓶,data
是post
到服務器的數據急鳄,該方法返回一個包含兩個元素的(filename,headers)
元組,filename
表示保存到本地的路徑堰酿,header
表示服務器的響應頭疾宏,舉例:
for each in jpg:
filename = each.split('/')[-1]
#這個each是一個類似:https://imgsa.baidu.com/forum/w%3D580/sign=xxx.jpg的圖片網址
#filename就是從最后一個'/'開始到后面的文件名
urllib.request.urlretrieve(each, filename, None) #下載該圖片并取名
2.parse
對數據的處理
(1)urlencode()
對提交的數據進行轉碼触创,舉例:
data = urllib.parse.urlencode(data).encode("utf-8") #encode就是unicode文件轉成utf-8編碼形式
response = urllib.request.urlopen(url, data) #把utf-8解碼成unicode形式
注:
encode
函數是將一個unicode
類按照指定的編碼(如果不指定則使用defaultencoding
)轉換為不帶編碼標記的str
類坎藐,即byte
字節(jié)流;decode
函數是將一個str
類按照指定編碼(如果不指定則使用defaultencoding
)轉換為使用utf-8
編碼的unicode
類
3.chardet
自動檢測編碼哼绑,舉例:
import chardet
response = urllib.request.urlopen(url)
html = response.read()
cs = chardet.detect(html)
print(cs) #輸出一個字典岩馍,里面記錄了該網頁的編碼、語言等(但也可能會有錯)
html = html.decode(cs.get("encoding", 'utf-8"))
#將頁面按chardet解析的編碼解碼抖韩,如果不行就用第二個參數utf-8解碼
print(html) #解碼后的頁面
實戰(zhàn)
1.隱藏
網頁有時會根據User-Agent
來判斷是否為正常用戶訪問蛀恩,從而將非用戶訪問屏蔽,所以使用爬蟲時要仿照瀏覽器信息來隱藏自己的爬蟲身份茂浮,具體做法:
(1)Request
對象生成前新建一個head
參數(需為字典)双谆,里面放入User-Agent
信息傳入(referer
有時也挺需要的,當然參數能盡量都傳會更真實點)席揽,并且request要采用request.Request(url, data, head)
方法顽馋,舉例:
head = {}
head['User-Agent'] = '瀏覽器身份'
req = urllib.request.Request(url, data, head) #連接前添加head
response = urllib.request.urlopen(req)
(2)request對象生成后通過Request.add_header('key','val')
方法修改,舉例:
req = urllib.request.Request(url, data) #連接前不用head
req.add_header('User-Agent','瀏覽器身份') #添加head
response = urllib.request.urlopen(req)
2.爬取真人化
當使用爬蟲時無時無刻都是爬取數據幌羞,這種非常人的行為會引起懷疑寸谜,所以需要使用設置閾值來進行周期控制或者設置代理
(1)設置閾值
引入time
模塊,使用sleep()
方法控制單次時間属桦,舉例:
time.sleep(2) #延遲2秒后運行
(2)代理
利用多個代理同時爬刃艹铡(比如去:http://cn-proxy.com/
找,需要翻墻),步驟:第一步設置一個字典包含代理服務器,格式:{'協議':'ip:port'}
移国,然后將字典放入ProxyHandler()
方法內衙解;第二步用build_opener()
方法定制、創(chuàng)建一個opener
梁棠,并將代理放入;第三步用install_opener()
方法安裝opener
,當然前面這個方法是全程只使用這個opener惜论,如果想只在需要時使用可以用opener.open()
方法,并把代理鏈接放入止喷,舉例:
proxy_support=urllib.request.ProxyHandler({'http':'39.134.10.11:8080'})
opener = urllib.request.build_opener(proxy_support)
#print(proxy_support.proxies) #想看自己用的代理ip時用這個可以看
opener.addheaders = [('User-Agent','Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36')]
#加入header馆类,可選
urllib.request.install_opener(opener)
注:
可以同時設置多個代理,設置一個列表弹谁,然后將列表放入ProxyHandler()
方法里乾巧,舉例:
iplist=['221.130.253.135:8090','39.134.10.11:8080','112.21.164.58:1080'] #代理ip列表
proxy_support=urllib.request.ProxyHandler({'http':random.choice(iplist)}) #隨機選取一個ip
3.不在html上的內容
當要爬取的數據不在源代碼上句喜,說明可能是以json
數據傳輸,所以如果要截獲它沟于,首先在瀏覽器下按f12
咳胃,選擇network
下,刷新后會發(fā)現是很多文件加載組成的這個網頁旷太,我們需要的數據都在這里面展懈,比如網易云評論。那么多文件一個個找顯然不合理供璧,所以可以通過調慢網速然后等一個個文件加載存崖,當有我們想要的時候就暫停,然后找到這個文件睡毒,后面就好做了来惧,這里步驟就是:
(1)勾上Disable cache
,網速調慢比如regular 2G
(在Disable cache右邊應該)演顾,然后左上邊有個紅色的按鈕記住
(2)刷新網頁违寞,當我們想要的東西出現時停止刷新(把刷新的×
點了),然后也點前面那個紅色的按鈕偶房,剩下的東西也就會停止加載了趁曼,這個時候基本要的東西也出來了,就可以找了
(3)在XHR
和Doc
文件類型中找找棕洋,一般數據都傳這里面(看preview
挡闰,response
也就是整理好的樣式),找到以后就可以看它請求的url了掰盘,然后的一些data
數據啥的拷下來摄悯,接下來看他的post
還是get
方法,然后傳相應數據就可以了
(4)如果數據保存在json當中愧捕,可以通過導入json模塊奢驯,并使用json.loads(res.text)
方法來讀取,此時內容會被保存為字典形式次绘,提取時就可以按dict[]
方式了
異常處理
需要先導入urllib.error
Http返回狀態(tài)碼
100——299 代表成功
400——499 代表客戶端問題導致的連接失敗
500——599 代表服務端出問題導致連接失敗
更多可以導入requests
瘪阁,然后輸入requests.codes
,然后看源代碼邮偎。
異常寫法一
try:
response = urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
print(e.code) #HTTP輸出code
except urllib.error.URLError as e: #記住這種寫法要把HTTP的寫在URL的前面
print(e.reason) #URL輸出reason
else:
#normal situlation
異常寫法二(推薦)
try:
response = urllib.request.urlopen(req)
except urllib.error.URLError as e:
if hasattr(e, 'reason'):
print(e.reason)
elif hasattr(e, 'code'):
print(e.code)
else:
#normal situlation
更好的第三方庫
python里有更好的模塊用于爬蟲管跺,忘了上面自帶的,通過:pip install
安裝requests
模塊和bs4
模塊禾进,這兩個模塊的使用很簡單豁跑,最主要有兩個方法:requests.get()
和bs4.BeautifulSoup()
,第一個負責連接網頁并獲取內容泻云,第二個方法能把內容轉碼并保存在容器中
1.requests模塊
(1)get(url[,param=payload,headers=headers...])
get
方法連接網頁艇拍,可選是否傳參和引入報頭狐蜕,如果要傳參或引入報頭,傳入內容需為字典形式卸夕,當證書有問題時馏鹤,比如12306不是使用ssl
協議,所以用https登錄會提示不可信的鏈接娇哆,即報錯,這時候在get參數里加上一個:verify=False
就可以了勃救,舉例:
requests.get(url, headers=header, verify=False)
不過這時候雖然能連接碍讨,但可能還是會有警告,所以在連接前下面這句捕捉警告就可以了:
logging.captureWarnings(True)
注:
get方法里可以傳參數方法:
①requests.get('url?key1=value1&key2=value2&...')
②requests.get(url, params={'key1':'value1', 'key2':'value2', ...})
(2)post(url[,data=data,headers=headers...])
post
方法連接網頁蒙秒,使用方法參考get
注:
requests連接返回的是一個response
類型勃黍,直接打印是連接狀態(tài)碼(成功為200
),其內容(即response.text
)為str
類型晕讲,上面兩個連接方法可以傳入的參數很多覆获,不只是參數、報頭瓢省,還有:
代理 proxies=proxies弄息,其中proxies格式:{"http":"ip:port"}
cookies cookies=cookies,可以登錄后把cookie信息放到header里然后傳進去
請求時間 timeout=幾秒
json數據 json=payload
文件 files=files
data
(3)encoding
查看獲取文本的編碼勤婚,舉例:
res = requests.get(url)
res.encoding
#結果輸出編碼格式摹量,如果要修改輸出編碼格式就給這個賦值,例如:res.encoding = 'gb2312'
(4)text
查看獲取的頁面內容馒胆,一般是源代碼之類的文本信息
(5)content
也是獲取的頁面內容缨称,但這個返回的是字節(jié)流,如果是要下載圖片祝迂、視頻或者網頁(整個網頁而不只是源代碼)之類的就用這個睦尽,比如下載一張網上的圖片:
with open('a.jpg','wb') as f:
f.write(res.content)
(6)status_code
返回訪問頁面的狀態(tài)碼(一般200是訪問成功的狀態(tài)碼),我們可以用這個來判斷頁面是否訪問成功(raise_for_status()
拋出失敗請求異常)型雳,舉例:
if res.status_code == 200:
print("success")
(7)headers
以字典形式返回報頭当凡,里面有:Content-Type
、server
纠俭、connection
等很多內容宁玫,比如我只想知道是啥內容類型可以:res.headers['Content-Type']
或者res.headers.get('Content-Type')
(8)cookies
返回cookies
(9)history
可以返回是否內容被重定向,比如原來是http
變成https
(10)url
可以返回你訪問的網頁的網址柑晒,比如你訪問"http://www.baidu.com"欧瘪,但是肯定會變成"https://www.baidu.com",所以這時候res.url
就會返回后一個網址
注:
(3)——(10)都是在連接后的對象里進行的操作
(11)Session()
會話維持匙赞,可以模擬登陸佛掖,然后就共享這一個會話妖碉,像如果兩次訪問一個界面,會話是不同的芥被,所以cookies
也是不一樣欧宜,比如:
data = {'name':'aaa','password':'111'}
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}
r = requests.post("http://localhost:8080/BookShop/Servlet",data=data, headers=headers) #訪問1
s = requests.get('http://localhost:8080/BookShop/Servlet') #訪問2,get沒有cookie
print(r.cookies) #因為2次訪問會話不同拴魄,可以發(fā)現cookies也不一樣
print(s.cookies)
結果:
<RequestsCookieJar[<Cookie JSESSIONID=2E70361488F66F167EB148A6823C396C for localhost.local/BookShop>]>
<RequestsCookieJar[]>
而用Session()以后冗茸,這幾個訪問都在同一個會話當中,cookies也是相同的匹中,舉例:
import requests
s = requests.Session() #創(chuàng)建一個會話
data = {'name':'aaa','password':'111'}
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}
s.get('http://localhost:8080/BookShop/Servlet') #先訪問這個頁面獲得會話夏漱,但并沒有cookie
r = s.post("http://localhost:8080/BookShop/Servlet",data=data, headers=headers) #這次訪問還是跟上次一樣的會話
print(r.cookies) #會發(fā)現兩個都有cookies,且值相同
print(s.cookies)
結果為:
<RequestsCookieJar[<Cookie JSESSIONID=101406D2016C041539F2BD40CE47696E for localhost.local/BookShop>]>
<RequestsCookieJar[<Cookie JSESSIONID=101406D2016C041539F2BD40CE47696E for localhost.local/BookShop>]>
(12)json()
如果響應的結果是個json
格式的內容顶捷,可以直接用requests.json()
保存下來挂绰,比如:
res = requests.get('http://www.httpbin.org/get') #該頁面訪問結果是一個json數據
print(res.text)
print(res.json()) #以json格式保存
(13)codes
輸入requests.codes
,查看源代碼可以看到所有狀態(tài)碼對應的意義服赎,并且從其集合里也可以找到對應的代替這個狀態(tài)碼的字符葵蒂,比如200
的集合里有ok
,所以可以用requests.codes.ok
代替200重虑,更多可以看源代碼里寫的(比如requests.codes.not_found
==404)践付,這里挖一部分源代碼:
# Informational.
100: ('continue',)
200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '?')
# Redirection.
302: ('found',)
305: ('use_proxy',)
306: ('switch_proxy',)
# Client Error.
400: ('bad_request', 'bad')
403: ('forbidden',)
404: ('not_found', '-o-')
# Server Error.
500: ('internal_server_error', 'server_error', '/o\\', '?')
502: ('bad_gateway',)
504: ('gateway_timeout',)
異常
requests下異常導入:from requests.exceptions import RequestException
,然后就可以直接用了:
try:
res = request.get(url)
…
except RequestException:
…
更多參考
https://blog.csdn.net/imail2016/article/details/72381989
官方中文文檔
http://cn.python-requests.org/zh_CN/latest/
2. BeautifulSoup4模塊
(導入時import bs4
)
(1)BeautifulSoup(獲取的內容缺厉,解析器)
比如用res = requests.get()
獲取頁面后可以通過這個方法獲取內容荔仁,如果要源代碼文本,就第一個參數是:res.text
芽死,要流文件就:res.content
乏梁,參考上面requests模塊可以獲取的內容,第二個解析器默認的話用html.parser
关贵,用lxml
也可以遇骑,如果是xml
語言,則解析器用xml揖曾,但后面兩個解析器需要安裝C語言庫落萎,使用舉例:
soup = bs4.BeautifulSoup(res.text, 'html.parser')
(2)soup.target
可以直接通過:soup.標簽
來獲取內容,比如:print(soup.title)
就可以知道title
標簽里的內容了(不過標簽也會附上炭剪,如果只要內容练链,就在后面再加.string
),但比如有很多個p
標簽奴拦,那么:soup.p
就只返回第一個p標簽的內容媒鼓,我們還可以獲取標簽的屬性值,比如:print(soup.img['name'])
,但要注意的是因為只能獲取第一個標簽的信息绿鸣,所以第一個標簽如果沒有這個屬性就會報錯疚沐,比如上面這個img
的,如果第二個img有name
屬性潮模,第一個沒有亮蛔,那么就會報錯,為了獲得的第一個標簽是我們想要的擎厢,我們可以嵌套著獲取標簽究流,比如:
print(soup.head.title.string)
還有想要知道子標簽可以用:content
、children
动遭,比如要知道p標簽下所有標簽:soup.p.content
芬探;要知道父標簽就parent
,如果要知道所有祖先標簽沽损,即父標簽、父父標簽循头、所有他上級的標簽的話用parents
绵估;要知道兄弟標簽則next_siblings
(3)soup.prettify()
格式化代碼,輸出時會好看些
(4)find_all(['標簽名',屬性='值'])
可以從這源代碼當中獲取標簽卡骂,比如獲取所有的<a href=''></a>
即<a>標簽可以:
targets = soup.find_all('a')
如果還想獲得的a標簽的class
屬性為abc国裳,則:
targets = soup.find_all('a',class_='abc')
class在python中作為類的關鍵字,所以要查找網頁中的class標簽時應該在后面加下劃線全跨,即class_
缝左,也可以不根據標簽而是滿足某個屬性:
targets = soup.find_all(class_="BDE_Smiley")
并且該方法還可以以集合或者字典形式傳入多個參數,比如獲取多個標簽:
targets = soup.find_all({"img", "title", "head"})
獲取多種條件的一個標簽:
targets = soup.find_all("img", {"class":{"BDE_Image","BDE_Smiley"}})
還有就是必須用集合形式來傳參的情況浓若,比如有些有標簽屬性帶-
渺杉,而此時就會報錯(只允許有_
和字母數字),舉例:
ip_s = soup.find_all('td', attrs={'data-title':'IP'})
#data-title帶-挪钓,不能寫成data-title='IP'
注:
find_all
返回的是個結果集是越,所以targets
類型是結果集,targets下內容是標簽(比如for each in targets
中的each是標簽)碌上,標簽下的text
之類的屬性才是str
倚评;而結果集下沒有text屬性,標簽下才有
注2:
返回的結果集下假如有多個<a>標簽馏予,那么索引(targets.a)只能返回第一個<a>標簽的結果天梧,此時如果想要所有的<a>標簽,可以在下面繼續(xù)用find_all()
嵌套索引霞丧,舉例:targets.find_all('a')
(5)獲取內容
接著(2)呢岗,當取得targets
后因為內容是一大堆<a>
標簽和對應內容,如果我們想要<a>的內容的話就:
for each in targets:
print(each.text)
如果想要某個屬性,比如src
的話就可以:
for each in targets:
print(each.get("src"))
(6)select('查找內容')
①如果是查找標簽敷燎,直接標簽名
就行暂筝,例如:soup.select('title')
②如果是查找id
則前面加#
,即soup.select('#id名')
③如果查找class
則前面加.
硬贯,例如:soup.select('.List-header')
④如果要獲取標簽某個屬性值則到標簽下加['屬性名']
就好了焕襟,例如:soup.select('a')[0]['href']
,則獲取第一個a標簽下的href
⑤如果要層層迭代饭豹,就空格
隔開鸵赖,比如要獲得id為abc下被div修飾的p標簽:soup.select('#abc div p')
注:
最終返回的是個列表,列表下是標簽拄衰,標簽下是str
綜合簡單示例
import requests
import bs4
url = "https://movie.douban.com/top250"
header = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}
res = requests.get(url, headers=header) #引入報頭它褪,如果get方法請求參數:params=payload
soup = bs4.BeautifulSoup(res.text, "html.parser")
#將res連接得到的內容轉文本形式給soup,解析器用默認的parser
targets = soup.find_all("div", class_="hd")
#找到想定位的地方翘悉,這里是一個div標簽茫打,class屬性值是hd
#因為class在python中是類,所以非類的class用class_代替
for each in targets:
print(each.a.span.text) #輸出div標簽下的a標簽的span標簽里的文本內容
3.pyquery
其和bs4
功能差不多妖混,同作為CSS
解析器老赤,而且其語法幾乎和jquery
相同,所以有時候更為方便
4.requests_html
是requests模塊的作者又開發(fā)出來的一個新模塊制市,相當于requests
和bs4
的結合抬旺,并且還多了JS編譯等功能,詳細可以參考:
https://segmentfault.com/a/1190000015641160