1. 程式人生 > 實用技巧 >爬蟲—模擬登陸

爬蟲—模擬登陸

報錯HttpConnectinPool:
原因:

  • 1.短時間內發起了高頻的請求導致ip被禁。
  • 2.傳送高頻的請求且請求成功後沒有被及時斷開,導致http連線池(http連線物件)中的連線資源被耗盡。
    解決:
  • 1.代理
  • 2.headers中加入Conection:"close",表示請求後連線立即斷開。

代理

代理:代理伺服器,可以接受請求然後將其轉發。

匿名度

  • 透明:知道你使用了代理並且知道你的真實ip
  • 匿名:知道你使用了代理,但是不知道你的真實ip
  • 高匿:啥也不知道
    型別:
  • http,只能轉發http請求
  • https,只能轉發https請求
    免費代理:
  • www.goubanjia.com
  • 快代理
  • 西祠代理
  • http://http.zhiliandaili.cn/

使用代理

importrequests
headers={
'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/76.0.3809.132Safari/537.36',
'Connection':"close"
}
url='https://www.baidu.com/s?ie=UTF-8&wd=ip'
page_text=requests.get(url,headers=headers,proxies={'https':'1.197.204.153:9999'
}).text
withopen('ip.html','w',encoding='utf-8')asfp:
fp.write(page_text)

代理可能隨時被封

解決方法:代理池

#代理池:列表
importrandom
proxy_list=[
{'https':'121.231.94.44:8888'},
{'https':'131.231.94.44:8888'},
{'https':'141.231.94.44:8888'}
]
url='https://www.baidu.com/s?wd=ip'
page_text=requests.get(url,headers=headers,proxies=random.choice(proxy_list)).text
with
open('ip.html','w',encoding='utf-8')asfp:
fp.write(page_text)

使用購買代理並爬取代理

推薦網址:http://http.zhiliandaili.cn/(代理精靈)

  1. 進入網址後註冊登陸
  2. 購買後進入API提取
  3. 選擇提取型別,提取數量,資料格式(HTML),分隔符(換行),是否去重等等
  4. 選擇好後點擊生成API連結,複製連結
#檢測IP是否可用
deffunc_ip(proxy_list_http)
#隨便訪問一個網址
response=requests.get('https://www/sogou.com',headers=headers,proxies={'https':ip})
#status_code響應狀態碼
ifresponse.status_code=='200':

returnTrue
else;returnFalse
#從代理精靈中提取代理ip
API_url='http://t.11jsq.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=4&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=2'
page_text=requests.get(ip_url,headers=headers).text
tree=etree.HTML(page_text)
ip_list=tree.xpath('//body//text()')
ip_list#購買的IP

使用購買的IP爬取免費IP構建代理池

fromlxmlimportetree

#爬取西祠代理
proxy_list_http=[]
proxy_list_https=[]

defget_ip(url,headers,proxies):
page_text=requests.get(url,headers=headers,proxies={'https':ip_port}).text
tree=etree.HTML(page_text)
#tr_list=tree.xpath('//*[@id="ip_list"]/tbody/tr')
#tbody不可以出現在xpath表示式中,否則表示式會失效
tr_list=tree.xpath('//*[@id="ip_list"]//tr')[1:]
fortrintr_list:
ip=tr.xpath('./td[2]/text()')[0]
port=tr.xpath('./td[3]/text()')[0]
t_type=tr.xpath('./td[6]/text()')[0]
ips=ip+':'+port

#若IP過多,可以存進資料庫中,但效率不高,可以直接購買ip
ift_type=='HTTP':
dic={
t_type:ips
}
proxy_list_http.append(dic)
else:
dic={
t_type:ips
}
proxy_list_https.append(dic)


url='https://www.xicidaili.com/nn/%d'
forpageinrange(1,20):
new_url=format(url%page)
ip_port=random.choice(ip_list)
iffunc_ip(ip_port):
get_ip(new_url,headers,ip_port)
else:
ip_list.remove(ip_port)
print(len(proxy_list_http),len(proxy_list_https))

Cookie的處理

Cookie中有請求相關的狀態資訊,伺服器端可以通過Cookie判斷請求者的身份資訊與狀態資訊。

Cookie的處理:

  • 手動處理:將Cookie封裝到headers中
  • 自動處理:session物件。可以建立一個session物件,該物件可以像requests一樣進行請求傳送。不同之處在於如果在使用session進行請求傳送的過程中產生了cookie,則cookie會被自動儲存在session物件中。

專案 手動處理Cookie

對雪球網中的新聞資料進行爬取 urlhttps://xueqiu.com/

分析:

  • 頁面資料是動態載入的(拖動滾輪,頁面不斷載入,url位址列不重新整理)
  • Request Headers欄中有Cookie資訊
  • 可以複製Cookie資訊,傳入請求頭中進行爬取
headers={
'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/76.0.3809.132Safari/537.36',
'Cookie':'aliyungf_tc=AQAAAAl2aA+kKgkAtxdwe3JmsY226Y+n;acw_tc=2760822915681668126047128e605abf3a5518432dc7f074b2c9cb26d0aa94;xq_a_token=75661393f1556aa7f900df4dc91059df49b83145;xq_r_token=29fe5e93ec0b24974bdd382ffb61d026d8350d7d;u=121568166816578;device_id=24700f9f1986800ab4fcc880530dd0ed'
}
url='https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=20349203&count=15&category=-1'
page_text=requests.get(url=url,headers=headers).json()
page_text

直接將Cookie新增至請求頭,但Cookie有時間限制,每次爬蟲可能需要手動處理,使程式碼不易維護。

使用session物件自動處理Cookie

#建立session物件
session=requests.Session()

#訪問網站首頁,獲取Cookie
session.get('https://xueqiu.com',headers=headers)

#利用session物件爬取頁面
url='https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=20349203&count=15&category=-1'
page_text=session.get(url=url,headers=headers).json()
page_text

模擬登陸

驗證碼的識別:要基於打碼平臺識別識別驗證碼。
相關平臺:

  • 打碼兔

  • 雲打碼

  • 超級鷹:http://www.chaojiying.com/about.html

本教程使用超級鷹平臺:

  • 註冊:(使用者中心身份)
  • 登陸:
    • 軟體ID-->生成一個軟體ID-->記錄軟體ID
    • 開發文件-->選擇python-->下載示例程式碼
    • 價格體系-->選擇驗證碼型別並記錄
#貼上示例程式碼
importrequests
fromhashlibimportmd5

classChaojiying_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;MSIE8.0;WindowsNT5.1;Trident/4.0)',
}

defPostPic(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)
returnr.json()

defReportError(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)
returnr.json()

模擬登陸古詩文網

urlhttps://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx

#識別古詩文網登陸頁面中的驗證碼
deftranformImgData(imgPath,t_type):
#超級鷹使用者名稱,超級鷹使用者名稱的密碼,軟體ID
chaojiying=Chaojiying_Client('chongxiao','chongxiao','907070')
#本地圖片檔案路徑
im=open(imgPath,'rb').read()
#chaojiying返回一個字典,pic_str鍵的值是圖形中的驗證碼
returnchaojiying.PostPic(im,t_type)['pic_str']

url='https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/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_data=requests.get(img_src,headers=headers).content
withopen('./code.jpg','wb')asfp:
fp.write(img_data)

#1004驗證碼型別
tranformImgData('./code.jpg',1004)

分析:

  • 將分析成功的字串驗證碼傳送給古詩文網
  • 在瀏覽器直接登入,用抓包工具,找到哪一個資料包傳送的登入請求,一般為POST請求,且Form Data中有請求的使用者名稱與密碼資料。
  • 找到傳送登入請求的資料包為login.aspx?from…;從General中找到Request URL,並且發現該資料包請求頭中有Cookie。
  • 退出賬號再次登入,發現From Data的前兩個引數與第一次不一樣,即為動態變化的。
  • 猜測動態引數是由隱藏的input標籤攜帶,在登入頁面,找到對應的頁面資料包,進行全域性全域性搜尋,搜尋變化引數的鍵值,結果與猜測一樣。
  • 選擇隱藏的input標籤,右鍵copy Xpath表示式。
#模擬登陸
s=requests.Session()
url='https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text=s.get(url,headers=headers).text
tree=etree.HTML(page_text)
img_src='https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]

#此處用requests物件請求,卻發現仍無法請求成功,經分析發現,登入的Cookie是由獲取圖片的請求攜帶的。
img_data=s.get(img_src,headers=headers).content
withopen('./code.jpg','wb')asfp:
fp.write(img_data)

#動態獲取變化的請求引數
__VIEWSTATE=tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR=tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]/@value')[0]

code_text=tranformImgData('./code.jpg',1004)
print(code_text)
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':'[email protected]',
'pwd':'bobo328410948',
'code':code_text,
'denglu':'登入',
}
page_text=s.post(url=login_url,headers=headers,data=data).text
withopen('login.html','w',encoding='utf-8')asfp:
fp.write(page_text)

動態變化的請求引數:通常情況下動態變化的請求引數都會被隱藏在前臺頁面原始碼中。

攜帶Cookie的響應:伺服器傳送Cookie不一定是請求登入頁面傳送的,也可能是請求某個動態資料或圖片傳送的,若用session物件處理Cookie,則最好每一個請求都用session物件傳送。