使用Selenium做网站登录的免验证

我发现,我已经三年多没有更新博客了。这几年一直感觉没什么可写的,工作上没遇到的问题python的不多,主要是前端页面上遇到的问题,感觉写起来比较困难,一写就要贴上去很多代码,还没什么必要,不贴又说不明白,所以干脆不写了。
 
今年开始研究新玩意儿了——爬虫。俗话说,爬虫爬得好,牢饭吃到饱(不是)。以前感觉爬虫太烦了,所以一直没去研究。但工作需要,没办法,还是得硬着头皮上了(我真没有在搞什么违法的事情啊)。
 
其实我做的这个爬虫也算是比较简单的那种,用Selenium登录某网站系统,在某个页面抓取一些实时的聊天信息,并加以回复。具体哪个网站我就不说了,抓取页面元素也很简单,关键难在了登录免验证的过程。
 
一般的网站套路是比较固定的,第一次登录之后,把cookie保存下来,以后再打开这个网站的登录页,加载cookie然后刷新一下,就ok了。
 
保存cookie:
1 def save_cookies():
2     # 我这里用pickle来序列化的,也可以用json
3     # 在登录网站之后,cookie中就保存了用户已登录的信息,driver.get_cookies()就可以获得这些cookie,保存在本地即可
4     cookie_filename = "cookies_xxx.pickle"
5     with open(cookie_filename, "wb") as f:
6         pickle.dump(driver.get_cookies(), f)

 

加载cookie:
 1 def load_cookies(driver):
 2     # 获得本地保存的cookie
 3     filename = "cookie_xxx.pickle"
 4     # 读取cookie内容
 5     with filename(path, 'rb') as file:
 6         cookies = pickle.load(file)
 7     # 加载到driver
 8     for c in cookies:
 9         driver.add_cookie(c)
10 
11 
12 # 需要先打开要登录的网站,否则加载cookie时会报域名不匹配的异常
13 # selenium.common.exceptions.InvalidCookieDomainException: Message: invalid cookie domain
14 driver.get("https://login.xxx.com")
15 # 加载cookie前清理掉之前的cookie,这句有些网站不需要
16 driver.delete_all_cookies()
17 # 加载cookie
18 load_cookies(driver)
19 # 已经加载好了,可以去你想去的页面了。如果想在当前页面,就刷新下页面
20 driver.refresh()  # 刷新页面,原地不动的情况下使用,要跳转的话可以不用
21 driver.get("https://aaa.xxx.com/bbb/")  # 跳转到你想去的页面就好了

 

但我现在爬的这个网站就很棒棒,加载cookie后刷新,不生效,依然停留在登录页面。我试过各种姿势加载cookie,还把保存下来的cookie和手动登录后的cookie对比(好像也没什么区别啊),始终不行。
 
但是我发现,我手动登录网站的话,不退出账号就关闭浏览器,再次打开浏览器打开网站,是不用登录的,这说明cookie本身是没问题的。所以我猜测,网站是发现了我的机器行为。于是下一步我开始研究,selenium在初始化webdriver时候的那堆选项,也就是options。我借鉴了一些博客中写的常用的option,最终初始化webdriver的方法如下:
 
 1 def init_driver():
 2     options = Options()
 3     options.add_argument("--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")  # 模拟人为操作user-agent肯定需要的
 4     options.add_argument("--disable-blink-features=AutomationControlled")  # 检查是否为软件自动控制
 5     options.add_argument("--ignore-certificate-errors")  # 忽略证书错误
 6     options.add_argument("--ignore-ssl-errors")  # 忽略ssl错误
 7     options.add_argument('--disable-gpu')  # 关闭GPU加速
 8     options.add_argument('--no-sandbox')  # 以最高权限运行
 9     options.add_argument('--log_level=3')  # 日志级别(在命令行里不会报一堆干扰信息)
10     options.add_experimental_option('excludeSwitches', ['enable-automation'])  # 也是检查是否为软件自动控制
11     options.add_experimental_option("useAutomationExtension", False)  # 还是检查是否为软件自动控制
12     options.add_experimental_option("detach", True)  # 程序结束时浏览器不会自动关闭
13     _driver = webdriver.Chrome(options=options)
14     return _driver

 

信心满满的打开网站再试,果然!
还是不行。
 

 

但是琢磨了一段时间后,我突然想到,我在一个页面看到的一个option写法:
 
1 options.add_experimental_option("debuggerAddress", "127.0.0.1:9527")

 

它的意思是说,用远程调试模式,连接本地9527端口的chrome。这不就是说,它可以用我原来的浏览器,打开网站时候加载原来的cookie,然后就可以免登录了?
也许是个办法,我得试一下。
 
这个方法要求本地的chrome在启动的时候带参数:
 
--remote-debugging-port=9527

 

我有两种办法启动它,一种是,在命令行,输入chrome的位置,后面加上参数;另一种方法是,使用快捷方式。当然用后者啊!
快捷方式就在桌面上,右键,属性,在“目标”那一行最后加上参数,确定即可。
 

 

最后要注意的就是,用这个方法的话,必须得先用这个快捷方式打开浏览器,这就相当于在9527端口开了个服务器,然后我的程序就可以连接使用了。
 

 

好奇怪的操作有木有!既然这样,我干嘛不写代码执行呢?先打开带参数的浏览器,再让程序去连接它?
简单,一行代码搞定!
 
1 os.system(os.path.abspath(os.path.join(os.path.expanduser("~"), "AppData\\Local\\Google\\Chrome\\Application\\chrome.exe")) + " --remote-debugging-port=9527")

 

结果无效。

 

 

先关掉浏览器,再掐掉程序(程序是在命令行下执行的,如果不关闭浏览器的话Ctrl+C中止不了程序),就着命令行的错误看了一下,程序是在连接127.0.0.1:9527 浏览器的时候卡住了。也就是说,用os.system执行命令打开浏览器这种方法不行。
 
接下来我又尝试了subprocess.run,subprocess.call,subprocess.getoutput,都不行。就在我走投无路的时候,我突然发现了os.startfile函数:
 
os.startfile(os.path.abspath(os.path.join(os.path.expanduser("~"), "Desktop\\Google Chrome.lnk")))

 

再次尝试,成功,可以正常打开桌面的chrome浏览器的快捷方式,打开网站,并在第一次登录之后,以后再来到网站时候都可以直接打开网页,不用再次登录了。接下来就可以愉快地爬我需要的信息了。

其实做到现在这样子,还是有一点小瑕疵的:我需要手动在chrome的快捷方式后面加个参数。虽说只是部署时的一次性工作,但是,为什么不用代码来添加呢?不过,那是另一个问题了。

(写了一篇新的随笔:https://www.cnblogs.com/anpengapple/p/18189293 ,就是处理windows快捷方式的)

 

posted @ 2024-05-08 11:36  _小苹果  阅读(43)  评论(0编辑  收藏  举报