應對反爬蟲問題(正在學習中)
1.構造合理的HTTP請求頭
目前我一般使用的是更改User-Agent 有些網站不喜歡爬蟲訪問,會檢測連線物件,如果是爬蟲程式不會讓你訪問
import requests
url='https://www.amazon.cn/'
hd={'User-Agent':'Mozilla/5.0'}
try:
r=requests.get(url,headers=hd)
r.raise_for_status()
print(r.request.headers)
except:
print('----')
#輸出
{'User-Agent': 'Mozilla/5.0' , 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
2.注意隱含輸入欄位
在 HTML 表單中,“隱含”欄位可以讓欄位的值對瀏覽器可見,但是對使用者不可見(除非看網頁原始碼)。
用隱含欄位阻止網路資料採集的方式主要有兩種。 第一種是表單頁面上的一個欄位可以用伺服器生成的隨機變量表示。如果提交時這個值不在表單處理頁面上,伺服器就有理由認為這個提交不是從原始表單頁面上提交的,而是由一個網路機器人直接提交到表單處理頁面的。繞開這個問題的最佳方法就是,首先採集表單所在頁面上生成的隨機變數,然後再提交到表單處理頁面。
第二種方式是“蜜罐”(honey pot)。如果表單裡包含一個具有普通名稱的隱含欄位(設定蜜罐圈套),比如“使用者名稱”(username)或“郵箱地址”(email address),設計不太好的網路機器人往往不管這個欄位是不是對使用者可見,直接填寫這個欄位並向伺服器提交,這樣就會中伺服器的蜜罐圈套。伺服器會把所有隱含欄位的真實值(或者與表單提交頁面的預設值不同的值)都忽略,而且填寫隱含欄位的訪問使用者也可能被網站封殺。
那麼該如何去避開蜜罐圈套
比如我們在進行網路爬蟲的時候,我們很容易可以採集到資訊,但是如果爬取到的一個網路表單的欄位通過CSS設定成不可見,那麼普通使用者在瀏覽器上是看不的,所以也不可能填寫這個欄位,如果被被填寫了,就會認為可能是爬蟲乾的,那麼就是封禁你。
這種手段不僅可以應用在網站的表單上,還可以應用在連結、圖片、檔案,以及一些可以被機器人讀取,但普通使用者在瀏覽器上卻看不到的任何內容上面。訪問者如果訪問了網站上的一個“隱含”內容,就會觸發伺服器指令碼封殺這個使用者的 IP 地址,把這個使用者踢出網站,或者採取其他措施禁止這個使用者接入網站。
from selenium import webdriver
url='http://pythonscraping.com/pages/itsatrap.html'
driver=webdriver.Chrome()
driver.get(url)
items=driver.find_elements_by_tag_name('a')
for item in items:
if not item.is_displayed():#如果標籤是不可見的
print('{}連結不可見'.format(item.get_attribute('href')))
inputs=driver.find_elements_by_tag_name('input')
for item in inputs:
if not item.is_displayed():
print('{}此表單不可填寫'.format(item.get_attribute('name')))
#輸出
http://pythonscraping.com/dontgohere連結不可見
phone此表單不可填寫
email此表單不可填寫
3.建立自己的代理IP池
伺服器會檢測一個IP在單位時間內的請求次數,如果超過了某個閾值,那麼伺服器會直接拒絕服務,返回一些錯誤資訊。 所以我們可以藉助某種方式來偽裝IP,讓伺服器無法識由我們本機發起的請求,這樣子我們就可以防止封IP了
這裡我們需要 1.去網站爬取免費的IP 2.檢查爬去的IP是否有效 3.將有效的IP儲存下來
那麼怎麼樣才能檢測一個IP是否有效呢? 第一種方式:利用requests.get(url,timeout=?),設定timeout一個值,超時了就說明不可用 第二種方式:用本機去ping這個IP,如果ping通,那麼就說明這個IP也能去訪問其他的網址
我利用第二種方式去實現 那麼怎麼利用python實現 Subprocess.Popen()可以建立一個程序,當shell引數為true時,程式通過shell來執行:
引數:
程式碼:
from bs4 import BeautifulSoup
import requests
import subprocess as sp
import re
def getIP(IPlist): # 爬取網頁上的免費ip,並且存入IPlist
url = 'http://www.xicidaili.com/nn' # 獲得免費IP的網址
hd = {'User-Agent': 'Mozilla/5.0'} # 模擬瀏覽器進行訪問
try:
r = requests.get(url, headers=hd)
r.raise_for_status()
r.encoding = r.apparent_encoding
soup = BeautifulSoup(r.text, 'html.parser')
# 根據頁面結構來選擇出元素
list = (soup.select('tr')[1:]) # 用CSS選擇器先找到tr標籤,其中第一個標籤是無效的
for item in list: # 遍歷
taglist = item.select('td') # 找到td標籤,其中第二,第三,第五個標籤是我們想要的
ip = taglist[5].string.lower() + '#' + taglist[1].string + '#' + taglist[2].string # 新增#後面好處理
IPlist.append(ip)
except:
print('1111')
def check_ip(ip): # 檢查ip是否可用
cmd = 'ping -n 3 -w 3 {}'.format(ip) # 本機能夠ping通這個代理IP,那麼我們也就可以使用這個代理IP去訪問其他網站
p = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE, shell=True) # 使用Subprocess.Popen可以建立一個程序
out = p.stdout.read().decode('gbk')
# 丟包數
Lost_Packets = loss(out)
# 平均耗時
time = average_time(out)
# 選擇出可用的ip,可用就返回True
if Lost_Packets <= 1 and time <= 500: # 當丟包數小於1,平均耗時小於500ms
return True
else:
return False
def loss(out): # 匹配出丟包數
match = re.search(r'丟失 = (\d)', out) # 利用正則表示式
Lost_Packets = int(match.group(1))
return Lost_Packets
def average_time(out):
match2 = re.search(r'平均 = (\d*)ms', out)
if match2:
return int(match2.group(1))
else:
return 1000
def main():
IPlist = []
getIP(IPlist)
ulist = [] # 用來儲存可用的IP地址
for IP in IPlist: # 先檢查前20個
elements = IP.split('#') # 以'#'分割,提取其中的關鍵資訊
ip = elements[1]
# print(ip)
if check_ip(ip):
ulist.append({elements[0]: elements[1] + ':' + elements[2]})
print(ulist)
main()
#給出部分輸出
{'http': '111.194.14.157:8118'}, {'https': '106.14.47.5:80'}, {'http': '106.75.225.83:808'}, {'https': '218.59.228.18:61976'}
這樣就好啦 以後學習到其他的繼續更新