大家好,我是 Jack。
之前一直有小伙伴问我,有没有免费的股票信息查询的 API 接口?
我看了一圈,很多免费的 API 接口都年久失修,失效了。
那好吧,咱自己写一个。
想要玩量化交易,第一步,那得有稳定的股票数据来源。
然后再谈什么量化策略,怎么选股、何时买股。
怎么稳定的获取数据呢?
只能是抄起我的老板行,写个网络爬虫,自动抓取数据。
玩股票、玩基金的,应该多多少听过一款股票交流 APP 雪球。

这里面的数据很全,就它了!
前方提醒:使用网络爬虫,请控制好访问频率。
在雪球上,想要获得各种股票信息,那需要携带身份信息,也就是要有 Cookie。
没有 Cookie,很多信息是获取不到的。
2017 年的时候,我就写过关于 Cookie 的文章。

一些基础知识忘记的小伙伴,可以重温下我这个系列的文章。
想要获取 Cookie,那就需要进行模拟登录。
模拟登录 - 准备篇
模拟登录,顾名思义,就是模拟人类的行为,登录这个网站。
登录之后,我们就可以用保存身份信息的 Cookie,获取我们想要的各种数据:股票信息、基金信息等。
我们先手动登录,体验一下整个登录流程。
手动登录
第一步:点击登录按钮。

第二步:输入帐号和密码,并点击登录。

第三步:解锁滑块。

第四步:登录成功。

模拟登录
接下来,就是需要写个代码,让代码替我们完成上述操作。
这里我使用 Selenium,它是一款自动化测试工具。
不过说实话,Selenium 这东西挺老了。
现在有不少更好的工具,不过对于模拟登录的知识储备,我还停留在 2017 年,也只会用它了。
有更好更好的方法的话,欢迎小伙伴们提交 PR。
不过,好在 Selenium 虽然老了点,但还能勉强胜任获取 Cookie 这项工作的。
Selenium 不会的小伙伴,可以看我从前的教程:
https://jackcui.blog.csdn.net/article/details/72331737
想要使用 Selenium,首先需要下载浏览器驱动,这里以 Chrome 浏览器为例。
打开 Chrome 浏览器,查看 Chrome 版本号。

然后根据这个版本号,下载相同大版本的驱动。
http://chromedriver.storage.googleapis.com/index.html

根据自己的操作系统,选择对应的版本。

我的是 Windows 电脑,选择 Win32 的版本。
下载好后,解压备用。
最后安装 Selenium 第三方依赖库。
python -m pip install selenium==3.4 --user
注意,需要安装 3.4 的版本,Selenium 的新版本改动较多,用我的代码会存在接口不兼容的情况。
模拟登录 - 实战篇
我们先睹为快,看下让代码自动登录雪球的效果:
https://cuijiahua.com/wp-content/uploads/2022/12/1.mp4
(PS:录屏时间 12.2,由于大家都知道的原因,页面为黑白)
其实模拟登录的思路很简单,就是根据审查元素,找到各个元素的位置。
比如登录按钮,右键审查元素,然后选择 Copy Xpath。

就能拷贝路径地址。
使用这种方法,找到帐号输入框、密码输入框的位置,然后点击登录即可。
这里的难点在于验证码。
不过好在,GEETEST 验证码的破解,我还是有些经验的,17 年的时候,就写过相关内容。

很多代码,直接复用即可。
整体思路就是:
-  使用Selenium打开页面。 
-  匹配到输入框,输入账号密码,点击登录。 
-  读取验证码图片,并做缺口识别。 
-  根据缺口位置,计算滑动距离。 
-  根据滑动距离,拖拽滑块到需要匹配的位置。 
直接放代码:
  
   - 
    
     
    
    
     
      from selenium 
      import webdriver
     
    
- 
    
     
    
    
     
      from selenium.webdriver 
      import ActionChains
     
    
- 
    
     
    
    
     
      from io 
      import BytesIO
     
    
- 
    
     
    
    
     
      import json
     
    
- 
    
     
    
    
     
      import base64
     
    
- 
    
     
    
    
     
      import time
     
    
- 
    
     
    
    
     
      from selenium.webdriver.common.by 
      import By
     
    
- 
    
     
    
    
     
      from selenium.webdriver.support.wait 
      import WebDriverWait
     
    
- 
    
     
    
    
     
      from selenium.webdriver.support 
      import expected_conditions 
      as EC
     
    
- 
    
     
    
    
     
      from time 
      import sleep
     
    
- 
    
     
    
    
     
      from PIL 
      import Image
     
    
- 
    
     
    
    
     
      from selenium 
      import webdriver
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      # 账号
     
    
- 
    
     
    
    
     
      USERNAME = 
      '***'
     
    
- 
    
     
    
    
     
      # 密码
     
    
- 
    
     
    
    
     
      PASSWORD = 
      '***'
     
    
- 
    
     
    
    
     
      BORDER = 
      6
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      class 
      Login(
      object):
     
    
- 
    
     
    
    
     
          
      def 
      __init__(
      self):
     
    
- 
    
     
    
    
     
              self.url = 
      'https://xueqiu.com/'
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
              opt = webdriver.ChromeOptions()
     
    
- 
    
     
    
    
     
              opt.add_experimental_option(
      'w3c', 
      False)
     
    
- 
    
     
    
    
     
              self.browser = webdriver.Chrome(
      "chromedriver.exe", chrome_options=opt)
     
    
- 
    
     
    
    
     
              self.browser.maximize_window()
      #第一处修复,设置浏览器全屏
     
    
- 
    
     
    
    
     
              self.username = USERNAME
     
    
- 
    
     
    
    
     
              self.password = PASSWORD
     
    
- 
    
     
    
    
     
              self.wait = WebDriverWait(self.browser, 
      20)
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      __del__(
      self):
     
    
- 
    
     
    
    
     
              
      print(
      "close")
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      open(
      self):
     
    
- 
    
     
    
    
     
              self.browser.get(self.url)
     
    
- 
    
     
    
    
     
              ele = self.browser.find_element_by_xpath(
      '//*[@id="app"]/nav/div[1]/div[2]/div/div')
      #第二处修复,改xpath
     
    
- 
    
     
    
    
     
              ele.click()
     
    
- 
    
     
    
    
     
              username = self.wait.until(EC.presence_of_element_located((By.XPATH, 
      '//input[@name="username"]')))
     
    
- 
    
     
    
    
     
              pwd = self.wait.until(EC.presence_of_element_located((By.XPATH, 
      '//input[@name="password"]')))
     
    
- 
    
     
    
    
     
              username.send_keys(self.username)
     
    
- 
    
     
    
    
     
              time.sleep(
      2)
     
    
- 
    
     
    
    
     
              pwd.send_keys(self.password)
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      # 获取验证码按钮
     
    
- 
    
     
    
    
     
          
      def 
      get_yzm_button(
      self):
     
    
- 
    
     
    
    
     
              button = self.wait.until(EC.presence_of_element_located((By.XPATH, 
      '/html/body/div[2]/div[1]/div/div/div/div[2]/div[2]/div[2]')))
      #第三处修复,改xpath
     
    
- 
    
     
    
    
     
              
      return button
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      # 获取验证码图片对象
     
    
- 
    
     
    
    
     
          
      def 
      get_img_element(
      self):
     
    
- 
    
     
    
    
     
              element = self.wait.until(EC.presence_of_element_located((By.XPATH, 
      '//cavas[@name="geetest_canvas_bg geetest_absolute"]')))
     
    
- 
    
     
    
    
     
              
      return element
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      get_position(
      self):
     
    
- 
    
     
    
    
     
              
      # 获取验证码位置
     
    
- 
    
     
    
    
     
              element = self.get_img_element()
     
    
- 
    
     
    
    
     
              sleep(
      2)
     
    
- 
    
     
    
    
     
              location = element.location
     
    
- 
    
     
    
    
     
              size = element.size
     
    
- 
    
     
    
    
     
              top, bottom, left, right = location[
      'y'], location[
      'y'] + size[
      'height'], location[
      'x'], location[
      'x'] + size[
     
    
- 
    
     
    
    
     
                  
      'width']
     
    
- 
    
     
    
    
     
              
      return left, top, right, bottom
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      get_geetest_image(
      self):
     
    
- 
    
     
    
    
     
              
      """
     
    
- 
    
     
    
    
     
              获取验证码图片
     
    
- 
    
     
    
    
     
              :return: 图片对象
     
    
- 
    
     
    
    
     
              """
     
    
- 
    
     
    
    
     
              
      '''
     
    
- 
    
     
    
    
     
              <canvas class="geetest_canvas_bg geetest_absolute" height="160" width="260"></canvas>
     
    
- 
    
     
    
    
     
              '''
     
    
- 
    
     
    
    
     
              
      # 带阴影的图片
     
    
- 
    
     
    
    
     
              
      # im = self.wait.until(EC.presence_of_element_located((By.XPATH, '/html/body/div[4]/div[2]/div[6]/div/div[1]/div[1]/div/a/div[1]/div/canvas[1]')))
     
    
- 
    
     
    
    
     
              im = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 
      '.geetest_canvas_bg')))
     
    
- 
    
     
    
    
     
              time.sleep(
      2)
     
    
- 
    
     
    
    
     
              im.screenshot(
      'captcha.png')
     
    
- 
    
     
    
    
     
              
      # 执行 JS 代码并拿到图片 base64 数据
     
    
- 
    
     
    
    
     
              JS = 
      'return document.getElementsByClassName("geetest_canvas_fullbg")[0].toDataURL("image/png");'  
      # 不带阴影的完整图片
     
    
- 
    
     
    
    
     
              im_info = self.browser.execute_script(JS)  
      # 执行js文件得到带图片信息的图片数据
     
    
- 
    
     
    
    
     
              
      # 拿到base64编码的图片信息
     
    
- 
    
     
    
    
     
              im_base64 = im_info.split(
      ',')[
      1]
     
    
- 
    
     
    
    
     
              
      # 转为bytes类型
     
    
- 
    
     
    
    
     
              captcha1 = base64.b64decode(im_base64)
     
    
- 
    
     
    
    
     
              
      # 将图片保存在本地
     
    
- 
    
     
    
    
     
              
      with 
      open(
      'captcha1.png', 
      'wb') 
      as f:
     
    
- 
    
     
    
    
     
                  f.write(captcha1)
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
              JS = 
      'return document.getElementsByClassName("geetest_canvas_bg")[0].toDataURL("image/png");'
     
    
- 
    
     
    
    
     
              
      # 执行 JS 代码并拿到图片 base64 数据ng  # 带阴影的图片
     
    
- 
    
     
    
    
     
              im_info = self.browser.execute_script(JS)  
      # 执行js文件得到带图片信息的图片数据
     
    
- 
    
     
    
    
     
              
      # 拿到base64编码的图片信息
     
    
- 
    
     
    
    
     
              im_base64 = im_info.split(
      ',')[
      1]
     
    
- 
    
     
    
    
     
              
      # 转为bytes类型
     
    
- 
    
     
    
    
     
              captcha2 = base64.b64decode(im_base64)
     
    
- 
    
     
    
    
     
              
      # 将图片保存在本地
     
    
- 
    
     
    
    
     
              
      with 
      open(
      'captcha2.png', 
      'wb') 
      as f:
     
    
- 
    
     
    
    
     
                  f.write(captcha2)
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
              captcha1 = Image.
      open(
      'captcha1.png')
     
    
- 
    
     
    
    
     
              captcha2 = Image.
      open(
      'captcha2.png')
     
    
- 
    
     
    
    
     
              
      return captcha1, captcha2
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      # 获取网页截图
     
    
- 
    
     
    
    
     
          
      def 
      get_screen_shot(
      self):
     
    
- 
    
     
    
    
     
              screen_shot = self.browser.get_screenshot_as_png()
     
    
- 
    
     
    
    
     
              screen_shot = Image.
      open(BytesIO(screen_shot))
     
    
- 
    
     
    
    
     
              
      return screen_shot
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      get_yzm_img(
      self, name='captcha.png'):
     
    
- 
    
     
    
    
     
              
      #  获取验证码图片
     
    
- 
    
     
    
    
     
              left, top, right, bottom = self.get_position()
     
    
- 
    
     
    
    
     
              
      print(
      '验证码位置', top, bottom, left, right)
     
    
- 
    
     
    
    
     
              screen_shot = self.get_screen_shot()
     
    
- 
    
     
    
    
     
              captcha = screen_shot.crop((left, top, right, bottom))
     
    
- 
    
     
    
    
     
              captcha.save(name)
     
    
- 
    
     
    
    
     
              
      return captcha
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      get_slider(
      self):
     
    
- 
    
     
    
    
     
              
      # 获取滑块
     
    
- 
    
     
    
    
     
              
      # :return: 滑块对象
     
    
- 
    
     
    
    
     
              slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 
      'geetest_slider_track')))
     
    
- 
    
     
    
    
     
              
      return slider
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      get_gap(
      self, image1, image2):
     
    
- 
    
     
    
    
     
              
      """
     
    
- 
    
     
    
    
     
              获取缺口偏移量
     
    
- 
    
     
    
    
     
              :param image1: 不带缺口图片
     
    
- 
    
     
    
    
     
              :param image2: 带缺口图片
     
    
- 
    
     
    
    
     
              :return:
     
    
- 
    
     
    
    
     
              """
     
    
- 
    
     
    
    
     
              left = 
      62
     
    
- 
    
     
    
    
     
              
      for i 
      in 
      range(left, image1.size[
      0]):
     
    
- 
    
     
    
    
     
                  
      for j 
      in 
      range(image1.size[
      1]):
     
    
- 
    
     
    
    
     
                      
      if 
      not self.is_pixel_equal(image1, image2, i, j):
     
    
- 
    
     
    
    
     
                          left = i
     
    
- 
    
     
    
    
     
                          
      return left
     
    
- 
    
     
    
    
     
              
      # return left
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      is_pixel_equal(
      self, image1, image2, x, y):
     
    
- 
    
     
    
    
     
              
      """
     
    
- 
    
     
    
    
     
              判断两个像素是否相同
     
    
- 
    
     
    
    
     
              :param image1: 图片1
     
    
- 
    
     
    
    
     
              :param image2: 图片2
     
    
- 
    
     
    
    
     
              :param x: 位置x
     
    
- 
    
     
    
    
     
              :param y: 位置y
     
    
- 
    
     
    
    
     
              :return: 像素是否相同
     
    
- 
    
     
    
    
     
              """
     
    
- 
    
     
    
    
     
              
      # 取两个图片的像素点
     
    
- 
    
     
    
    
     
              pixel1 = image1.load()[x, y]
     
    
- 
    
     
    
    
     
              pixel2 = image2.load()[x, y]
     
    
- 
    
     
    
    
     
              threshold = 
      60
     
    
- 
    
     
    
    
     
              
      if 
      abs(pixel1[
      0] - pixel2[
      0]) < threshold 
      and 
      abs(pixel1[
      1] - pixel2[
      1]) < threshold 
      and 
      abs(
     
    
- 
    
     
    
    
     
                      pixel1[
      2] - pixel2[
      2]) < threshold:
     
    
- 
    
     
    
    
     
                  
      return 
      True
     
    
- 
    
     
    
    
     
              
      else:
     
    
- 
    
     
    
    
     
                  
      return 
      False
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      get_track(
      self, distance):
     
    
- 
    
     
    
    
     
              
      """
     
    
- 
    
     
    
    
     
              根据偏移量获取移动轨迹
     
    
- 
    
     
    
    
     
              :param distance: 偏移量
     
    
- 
    
     
    
    
     
              :return: 移动轨迹
     
    
- 
    
     
    
    
     
              """
     
    
- 
    
     
    
    
     
              
      # 初速度
     
    
- 
    
     
    
    
     
              v = 
      0
     
    
- 
    
     
    
    
     
              
      # 单位时间为0.2s来统计轨迹,轨迹即0.2内的位移
     
    
- 
    
     
    
    
     
              t = 
      0.3
     
    
- 
    
     
    
    
     
              
      # 位移/轨迹列表,列表内的一个元素代表0.2s的位移
     
    
- 
    
     
    
    
     
              tracks = []
     
    
- 
    
     
    
    
     
              
      # 当前的位移
     
    
- 
    
     
    
    
     
              current = 
      5
     
    
- 
    
     
    
    
     
              
      # 到达mid值开始减速
     
    
- 
    
     
    
    
     
              mid = distance * 
      3 / 
      5
     
    
- 
    
     
    
    
     
              
      while current < distance:
     
    
- 
    
     
    
    
     
                  
      if current < mid:
     
    
- 
    
     
    
    
     
                      
      # 加速度越小,单位时间的位移越小,模拟的轨迹就越多越详细
     
    
- 
    
     
    
    
     
                      a = 
      2
     
    
- 
    
     
    
    
     
                  
      else:
     
    
- 
    
     
    
    
     
                      a = -
      3
     
    
- 
    
     
    
    
     
                  
      # 初速度
     
    
- 
    
     
    
    
     
                  v0 = v
     
    
- 
    
     
    
    
     
                  
      # 0.2秒时间内的位移
     
    
- 
    
     
    
    
     
                  s = v0 * t + 
      0.4 * a * (t ** 
      2)
     
    
- 
    
     
    
    
     
                  
      # 当前的位置
     
    
- 
    
     
    
    
     
                  current += s
     
    
- 
    
     
    
    
     
                  
      # 添加到轨迹列表
     
    
- 
    
     
    
    
     
                  tracks.append(
      round(s))
     
    
- 
    
     
    
    
     
                  
      # 速度已经达到v,该速度作为下次的初速度
     
    
- 
    
     
    
    
     
                  v = v0 + a * t
     
    
- 
    
     
    
    
     
              
      return tracks
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      move_to_gap(
      self, slider, track):
     
    
- 
    
     
    
    
     
              
      """
     
    
- 
    
     
    
    
     
              拖动滑块到缺口处
     
    
- 
    
     
    
    
     
              :param slider: 滑块
     
    
- 
    
     
    
    
     
              :param track: 轨迹
     
    
- 
    
     
    
    
     
              :return:
     
    
- 
    
     
    
    
     
              """
     
    
- 
    
     
    
    
     
              ActionChains(self.browser).click_and_hold(slider).perform()
     
    
- 
    
     
    
    
     
              
      for x 
      in track:
     
    
- 
    
     
    
    
     
                  ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=
      0).perform()
     
    
- 
    
     
    
    
     
              time.sleep(
      0.5)
     
    
- 
    
     
    
    
     
              ActionChains(self.browser).release().perform()
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      shake_mouse(
      self):
     
    
- 
    
     
    
    
     
              
      """
     
    
- 
    
     
    
    
     
              模拟人手释放鼠标抖动
     
    
- 
    
     
    
    
     
              :return: None
     
    
- 
    
     
    
    
     
              """
     
    
- 
    
     
    
    
     
              ActionChains(self.browser).move_by_offset(xoffset=-
      2, yoffset=
      0).perform()
     
    
- 
    
     
    
    
     
              ActionChains(self.browser).move_by_offset(xoffset=
      2, yoffset=
      0).perform()
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      operate_slider(
      self, track):
     
    
- 
    
     
    
    
     
              
      '''
     
    
- 
    
     
    
    
     
              拖动滑块
     
    
- 
    
     
    
    
     
              '''
     
    
- 
    
     
    
    
     
              
      # 获取拖动按钮
     
    
- 
    
     
    
    
     
              back_tracks = [-
      1,-
      1, -
      1, -
      1]
     
    
- 
    
     
    
    
     
              slider_bt = self.browser.find_element_by_xpath(
      '//div[@class="geetest_slider_button"]')
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
              
      # 点击拖动验证码的按钮不放
     
    
- 
    
     
    
    
     
              ActionChains(self.browser).click_and_hold(slider_bt).perform()
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
              
      # 按正向轨迹移动
     
    
- 
    
     
    
    
     
              
      for i 
      in track:
     
    
- 
    
     
    
    
     
                  ActionChains(self.browser).move_by_offset(xoffset=i, yoffset=
      0).perform()
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
              time.sleep(
      1)
     
    
- 
    
     
    
    
     
              ActionChains(self.browser).release().perform()
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      get_cookies(
      self):
     
    
- 
    
     
    
    
     
              
      try:
     
    
- 
    
     
    
    
     
                  cookie_list = self.browser.get_cookies()
     
    
- 
    
     
    
    
     
                  cookie_dict = {i[
      'name']: i[
      'value'] 
      for i 
      in cookie_list}
     
    
- 
    
     
    
    
     
                  
      with 
      open(
      'xueqiu_cookies', 
      'w', encoding=
      'utf8')
      as f:
     
    
- 
    
     
    
    
     
                      cookie_dict = json.dumps(cookie_dict)
     
    
- 
    
     
    
    
     
                      f.write(cookie_dict)
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
                  
      return cookie_dict
     
    
- 
    
     
    
    
     
              
      except:
     
    
- 
    
     
    
    
     
                  
      print(
      "cookie 获取失败")
     
    
- 
    
     
    
    
     
                  
      return 
      None
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      # 读取cookie
     
    
- 
    
     
    
    
     
          
      def 
      return_cookie(
      self):
     
    
- 
    
     
    
    
     
              cookies = 
      ''
     
    
- 
    
     
    
    
     
              
      with 
      open(
      'xueqiu_cookies', 
      'r')
      as f:
     
    
- 
    
     
    
    
     
                  cookie = f.read()[
      1:-
      1]
     
    
- 
    
     
    
    
     
                  cookie = cookie.split(
      ', ')
     
    
- 
    
     
    
    
     
                  
      for i 
      in cookie:
     
    
- 
    
     
    
    
     
                      cook = i.split(
      ': ')
     
    
- 
    
     
    
    
     
                      cookies += cook[
      0][
      1:-
      1] + 
      '=' + cook[
      1][
      1:-
      1] + 
      ';'
     
    
- 
    
     
    
    
     
                  
      return cookies
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      def 
      run(
      self):
     
    
- 
    
     
    
    
     
              
      # 破解入口
     
    
- 
    
     
    
    
     
              self.
      open(), sleep(
      3)
     
    
- 
    
     
    
    
     
              self.get_yzm_button().click(), sleep(
      2)
      # 点击验证按钮
     
    
- 
    
     
    
    
     
              
      # 点按呼出缺口
     
    
- 
    
     
    
    
     
              slider = self.get_slider()
     
    
- 
    
     
    
    
     
              
      # slider.click()
     
    
- 
    
     
    
    
     
              
      # 获取带缺口的验证码图片
     
    
- 
    
     
    
    
     
              image1, image2 = self.get_geetest_image()
     
    
- 
    
     
    
    
     
              gap = self.get_gap(image1, image2)
     
    
- 
    
     
    
    
     
              
      print(
      '缺口位置', gap)
     
    
- 
    
     
    
    
     
              track = self.get_track(gap)
     
    
- 
    
     
    
    
     
              
      print(
      '滑动轨迹', track)
     
    
- 
    
     
    
    
     
              self.operate_slider(track)
     
    
- 
    
     
    
    
     
              
      # 判定是否成功
     
    
- 
    
     
    
    
     
              time.sleep(
      8)
     
    
- 
    
     
    
    
     
              
      try:
     
    
- 
    
     
    
    
     
                  elem = self.wait.until(
     
    
- 
    
     
    
    
     
                      EC.text_to_be_present_in_element((By.CLASS_NAME, 
      'nav__btn--longtext'), 
      '发帖'))
     
    
- 
    
     
    
    
     
                  
      if elem:
     
    
- 
    
     
    
    
     
                      cookie = self.get_cookies()
     
    
- 
    
     
    
    
     
                  
      else:
     
    
- 
    
     
    
    
     
                      
      print(
      "get cookies errors")
     
    
- 
    
     
    
    
     
              
      except Exception 
      as e:
     
    
- 
    
     
    
    
     
                  
      print(e, 
      'fail! ')
     
    
- 
    
     
    
    
     
                  time.sleep(
      3)
     
    
- 
    
     
    
    
     
                  self.run()
     
    
- 
    
     
    
    
     
              
      finally:
     
    
- 
    
     
    
    
     
                  self.browser.quit()
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      if __name__ == 
      '__main__':
     
    
- 
    
     
    
    
     
          crack = Login()
     
    
- 
    
     
    
    
     
          crack.run()
     
    
 代码我也上传到 Github 上了,代码的后续更新维护会放在这里,建议 Star 收藏下。
https://github.com/Jack-Cherish/quantitative
数据获取
等待模拟登录完成后,会保存一个名为 xueqiu_cookies 的文件。

这里保存的是帐号的 Cookie,使用这个 Cookie 就能获取雪球的数据了。
比如,获取一下股票实时行情和现金流量表,就可以这样写:
  
   - 
    
     
    
    
     
      #-*- coding:utf-8 -*-
     
    
- 
    
     
    
    
     
      import requests
     
    
- 
    
     
    
    
     
      import json
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      def 
      fetch(
      url, xq_a_token):
     
    
- 
    
     
    
    
     
          headers = {
      'Host': 
      'stock.xueqiu.com',
     
    
- 
    
     
    
    
     
              
      'Accept': 
      'application/json',
     
    
- 
    
     
    
    
     
              
      'Cookie': 
      'xq_a_token={};'.
      format(xq_a_token),
     
    
- 
    
     
    
    
     
              
      'User-Agent': 
      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
     
    
- 
    
     
    
    
     
              
      'Accept-Language': 
      'zh-Hans-CN;q=1, ja-JP;q=0.9',
     
    
- 
    
     
    
    
     
              
      'Accept-Encoding': 
      'br, gzip, deflate',
     
    
- 
    
     
    
    
     
              
      'Connection': 
      'keep-alive'}
     
    
- 
    
     
    
    
     
          response = requests.get(url, headers = headers)
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      if response.status_code != 
      200:
     
    
- 
    
     
    
    
     
              
      raise Exception(response.content)
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
          
      return json.loads(response.content)
     
    
- 
    
     
    
    
      
     
    
- 
    
     
    
    
     
      if __name__ == 
      '__main__':
     
    
- 
    
     
    
    
     
          
      # 获取股票 SH600000 实时行情
     
    
- 
    
     
    
    
     
          url = 
      "http://stock.xueqiu.com/v5/stock/quote.json?extend=detail&symbol=SH600000&count=10"
     
    
- 
    
     
    
    
     
          
      with 
      open(
      "xueqiu_cookies", 
      "r") 
      as f:
     
    
- 
    
     
    
    
     
              cookies_info = json.load(f)
     
    
- 
    
     
    
    
     
          res = fetch(url, cookies_info[
      'xq_a_token'])
     
    
- 
    
     
    
    
     
          
      print(res)
     
    
- 
    
     
    
    
     
          
      # 获取股票 SH600000 现金流量表
     
    
- 
    
     
    
    
     
          url = 
      "http://stock.xueqiu.com/v5/stock/finance/cn/cash_flow.json?symbol=SH600000&count=10"
     
    
- 
    
     
    
    
     
          
      with 
      open(
      "xueqiu_cookies", 
      "r") 
      as f:
     
    
- 
    
     
    
    
     
              cookies_info = json.load(f)
     
    
- 
    
     
    
    
     
          res = fetch(url, cookies_info[
      'xq_a_token'])
     
    
- 
    
     
    
    
     
          
      print(res)
     
    
 运行结果:

有了 Cookie,很多接口数据都能获取,实时行情、实时分笔、业绩预告、机构评级、资金流向趋势、资金流向历史、资金成交分布、大宗交易、融资融券、业绩指标、利润表、资产负债表、现金流量表、主营业务构成、F10 十大股东、F10 主要指标等等。
这些数据,都能获取。
絮叨
篇幅有限,今天就是带大家小小实战下。
后续我会完善各个常用查询接口,方便大家获取各类数据,用于量化分析。
万事开头难,先弄好数据,再看量化策略~
如果喜欢这类的内容,记得点赞,喜欢的人多的话,我会快速加更的~
最后必须提醒一下各位:
获取数据,请温柔,请勿高并发获取,且用且珍惜。
对了,还有不少小伙伴问我,我的量化策略收益如何。
去年的五万元实验,最后浮盈不到 10%,清仓之后就换新的策略实验了。
6月份的时候,又用上了新策略,新的策略一直跑到今年 10 月份,也就这样:

实验没放多少钱,随便玩玩,你觉得,这点收益如何?
好了,今天就聊这么多吧,我是 Jack,我们下期见~
转载:https://blog.csdn.net/c406495762/article/details/128267619
 
					