第三章.requests 模塊
3.1基本概念
-
什么是requests模塊?
-
一種基於網絡請求的模塊,作用就是用來模擬瀏覽器發起請求
-
-
為什么要使用requests模塊?
-
因為在使用urllib模塊的時候,會有諸多不便之處,總結如下
-
手動處理url編碼
-
手動處理post請求參數
-
處理cookie和代理操作繁瑣.......
-
-
-
如何使用requests模塊
-
安裝:
-
pip install requests
-
-
使用流程
-
指定url
-
基於requests模塊發起請求
-
獲取響應對象中的數據值
-
持久化存儲
-
-
-
什么是動態加載的數據?
-
由另一個額外的請求請求到的數據
-
-
如何判斷一個頁面中的是否存在動態加載的數據?
-
抓包工具進行局部搜索
-
-
如果判定出頁面中有動態加載的數據,如何進行數據的定位?
-
使用抓包工具進行全局搜索
-
-
對一個陌生的網站數據進行爬取前一定要判定你爬取到的數據是否為動態加載的!!!
3.2代碼展示
-
需求一 :爬取xx首頁對應的源碼數據
import requests
#1.指定地址
url="https://www.sogou.com"
#返回值是一個響應對象
response = requests.get(url=url)
#text返回的是字符串形式的相應數據
page_text = response.text
#持久化存儲
with open("./sougou.html","w",encoding="utf-8") as f:
f.write(page_text) -
需求二 :基於xx編寫一個簡單的頁面采集器
import requests
#想要將url攜帶的參數設定為動態參數
wd = input("enter a key:")
#url中?可加可不加
url = "https://www.sogou.com/web?"
#存儲的就是動態請求參數
params = {
"query":wd
}
#一定需要將params作用到請求中
#params參數是對請求url參數的封裝
response = requests.get(url=url,params=params)
#text返回的是字符串形式的相應數據
page_text = response.text
filename = wd + ".html"
#持久化存儲
with open(filename,"w",encoding="utf-8") as f1:
f1.write(page_text)
print(wd,'下載成功')

#上述程序出現問題:
問題一:出現了中文亂碼
問題二:遇到了UA檢測反爬機制
#請求載體身份標識的偽裝:
User-Agent:請求載體身份標識,通過瀏覽器發起的請求,請求載體為瀏覽器,則該請求的User-Agent為瀏覽器的身份標識,使用爬蟲程序發起的請求,則該請求的載體為爬蟲程序,則該請求的User-Agent為爬蟲程序的身份標識。可以通過判斷該值來獲知該請求的載體究竟是基於哪款瀏覽器還是基於爬蟲程序。
#反爬機制
某些門戶網站會對訪問該網站的請求中的User-Agent進行捕獲和判斷,如果該請求的UA為爬蟲程序,則拒絕向該請求提供數據。
#反反爬策略
將爬蟲程序的UA偽裝成某一款瀏覽器的身份標識。import requests
#想要將url攜帶的參數設定為動態參數
wd = input("enter a key:")
#url中?可加可不加
url = "https://www.sogou.com/web?"
#存儲的就是動態請求參數
params = {
"query":wd
}
#即將發起請求對應的頭信息
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}
#一定需要將params作用到請求中
#params參數是對請求url參數的封裝
response = requests.get(url=url,params=params,headers=headers)
#手動修改相應數據的編碼
response.encoding = "utf-8"
#text返回的是字符串形式的相應數據
page_text = response.text
filename = wd + ".html"
#持久化存儲
with open(filename,"w",encoding="utf-8") as f1:
f1.write(page_text)
print(wd,'下載成功') -
需求三 : 爬取xx電影詳情數據
#需求分析:
當滾輪滑動到底部的時候,發起一個ajax請求,且該請求請求到了一組電影數據
#動態加載的數據 :就是通過另外一個額外的請求到的數據
- ajax生成動態加載的數據
- js生成動態加載的數據import requests
url = "https://movie.douban.com/j/chart/top_list"
start = input("enter a start:")
limit = input("enter a limit:")
#處理請求參數
params = {
"type": "5",
'interval_id': '100:90',
'action': '',
'start': start,
'limit': limit
}
#即將發起請求對應的頭信息
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}
response = requests.get(url=url,params=params,headers=headers)
#json返回的是序列化好的對象
data_list = response.json()
fp = open("douban.txt",'w',encoding="utf-8")
for dic in data_list:
name = dic["title"]
score = dic["score"]
fp.write(name + ":" + score + "\n")
print(name,"爬取成功")
fp.close()

-
需求四 :爬取xx餐廳官網位置信息
鏈接地址 : http://www.kfc.com.cn/kfccda/storelist/index.aspx
import requests
post_url = "http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword"
city = input("enter a city:")
data = {
'cname': '',
'pid': '',
'keyword': city,
'pageIndex': '1',
'pageSize': '10',
}
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}
#data參數表示的就是get方法中的params
response = requests.post(url=post_url,data=data,headers=headers)
response.json()
-
需求五 :爬取xx企業詳情信息
鏈接地址 :http://125.35.6.84:81/xk/
#分析:
1.網站首頁和企業的詳情頁都是動態加載的
2.分析某一家企業詳情數據是怎麽來的
-企業詳情是通過一個ajax請求(post)請求到的
#企業一:
-請求對應的:url:http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById
該請求攜帶了一個參數
id=d39908bcf299469d8e2b1e35a9cd1eee
#企業二:
url:http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById
id: 42410751abcf4cb39884774b0f1a97c5
-結論:
a.每家企業詳情頁都是通過一個post形式的ajax請求請求到的
b.每家企業對應ajax請求的url一樣,請求方式是post,只有請求參數id不同
c.只需要獲取每一家企業對應的id值即可獲取每一家企業對應的詳情數據
3.需要獲取每家企業的id值
#思路:每家對應的id值應該存儲在首頁對應的相關請求或響應中
#結論:每一家企業的id值是存儲在首頁某一個ajax請求對應的數據中,只需要將該相應數據中的企業id一區/解析出即可基礎版 ,存在問題:爬取速度慢,會出現無法爬取情況
import requests
import time
#要請求到每家企業對應的id
url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList"
#即將發起請求對應的頭信息
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}
page = input("enter a page")
page = int(page)
for i in range(1,page+1):
i = str(i)
data = {
'on': 'true',
'page': i,
'pageSize': '15',
'productName': '',
'conditionType': '1',
'applyname': '',
'applysn': ''
}
#該json()的返回值中有每一家企業的id
data_dict = requests.post(url=url,data=data,headers=headers).json()
#解析id
for dic in data_dict["list"]:
_id = dic["ID"]
# print(_id)
#對每個id對應的企業詳情數據進行捕獲
post_url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById"
post_data = {
"id":_id
}
#該json()的返回值中有每一家企業的詳細信息
detail_dic = requests.post(url=post_url,data=post_data,headers=headers).json()
company_name = detail_dic["epsName"]
address = detail_dic["epsProductAddress"]
fp = open("./company_detail.txt",'w',encoding="utf-8")
fp.write(company_name + ":" + address + "\n")
time.sleep(0.5)
print(company_name,'爬取成功')
fp.close()代碼優化版
import requests
import time
#要請求到每家企業對應的id
url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList"
#即將發起請求對應的頭信息
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
}
page = input("enter a page")
page = int(page)
with open("./company_detail.txt",'w',encoding="utf-8") as f:
for i in range(1,page+1):
i = str(i)
data = {
'on': 'true',
'page': i,
'pageSize': '15',
'productName': '',
'conditionType': '1',
'applyname': '',
'applysn': ''
}
#該json()的返回值中有每一家企業的id
data_dict = requests.post(url=url,data=data,headers=headers).json()
#解析id
for dic in data_dict["list"]:
_id = dic["ID"]
# print(_id)
#對每個id對應的企業詳情數據進行捕獲
post_url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById"
post_data = {
"id":_id
}
#該json()的返回值中有每一家企業的詳細信息
detail_dic = requests.post(url=post_url,data=post_data,headers=headers).json()
company_name = detail_dic["epsName"]
address = detail_dic["epsProductAddress"]
f.write(company_name + ":" + address + "\n")
print(company_name,'爬取成功') -
需求六:爬取西刺代理代理ip地址
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
#基於代理精靈構建一個ip池
from lxml import etree
all_ips = [] #列表形式的代理池
proxy_url = 'http://t.11jsq.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=52&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=2'
proxy_page_text = requests.get(url=proxy_url,headers=headers).text
tree = etree.HTML(proxy_page_text)
proxy_list = tree.xpath('//body//text()')
for ip in proxy_list:
dic = {'https':ip}
all_ips.append(dic)
import random
#爬取西祠代理中的免費代理ip
url = 'https://www.xicidaili.com/nn/%d'
free_proxies = []
for page in range(1,30):
new_url = format(url%page)
page_text = requests.get(new_url,headers=headers,proxies=random.choice(all_ips)).text
tree = etree.HTML(page_text)
tr_list = tree.xpath('//*[@id="ip_list"]//tr')[1:]#xpath表達式中不可以出現tbody,第一個是列頭
for tr in tr_list:
ip = tr.xpath('./td[2]/text()')[0]
port = tr.xpath('./td[3]/text()')[0]
t_type = tr.xpath('./td[7]/text()')[0]
dic = {
'ip':ip,
'port':port,
'type':t_type
}
free_proxies.append(dic)
print('第{}頁爬取完畢!!!'.format(page))
print(len(free_proxies)) -
需求七 :爬取雪球網中的新聞咨詢數據https://xueqiu.com
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
#獲取一個session對象
session = requests.Session()
main_url = "https://xueqiu.com" #推測對該url發起請求會產生cookie
session.get(main_url,headers=headers)
url = "https://xueqiu.com/v4/statuses/public_timeline_by_category.json"
params = {
'since_id': '-1',
'max_id': '20347172',
'count': '15',
'category': '-1',
}
page_text = session.get(url=url,headers=headers,params=params).json()
page_text
3.3代理
3.3.1代理基本知識
-
代理
-
代理服務器 :實現請求轉發,從而可以實現更換請求的ip地址
-
在requests中如何將請求的ip進行更換
-
-
代理的匿名度
-
透明: 服務器知道你使用了代理,並且知道了你的真實ip
-
匿名: 服務器知道你使用了代理,但不知道了你的真實ip
-
高匿: 服務器不知道你使用了代理,也不知道了你的真實ip
-
-
代理的類型
-
http :該類型的代理只可轉發http協議的請求
-
https :該類型的代理只可轉發https協議的請求
-
-
爬蟲中為什么需要使用代理?
-
一些網站會有相應的反爬蟲措施,例如很多網站會檢測某一段時間某個IP的訪問次數,如果訪問頻率太快以至於看起來不像正常訪客,它可能就會會禁止這個IP的訪問。所以我們需要設置一些代理IP,每隔一段時間換一個代理IP,就算IP被禁止,依然可以換個IP繼續爬取。
-
-
代理的分類:
-
正向代理:代理客戶端獲取數據。正向代理是為了保護客戶端防止被追究責任。
-
反向代理:代理服務器提供數據。反向代理是為了保護服務器或負責負載均衡。
-
-
免費代理ip提供網站
-
西祠代理
-
快代理
-
付費
-
代理精靈
-
快代理
-
-
在爬蟲中遇到ip被禁用如何處理?
-
使用代理
-
構建一個代理
-
撥號服務器
-
-
更換本機ip
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
url = "https://www.baidu.com/s?wd=ip"
#proxies = {"http/https":"ip:port"}
page_text = requests.get(url=url,headers=headers,proxies={"https":"113.128.31.73:61234"}).text
with open("ip.html",'w',encoding="utf-8") as p:
p.write(page_text)
3.3.2設置ADSL撥號服務器代理
鏈接地址 :https://cuiqingcai.com/3443.html
3.3 cookie
-
作用 : 保存客戶端相關狀態
-
在請求中攜帶cookie,在爬蟲遇到cookie的反爬如何處理?
1.手動處理
在抓包工具中捕獲cookie,將其封裝在headers中
應用場景:cookie無有效時長且不是動態變化
2.自動處理
使用session機制
應用場景:動態變化 -
session對象
-
該對象和requests模塊用法幾乎一致,如果在請求的過程中產生了cookie,如果該請求使用session發起的,則cookie會自動存儲到session中
-
3.4 模擬登陸
驗證碼識別
-
相關的線上打碼平台
-
打碼兔
-
雲打碼
-
超級鷹 http://www.chaojiying.com/about.html
1.注冊,登錄(用戶中心的身份認證)
2.登錄后
創建一個軟件: 點擊 軟件id -->生成一個軟件id
下載一個示例代碼: 開發文檔-->python-->下載#平台示例代碼的演示
import requests
from hashlib import md5
class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}
def PostPic(self, im, codetype):
"""
im: 圖片字節
codetype: 題目類型 參考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
return r.json()
def ReportError(self, im_id):
"""
im_id:報錯題目的圖片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()
if __name__ == '__main__':
chaojiying = Chaojiying_Client('547434796', 'yhh941218', '901271') #用戶中心>>軟件ID 生成一個替換 96001
im = open('a.jpg', 'rb').read() #本地圖片文件路徑 來替換 a.jpg 有時WIN系統須要//
print (chaojiying.PostPic(im, 1004)["pic_str"]) #1902 驗證碼類型 官方網站>>價格體系 3.4+版 print 后要加()
-
古詩文網的模擬登陸
https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx
-
將古詩文網中的驗證碼進行識別
import requests
from lxml import etree
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
url = "https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx"
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
img_src = "https://so.gushiwen.org" + tree.xpath('//*[@id="imgCode"]/@src')[0]
img_code_data = requests.get(img_src,headers=headers).content
with open('./gushiwen.jpg','wb') as f:
f.write(img_code_data)
def getCodeImgText(imgPath,img_type):
chaojiying = Chaojiying_Client('547434796', 'yhh941218', '901271') #用戶中心>>軟件ID 生成一個替換 96001
im = open(imgPath, 'rb').read() #本地圖片文件路徑 來替換 a.jpg 有時WIN系統須要//
return chaojiying.PostPic(im, img_type)["pic_str"] #1902 驗證碼類型 官方網站>>價格體系 3.4+版 print 后要加()
img_text = getCodeImgText("./gushiwen.jpg",1004)
print(img_text)
為什么在爬蟲中要實現模擬登陸
#原因:
有的數據是必須經過登錄后才顯示出來的
-
需求 :對古詩文網做模擬登陸
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
def getCodeImgText(imgPath,img_type):
chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用戶中心>>軟件ID 生成一個替換 96001
im = open(imgPath, 'rb').read() #本地圖片文件路徑 來替換 a.jpg 有時WIN系統須要//
return chaojiying.PostPic(im, img_type)['pic_str']
#使用session捕獲cookie
s = requests.Session()
first_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
s.get(first_url,headers=headers)
url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
img_code_data = s.get(img_src,headers=headers).content
with open('./gushiwen.jpg','wb') as fp:
fp.write(img_code_data)
img_text = getCodeImgText('./gushiwen.jpg',1004)
print(img_text)
#動態捕獲動態的請求參數
__VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR = tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]/@value')[0]
#點擊登錄按鈕后發起請求的url:通過抓包工具捕獲
login_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
data = {
'__VIEWSTATE': __VIEWSTATE,
'__VIEWSTATEGENERATOR': __VIEWSTATEGENERATOR,
'from': 'http://so.gushiwen.org/user/collect.aspx',
'email': 'www.zhangbowudi@qq.com',
'pwd': 'bobo328410948',
'code': img_text,
'denglu': '登錄',
}
main_page_text = s.post(login_url,headers=headers,data=data).text
with open('main.html','w',encoding='utf-8') as fp:
fp.write(main_page_text)#涉及到的反爬:
1.驗證碼
2.動態請求參數:每次請求對應的請求參數都是動態變化
3.cookie
#動態捕獲:通常情況下,動態的請求參數都會被隱藏在前台頁面的源碼中
3.5 基於線程池的異步爬取
url = 'https://www.qiushibaike.com/text/page/%d/'
urls = []
for page in range(1,11):
new_url = format(url%page)
urls.append(new_url)
def get_request(url): #必須有一個參數
return requests.get(url,headers=headers).text
from multiprocessing.dummy import Pool
pool = Pool(10)
response_text_list = pool.map(get_request,urls) #使用自定義的函數func異步的處理urls列表中的每一個列表元素
print(response_text_list)
