我发现,我已经三年多没有更新博客了。这几年一直感觉没什么可写的,工作上没遇到的问题python的不多,主要是前端页面上遇到的问题,感觉写起来比较困难,一写就要贴上去很多代码,还没什么必要,不贴又说不明白,所以干脆不写了。
今年换了工作,开始研究新玩意儿了——爬虫。俗话说,爬虫爬得好,牢饭吃到饱(不是)。以前感觉爬虫太烦了,所以一直没去研究。但工作需要,没办法,还是得硬着头皮上了(我真没有在搞什么违法的事情啊)。
其实我做的这个爬虫也算是比较简单的那种,用Selenium登录某网站系统,在某个页面抓取一些实时的聊天信息,并加以回复。具体哪个网站我就不说了,抓取页面元素也很简单,关键难在了登录免验证的过程。
1 os.startfile(os.path.abspath(os.path.join(os.path.expanduser("~"), "Desktop\\Google Chrome.lnk")))
一般的网站套路是比较固定的,第一次登录之后,把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
信心满满的打开网站再试,果然!
还是不行。
![](https://img2024.cnblogs.com/blog/439064/202405/439064-20240508105412911-706602472.jpg)
但是琢磨了一段时间后,我突然想到,我在一个页面看到的一个option写法:
1 options.add_experimental_option("debuggerAddress", "127.0.0.1:9527")
它的意思是说,用远程调试模式,连接本地9527端口的chrome。这不就是说,它可以用我原来的浏览器,打开网站时候加载原来的cookie,然后就可以免登录了?
也许是个办法,我得试一下。
这个方法要求本地的chrome在启动的时候带参数:
--remote-debugging-port=9527
我有两种办法启动它,一种是,在命令行,输入chrome的位置,后面加上参数;另一种方法是,使用快捷方式。当然用后者啊!
快捷方式就在桌面上,右键,属性,在“目标”那一行最后加上参数,确定即可。
![](https://img2024.cnblogs.com/blog/439064/202405/439064-20240508111209036-503412267.jpg)
最后要注意的就是,用这个方法的话,必须得先用这个快捷方式打开浏览器,这就相当于在9527端口开了个服务器,然后我的程序就可以连接使用了。
![](https://img2024.cnblogs.com/blog/439064/202405/439064-20240508111235312-888778821.jpg)
好奇怪的操作有木有!既然这样,我干嘛不写代码执行呢?先打开带参数的浏览器,再让程序去连接它?
简单,一行代码搞定!
1 os.system(os.path.abspath(os.path.join(os.path.expanduser("~"), "AppData\\Local\\Google\\Chrome\\Application\\chrome.exe")) + " --remote-debugging-port=9527")
结果无效。
![](https://img2024.cnblogs.com/blog/439064/202405/439064-20240508111538444-260139104.jpg)
先关掉浏览器,再掐掉程序(程序是在命令行下执行的,如果不关闭浏览器的话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的快捷方式后面加个参数。虽说只是部署时的一次性工作,但是,为什么不用代码来添加呢?不过,那是另一个问题了。