一.什么是爬蟲
爬蟲的概念:
通過編寫程序,模擬瀏覽器上網,讓其去互聯網上爬取數據的過程.
爬蟲的工作流程:
模擬瀏覽器發送請求->下載網頁代碼->只提取有用的數據->存放於數據庫或文件中

爬蟲的分類:
- 通用爬蟲:爬取全部的頁面數據.
- 聚焦爬蟲: 抓取頁面中局部的頁面數據
- 增量式爬蟲:爬取網站中更新出的數據
反爬機制
門戶網站會通過制定相關的技術手段阻止爬蟲程序進行數據的爬取
反反爬策略:
- robots.txt協議: 防君子不防小人的協議
- UA檢測 ----->用戶表示(通過什么樣的代理發起的請求)
- cookie ------>訪問記錄
- 驗證碼 ------>打碼平台
- 動態加載數 ---->捕獲ajax包
- reference ---->跳轉過來的地址
二.requests模塊
1.requests請求過程涉及的協議
#1、請求方式: 常用的請求方式:
GET:請求數據
POST:提交數據
PUT:更新數據
DELETE:刪除數據 post與get請求最終都會拼接成這種形式:k1=xxx&k2=yyy&k3=zzz
post請求的參數放在請求體內: 可用瀏覽器查看,存放於form data內 get請求的參數直接放在url后 #2、請求url url全稱統一資源定位符,如一個網頁文檔,一張圖片 一個視頻等都可以用url唯一來確定 url編碼 https://www.baidu.com/s?wd=圖片 圖片會被編碼(看示例代碼) 網頁的加載過程是: 加載一個網頁,通常都是先加載document文檔, 在解析document文檔的時候,遇到鏈接,則針對超鏈接發起下載圖片的請求
#3、請求協議格式:
請求首行:
協議格式:HTTP/HTTPS
請求方式:GET/POST
請求主機:
請求頭:
空行:
請求體:
#4、請求頭
User-agent:告訴它這是瀏覽器發過來的請求(請求頭中如果沒有user-agent客戶端配置,服務端可能將你當做一個非法用戶)務必加上
host: 請求主機(服務器)
cookies:cookie用來保存登錄信息
Referer:由哪一個網址跳轉過來的,如果直接訪問是這個值是空的(用來做防盜鏈和廣告的作用的判斷) 一般做爬蟲都會加上請求頭
content-type:content-type:"urlencoded"/jason--->告訴服務器我提交數據的類型(psot請求才有的)
#5、請求體
如果是get方式,請求體沒有內容
如果是post方式,請求體是format data
ps:
1、登錄窗口,文件上傳等,信息都會被附加到請求體內
2、登錄,輸入錯誤的用戶名密碼,然后提交,就可以看到post,正確登錄后頁面通常會跳轉,無法捕捉到post
關於contentype詳解
瀏覽器------------------>服務器 1 針對post請求(post請求才有請求體) 2 向服務器發送post請求有哪些形式: form表單 (urlencoded編碼格式) user=yuan pwd=123 Ajax(urlencoded編碼格式) a=1 b=2 請求協議格式字符串 發送urlencoded編碼格式數據 ''' 請求首行 請求頭 content-type:"urlencoded" 空行 請求體 # user=yuan&pwd=123 urlencoded編碼格式 ''' 發送json數據 ''' 請求首行 請求頭 content-type:"json" 空行 請求體 # {"user":"yuan","pwd":123} json編碼格式 '''
2.request爬取數據的流程
requests使用流程:
-pip install requests
- 指定url
- 發起請求
- 獲取響應回來的頁面數據
- 持久化存儲
import requests
#爬取搜狗首頁的頁面數據
#1.指定url
url = 'https://www.sogo.com/'
#2.發起請求:get方法會返回一個響應對象
response = requests.get(url=url)
#3.獲取頁面數據
page_text = response.text
#4.持久化存儲
with open('sogou.html','w',encoding='utf-8') as fp:
fp.write(page_text)
3.帶User-Agent的請求
- User-Agent:請求載體的身份標識
- 反爬機制:瀏覽器會請求頭的UA來檢測是用戶請求還是機器請求
- 反反爬策略:獲取UA作為request的參數傳進去,模擬一個正常的用戶(瀏覽器)
例子:
url = 'https://www.qiushibaike.com/text/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
#手動設定請求的請求頭信息
response = requests.get(url=url,headers=headers)
print(response.text)
拿UA:
4.帶參數的request
某些網站會帶着我們輸入的參數進行請求:
看一個實例:
import requests
kw=input('請輸入一個詞語')
params={
'wd':kw
}
url='https://www.baidu.com/s'
headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
response=requests.get(url=url,params=params,headers=headers)
page_content=response.content #響應體的content是二進制的內容,text是文本內容
filename=kw+'.html'
with open(filename,'wb') as f:
f.write(response.content)
5.帶coockies的請求
import uuid import requests url = 'http://httpbin.org/cookies' #這個網址可以做測試使用 cookies = dict(sbid=str(uuid.uuid4())) res = requests.get(url, cookies=cookies) print(res.text)
6.帶session對象的請求(比cookies好用)
#實例化一個是session對象,session對象包含着cookies session=request.session()
#session對象可以直接發起請求的 session.get() session.post()
7.request的post請求
1. post請求和get請求大致相同:post(url,headers,data/jason)就是參數的名字不一樣而已,get請求參數是在params字典里面,
post請求參數在data或者jsaon里面(psot請求也可以有params參數,用來做跳轉?next=)
形式: requests.post(url="/login/",headers={},cookies={},params={"next":"index"},data={},json={})
2.來看一個例子,爬取百度翻譯的結果
import requests
import json
url='https://fanyi.baidu.com/sug'
headers={
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'
}
kw=input('請輸入一個詞')
data={
'kw':kw
}
response=requests.post(url=url,headers=headers,data=data)
print(response.json())
3,看一下jason和contentype的區別
import requests
res1=requests.post(url='http://httpbin.org/post', data={'name':'yuan'}) #沒有指定請求頭,#默認的請求頭:application/x-www-form-urlencoed
print(res1.json())
res2=requests.post(url='http://httpbin.org/post',json={'age':"22",}) #默認的請求頭:application/json
print(res2.json())
{'args': {}, 'data': '', 'files': {}, 'form': {'name': 'yuan'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Content-Length': '9', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.21.0'}, 'json': None, 'origin': '121.35.181.66, 121.35.181.66', 'url': 'https://httpbin.org/post'}
{'args': {}, 'data': '{"age": "22"}', 'files': {}, 'form': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Content-Length': '13', 'Content-Type': 'application/json', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.21.0'}, 'json': {'age': '22'}, 'origin': '121.35.181.66, 121.35.181.66', 'url': 'https://httpbin.org/post'}
8.request值ajax請求
import requests #url和參數,直接用抓包工具去請求頭和Query String ParamMetres抓取 url='https://movie.douban.com/j/chart/top_list' #因為是ajax發起的get請求,所以數據用params params={ 'type':'24', 'interval_id':'100:90', 'action':'', 'start': '20', 'limit':'20' } headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36' } #從response看到Content-Type: application/json; charset=utf-8,說明是支持中文顯示的 jason_text=requests.get(url=url,headers=headers,params=params).text print(jason_text)
9.request的ajax的post請求
import requests kw=input('請輸入一個城市') #這邊要記住,這是post請求,post請求不能不會把參數直接拼接在路徑后面,所有網址拷過來就不要動了 url='http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword' headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36' } # post請求用的是data,記得啊 data={ 'cname':'', 'pid': '', 'keyword': kw, 'pageIndex': '1', 'pageSize': '10' } #抓包工具能看到他是text類型,utf-8吧編碼的,支持中文顯示 page_text=requests.post(url=url,headers=headers,data=data).text print(page_text)
10.基於ajax的動態加載網址
import requests url='http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList' headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36' } #做循環作用的拿到所有頁碼的 for i in range(100): data={ 'on': 'true', 'page': '1', 'pageSize': '15', 'productName':'', 'conditionType': i, 'applyname': '', 'applysn': '' } # #json()方法:返回是一個序列化號的json對象 jason_text=requests.post(url=url,data=data,headers=headers).json() jason_list=jason_text['list'] #拿到所有企業的id(ajax發出的post請求是根據企業id返回結果的) id_list=[] for dic in jason_list: id_list.append(dic['ID']) #發出第二次請求,帶上企業id就能拿到具體的值了 url1='http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById' for id in id_list: data1={ 'id': id } json_text=requests.post(url=url1,data=data1,headers=headers).text print(json_text)
11.帶有代理的請求
import requests
res=requests.get('http://httpbin.org/ip', proxies={'http':'111.177.177.87:9999'}).json()
三.reponse的屬性
1.常見屬性
import requests response=requests.get('https://www.jd.com/') print(response.status_code) print(response.text) #文本內容 print(response.content) #二進制內容 print(response.headers) #響應頭 print((response.cookies)) #響應的cookie print(response.cookies.get_dict()) print(response.cookies.items()) print(response.url) #響應的url print(response.history) #如果有重定向重定向之前相應的數據 print(response.encoding) #響應體的編碼
2.編碼問題
import requests response=requests.get('http://www.autohome.com/news') response.encoding='gbk' #汽車之家網站返回的頁面內容為gb2312編碼的,而requests的默認編碼為ISO-8859-1,如果不設置成gbk則中文亂碼 with open("res.html","w") as f: #解碼過程是在拿text過程中執行的 f.write(response.text)
3.下載二進制內容
import requests response=requests.get('http://bangimg1.dahe.cn/forum/201612/10/200447p36yk96im76vatyk.jpg') with open("res.png","wb") as f: # f.write(response.content) # 比如下載視頻時,如果視頻100G,用response.content然后一下子寫到文件中是不合理的 for line in response.iter_content(): #用迭代器可以一段一段,不知於把自己的硬盤搞垮了 f.write(line)
4,解析jason數據
import requests import json response=requests.get('http://httpbin.org/get') res1=json.loads(response.text) #太麻煩 res2=response.json() #直接獲取json數據,和上面的同理 print(res1==res2)
5. Redirection and History
默認情況下,除了 HEAD, Requests 會自動處理所有重定向。可以使用響應對象的 history 方法來追蹤重定向。Response.history 是一個 Response 對象的列表,
為了完成請求而創建了這些對象。這個對象列表按照從最老到最近的請求進行排序。
>>> r = requests.get('http://github.com') >>> r.url 'https://github.com/' >>> r.status_code 200 >>> r.history [<Response [301]>]
另外,還可以通過 allow_redirects 參數禁用重定向處理:
>>> r = requests.get('http://github.com', allow_redirects=False) >>> r.status_code 301 >>> r.history []
6.相應狀態碼
response.status_code
#正常情況下我們是通過判斷相應的狀態碼是不是200 來判斷是否請求成功
我們也可可以通過相應對象的方法來直接捕獲狀態碼不是200的響應
1.相應狀態碼不是200的
import requests bad_r = requests.get('http://httpbin.org/status/404') bad_r.status_code #404 bad_r.raise_for_status() #當響應的狀態碼不是200的時候調用這個接口,會拋出異常,哦我們呢捕獲這個異常就好了 Traceback (most recent call last): File "<input>", line 1, in <module> File "D:\python37\lib\site-packages\requests\models.py", line 941, in raise_for_status raise HTTPError(http_error_msg, response=self) requests.exceptions.HTTPError: 404 Client Error: NOT FOUND for url: http://httpbin.org/status/404
2.狀態碼是200
import requests res=requests.get('https://www.baidu.com/') res.status_code #200 res.raise_for_status() #當響應的狀態碼是200的時候,調用這個接口的返回值的None print(res.raise_for_status()) None
3.判斷響應狀態碼中和請求的狀態碼中的ok(200)是不是相等的
importrequest res=requests.get('https://www.baidu.com/') res.status_code #200 res.status_code==requests.codes.ok True
所以通用的請求狀態碼判斷這塊可以這么寫:
import requests try: res=requests.get('https://www.baidu.com/') res.raise_for_status() except requests.HTTPError as error: print('status_error') except: print('other error')