最近在做一些淘寶數(shù)據(jù)的定時(shí)抓取demo參考github寝衫,關(guān)于定時(shí)任務(wù)的設(shè)置上一篇已經(jīng)介紹過(guò):APScheduler定時(shí)任務(wù)框架的使用迫筑,作為亞洲第一大電商平臺(tái)洞慎,淘寶的反爬機(jī)制是非常完善的埂材,在用selenium登陸淘寶的時(shí)候發(fā)現(xiàn)淘寶能檢測(cè)到并彈出滑塊,然后無(wú)論怎么滑動(dòng)都通過(guò)不了恤浪,在經(jīng)過(guò)一番搜索后發(fā)現(xiàn)很多網(wǎng)站對(duì)selenium都有檢測(cè)機(jī)制畅哑,如檢測(cè)是否存在特有標(biāo)識(shí) $cdc_asdjflasutopfhvcZLmcfl 、navigator.webdriver等水由。根據(jù)這條線索荠呐,可以在淘寶的js里找到了相關(guān)的檢測(cè)代碼:
在控制臺(tái)下輸入window.navigator.webdriver會(huì)發(fā)現(xiàn)和正常的瀏覽器打開(kāi)的有所不同
如果瀏覽器是正常打開(kāi)的話,navigator.webdriver大的值應(yīng)該是undefined或者false砂客,如果為true說(shuō)明能檢測(cè)到selenium
原因發(fā)現(xiàn)以后怎么去解決這個(gè)問(wèn)題就比較頭疼了泥张,后來(lái)在一篇文章中發(fā)現(xiàn),針對(duì)這個(gè)問(wèn)題的兩個(gè)解決方案鞭盟,編輯chromedriver發(fā)現(xiàn)cdc_asdjflasutopfhvcZLmcfl是chromedriver里面的一個(gè)變量名圾结,我們只需要將這個(gè)變量名改成其他的就好了,變量名修改教程在《Can a website detect when you are using selenium with chromedriver?》
Mac版本的可以通過(guò)如下方法將 cdc_ 替換為 dog_
perl -pi -e 's/cdc_/dog_/g' /path/to/chromedriver
//替換完成后查找下齿诉,如果查找不到說(shuō)明替換成功了
perl -ne 'while(/cdc_/g){print "$&\n";}' /path/to/chromedriver
Windows或者Linux版本可以利用Vim將 cdc_ 替換為 dog_
vim /path/to/chromedriver
Mac和win已經(jīng)修改好的最新版Chromedriver可以參考百度云鏈接: https://pan.baidu.com/s/1392txVPKDMczBIrUP3noIA 提取碼: bnm3
其次針對(duì)navigator.webdriver 通過(guò)mitmproxy做中間人代理將對(duì)應(yīng)的屏蔽代碼注入到原網(wǎng)站中從而達(dá)到規(guī)避檢測(cè)目的
首先是配置mitmproxy
注入屏蔽代碼
TARGET_URL = 'https://g.alicdn.com/secdev/sufei_data/3.6.8/index.js'
INJECT_TEXT = 'Object.defineProperties(navigator,{webdriver:{get:() => false}});'
def response(flow):
if flow.request.url.startswith(TARGET_URL):
flow.response.text = INJECT_TEXT + flow.response.text
print('注入成功')
if 'um.js' in flow.request.url or '115.js' in flow.request.url:
# 屏蔽selenium檢測(cè)
flow.response.text = flow.response.text + INJECT_TEXT
在運(yùn)行前啟動(dòng)代理
mitmdump -s httpProxy.py -p 9000
淘寶自動(dòng)登錄
# -*- coding:UTF-8 -*-
import time
import re
from datetime import date, timedelta
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options
TB_LOGIN_URL = 'https://login.taobao.com/member/login.jhtml'
CHROME_DRIVER = '/usr/local/bin/chromedriver' # Windows和Mac的配置路徑不一樣
class SessionException(Exception):
"""
會(huì)話異常類(lèi)
"""
def __init__(self, message):
super().__init__(self)
self.message = message
def __str__(self):
return self.message
class Crawler:
def __init__(self):
self.browser = None
def start(self, username, password):
print("初始化瀏覽器")
self.__init_browser()
print("切換至密碼輸入框")
self.__switch_to_password_mode()
time.sleep(0.5)
print("輸入用戶名")
self.__write_username(username)
time.sleep(2.5)
print("輸入密碼")
self.__write_password(password)
time.sleep(3.5)
print("程序模擬解鎖")
if self.__lock_exist():
self.__unlock()
print("開(kāi)始發(fā)起登錄請(qǐng)求")
self.__submit()
time.sleep(4.5)
# 登錄成功筝野,直接請(qǐng)求頁(yè)面
print("登錄成功,跳轉(zhuǎn)至目標(biāo)頁(yè)面")
self.__navigate_to_target_page()
time.sleep(6.5)
print("解析頁(yè)面文本")
crawler_list = self.__parse_page_content();
# 連接數(shù)據(jù)庫(kù)并保存數(shù)據(jù)
print("保存數(shù)據(jù)到mysql數(shù)據(jù)庫(kù)")
self.__save_list_to_db(crawler_list)
def __switch_to_password_mode(self):
"""
切換到密碼模式
:return:
"""
if self.browser.find_element_by_id('J_QRCodeLogin').is_displayed():
self.browser.find_element_by_id('J_Quick2Static').click()
def __write_username(self, username):
"""
輸入賬號(hào)
:param username:
:return:
"""
username_input_element = self.browser.find_element_by_id('TPL_username_1')
username_input_element.clear()
username_input_element.send_keys(username)
def __write_password(self, password):
"""
輸入密碼
:param password:
:return:
"""
password_input_element = self.browser.find_element_by_id("TPL_password_1")
password_input_element.clear()
password_input_element.send_keys(password)
def __lock_exist(self):
"""
判斷是否存在滑動(dòng)驗(yàn)證
:return:
"""
return self.__is_element_exist('#nc_1_wrapper') and self.browser.find_element_by_id(
'nc_1_wrapper').is_displayed()
def __unlock(self):
"""
執(zhí)行滑動(dòng)解鎖
:return:
"""
bar_element = self.browser.find_element_by_id('nc_1_n1z')
ActionChains(self.browser).drag_and_drop_by_offset(bar_element, 800, 0).perform()
time.sleep(1.5)
self.browser.get_screenshot_as_file('error.png')
if self.__is_element_exist('.errloading > span'):
error_message_element = self.browser.find_element_by_css_selector('.errloading > span')
error_message = error_message_element.text
self.browser.execute_script('noCaptcha.reset(1)')
raise SessionException('滑動(dòng)驗(yàn)證失敗, message = ' + error_message)
def __submit(self):
"""
提交登錄
:return:
"""
self.browser.find_element_by_id('J_SubmitStatic').click()
time.sleep(0.5)
if self.__is_element_exist("#J_Message"):
error_message_element = self.browser.find_element_by_css_selector('#J_Message > p')
error_message = error_message_element.text
raise SessionException('登錄出錯(cuò), message = ' + error_message)
#跳轉(zhuǎn)至目標(biāo)頁(yè)面
def __navigate_to_target_page(self):
pass
# 解析網(wǎng)頁(yè)數(shù)據(jù)
def __parse_page_content(self):
pass
#保存數(shù)據(jù)
def __save_list_to_db(self, crawler_list):
pass
def __init_browser(self):
"""
初始化selenium瀏覽器
:return:
"""
options = Options()
# options.add_argument("--headless")
prefs = {"profile.managed_default_content_settings.images": 1}
options.add_experimental_option("prefs", prefs)
options.add_argument('--proxy-server=http://127.0.0.1:9000')
options.add_argument('disable-infobars')
options.add_argument('--no-sandbox')
self.browser = webdriver.Chrome(executable_path=CHROME_DRIVER, options=options)
self.browser.implicitly_wait(3)
self.browser.maximize_window()
self.browser.get(TB_LOGIN_URL)
#執(zhí)行命令行
Crawler().start('username'), 'password'))
這篇文章主要用來(lái)記錄自己在爬蟲(chóng)過(guò)程中遇到問(wèn)題的解決方案demo參考github粤剧,主要的參考文章是python利用selenium實(shí)現(xiàn)淘寶自動(dòng)登錄
當(dāng)然也有一些其他的解決方案歇竟,例如cookie驗(yàn)證登錄或是支付寶登錄等,這些還沒(méi)嘗試過(guò)抵恋,另外這篇文章也可以作為參考實(shí)踐一下淘寶爬蟲(chóng) 之 登陸驗(yàn)證(二)