淘宝爬虫之自动登录

最近在做一些淘宝数据的定时抓取demo参考github,关于定时任务的设置上一篇已经介绍过:APScheduler定时任务框架的使用,作为亚洲第一大电商平台,淘宝的反爬机制是非常完善的,在用selenium登陆淘宝的时候发现淘宝能检测到并弹出滑块,然后无论怎么滑动都通过不了,在经过一番搜索后发现很多网站对selenium都有检测机制,如检测是否存在特有标识 $cdc_asdjflasutopfhvcZLmcfl 、navigator.webdriver等。根据这条线索,可以在淘宝的js里找到了相关的检测代码:

检测代码.png

在控制台下输入window.navigator.webdriver会发现和正常的浏览器打开的有所不同

屏幕快照 2019-02-14 下午5.38.45.png

如果浏览器是正常打开的话,navigator.webdriver大的值应该是undefined或者false,如果为true说明能检测到selenium
原因发现以后怎么去解决这个问题就比较头疼了,后来在一篇文章中发现,针对这个问题的两个解决方案,编辑chromedriver发现cdc_asdjflasutopfhvcZLmcfl是chromedriver里面的一个变量名,我们只需要将这个变量名改成其他的就好了,变量名修改教程在《Can a website detect when you are using selenium with chromedriver?
Mac版本的可以通过如下方法将 cdc_ 替换为 dog_

perl -pi -e 's/cdc_/dog_/g' /path/to/chromedriver
//替换完成后查找下,如果查找不到说明替换成功了
perl -ne 'while(/cdc_/g){print "$&\n";}' /path/to/chromedriver

Windows或者Linux版本可以利用Vim将 cdc_ 替换为 dog_

 vim /path/to/chromedriver

Mac和win已经修改好的最新版Chromedriver可以参考百度云链接: https://pan.baidu.com/s/1392txVPKDMczBIrUP3noIA 提取码: bnm3
其次针对navigator.webdriver 通过mitmproxy做中间人代理将对应的屏蔽代码注入到原网站中从而达到规避检测目的

首先是配置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检测
        flow.response.text = flow.response.text + INJECT_TEXT

在运行前启动代理

mitmdump -s httpProxy.py -p 9000

淘宝自动登录

# -*- 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):
    """
    会话异常类
    """
    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("开始发起登录请求")
        self.__submit()
        time.sleep(4.5)
        # 登录成功,直接请求页面
        print("登录成功,跳转至目标页面")
        self.__navigate_to_target_page()
        time.sleep(6.5)
        print("解析页面文本")
        crawler_list = self.__parse_page_content();
        # 连接数据库并保存数据
        print("保存数据到mysql数据库")
        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):
        """
        输入账号
        :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):
        """
        判断是否存在滑动验证
        :return:
        """
        return self.__is_element_exist('#nc_1_wrapper') and self.browser.find_element_by_id(
            'nc_1_wrapper').is_displayed()

    def __unlock(self):
        """
        执行滑动解锁
        :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('滑动验证失败, 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('登录出错, message = ' + error_message)

    #跳转至目标页面
    def __navigate_to_target_page(self):
        pass
    # 解析网页数据
    def __parse_page_content(self):
        pass
    #保存数据
    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)


#执行命令行
Crawler().start('username'), 'password'))

这篇文章主要用来记录自己在爬虫过程中遇到问题的解决方案demo参考github,主要的参考文章是python利用selenium实现淘宝自动登录

当然也有一些其他的解决方案,例如cookie验证登录或是支付宝登录等,这些还没尝试过,另外这篇文章也可以作为参考实践一下淘宝爬虫 之 登陆验证(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容