来自腾讯云

接《 爬虫实战:爬虫之 web 自动化终极杀手 ( 上)

再理一下方案步骤:

  1. 模拟用户登录
  2. 进入个人播放页
  3. 获取cookies
  4. 获取token cookie,并将其添加到headers中。
  5. 请求页面,获取数据

代码

  def login(driver):
      spotify_name = 'manaxiaomeimei'
      spotify_pass = 'dajiagongyong'
      spotify_login = 'https://accounts.spotify.com/en/login'
      spotify_overview = 'https://www.spotify.com/us/account/overview/'

      # 模拟用户登录()
      # 浏览器driver访问登录url
      driver.get(spotify_login)
      # 休息一下等待网页加载。(还有另一种方式:driver.implicitly_wait(3))
      time.sleep(3)

      # 获取页面元素对象方法(本次使用如下):
      #   find_element_by_id : 通过标签id获取元素对象 可在页面中获取到唯一一个元素,因为在html规范中。一个DOM树中标签id不能重复
      #   find_element_by_class_name : 通过标签类名获取元素对象,可能会重复(有坑)
      #   find_element_by_xpath : 通过标签xpath获取元素对象,类同id,可获取唯一一个元素。
      # 获取页面元素对象--用户名
      username = driver.find_element_by_id('login-username')
      # username.clear()
      # 坑:获取页面元素对象--密码
      #   在通过类名获取标签元素中,遇到了无法定位复合样式,这时候可采用仅选取最后一个使用的样式作为参数,即可(稳定性不好不建议使用。尽量使用by_id)
      # password = driver.find_element_by_class_name('form-control input-with-feedback ng-dirty ng-valid-parse ng-touched ng-empty ng-invalid ng-invalid-required')
      password = driver.find_element_by_id('login-password')
      # password.clear()
      # 获取页面元素对象--登录按钮
      login_button = driver.find_element_by_xpath('/html/body/div[2]/div/form/div[3]/div[2]/button')
      # 通过WebDriver API调用模拟键盘的输入用户名
      username.send_keys(spotify_name)
      # 通过WebDriver API调用模拟键盘的输入密码
      password.send_keys(spotify_pass)
      # 通过WebDriver API调用模拟鼠标的点击操作,进行登录
      login_button.click()
      # 休息一下等待网页加载
      driver.implicitly_wait(3)

  def enter_web_player(driver):
      '''
      进入web 播放器
      :param driver:浏览器控制对象
      :return:
      '''

      # 请求个人信息页
      spotify_overview = 'https://www.spotify.com/us/account/overview/'
      driver.get(spotify_overview)
      time.sleep(3)

      web = driver.find_element_by_id('nav-link-play')
      web.click()
      time.sleep(3)

  def spotify_view(song_name):
      '''
      使用自动化工具获取网页数据
      :param url: 待获取网页url
      :return: 页面数据
      '''

      # 初始化浏览器driver
      driver = webdriver.Chrome()

      # 用户登录
      login(driver)

      # 进入web播放页
      enter_web_player(driver)

      # 搜索打开歌曲url
      spotify_song = 'https://open.spotify.com/search/results/{song_name}'
      spotify_song = spotify_song.format(song_name=song_name)

      driver.get(spotify_song)
      time.sleep(5)
      # 搜索获取网页代码并返回
      html = driver.page_source
      return html

通过上边的代码我们可以获取到网页完整的数据,再使用BeautifulSoup对源代码进行解析获取数据内容就可以了。可是在想一下。我们利用浏览器自动化进行操作的时候效率很低,相比于调用接口的方法获取数据慢很多,并且在对数据进行解析的时候会比较麻烦,到这里我准备使用第二个方案再次进行完成这个需求。不多说动手吧。

方案2:

在方案1的研究基础上,方案2仅针对获取数据的部分进行更改,使用访问数据接口,获取数据并解析。

实现步骤:

  1. 获取登录并进入播放页获取token,和其他cookies
  2. 请求数据接口
  3. 解析返回数据

代码实现:

  def get_cookies(driver):
      '''
      收集cookies
      :param driver: 浏览器控制对象
      :return: cookies字典
      '''
      # 从浏览器获取cookies
      cookies = driver.get_cookies()
      # 解析cookies并返回结果
      cookies_dict = {}
      for c in cookies:
          cookies_dict[c['name']] = c['value']

      return cookies_dict

  def analyse(data ,songInfoList,albumInfoList):
      # 获取response的json()
      data = data.json()
      # 解析json
      songs = data.get('tracks', None)
      albums = data.get('albums', None)

      # 省略对json数据解析。注意判断None,将解析到是数据保存在两个list中即可。

  def search_spotify_song(song_name):                        
      '''
      使用自动化工具获取网页数据
      :param url: 待获取网页url
      :return: 页面数据
      '''

      # 初始化浏览器driver
      driver = webdriver.Chrome()

      # 用户登录(同上)
      login(driver)

      # 进入web播放页(同上)
      enter_web_player(driver)

      # 获取cookies
      cookies = get_cookies()

      '''
      搜索spotify歌曲,专辑
      :param song_name: 待搜索歌曲名
      :return: 歌曲、专辑搜索结果
      '''
      url = 'https://api.spotify.com/v1/search?type=album%2Cartist%2Cplaylist%2Ctrack&q={song_name}&decorate_restrictions=true&best_match=true&limit=50&market=from_token',

      url = url.format(song_name=song_name)
      protocol, s1 = urllib.splittype(url)
      host, s2 = urllib.splithost(s1)

      # 伪装浏览器,避免被kill
      headers = {
          'Host': host,
          'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36',
          'Accept': 'application/json',
          'Accept-Encoding': 'gzip, deflate, br',
          'Connection': 'keep-alive',
          'Accept-Language': 'zh-CN,zh;q=0.8',
          'Origin': 'https://open.spotify.com',
          'Referer': 'https://open.spotify.com/search/results/{song_name}'.format(
              song_name=song_name.decode('utf-8')),
          # 组装认证信息,解决token错误 
          'Authorization': 'Bearer %s' % cookies["wp_access_token"]
      }

      # 代理
      proxies = {
          "http": "dev-proxy.oa.com:8080",
          "https": "dev-proxy.oa.com:8080",
      }

      try:
      res = requests.get(url1, headers=headers, cookies=cookies, proxies=proxies)
           if res.status.code == 200:
              analyse(res ,songInfoList,albumInfoList)
      except Exception, e:
          print e
      return songInfoList[0:30]

至此我们已经获取到了spotif平台的结果数据。同样获取到了网易和百度的数据信息。

数据存储实战

到这里我们就需要将获取到的数据进行保存了。本次才用excel保存。

这里介绍两个excel操作库:

xlrd:读取excel

  • open_workbook(filename) 读取excel文件,返回excel对象

  • sheet_by_index(sheetx) 根据工作sheet下标读取,返回sheet对象

  • sheet_by_name(sheet_name) 根据工作sheet名字读取,返回sheet对象

  • sheet.nrows 工作sheet行数

  • row_values(rowx, start_colx=0, end_colx=None),返回rowx行
    数据(list)

  • xlwt:写excel

  • xlwt.Workbook(encoding=’utf-8’) 创建一个excel表格对象,指定编码方式,建议windows用户开启。

  • add_sheet(sheet_name) 添加一个sheet

  • write(r, c, label=””) 向sheet写入数据

  • write_merge(r1, r2, c1, c2, label=””) 向sheet合并单元格并写入数据,前四个参数控制区块:起始行 结束行 起始列 结束列

避免给同一个单元格重复写内容

基于此我们就可以将获取到的数据进行保存了。

excel表格操作代码:

  def read_excel_by_name(self, file='file.xls', by_name=u'Sheet1'):
      '''
      根据名称获取Excel表格中的数据
      :param file: Excel文件
      :param by_name: 默认从Sheet1中取数据 
      :return: 返回表格数据(也可按照表格定义对象进行列于对象进行映射)
      '''
      # 打开excel表格
      excel = Excel().open_excel(file)
      # 获取工作Sheet
      table = excel.sheet_by_name(by_name)
      # 获取工作sheet行数
      nrows = table.nrows  # 行数
      list = []
      # 遍历每行数据进行读取
      for rownum in range(0, nrows):
          row = table.row_values(rownum)
          if row:
              search_str = row[1]
              list.append(search_str)
      # 返回结果list
      return list

  def create_excel(self):
      '''
      创建excel表格
      '''
      excel_file = xlwt.Workbook(encoding='utf-8')

  def write_song(self, sheet, datas={}, l=1):
      '''
      写入数据到sheet中
      :param sheet: 工作sheet
      :param datas:待写入的数据
      :param l: 开始行数
      :return:
      '''
      max_l = 0
      if l == 0:
          sheet.write_merge(0, 1, 0, 0, u'查找歌曲名')
          sheet.write_merge(0, 0, 1, 3, u'百度')
          sheet.write(1, 1, u'歌曲名')
          sheet.write(1, 2, u'歌手')
          sheet.write(1, 3, u'专辑')
          sheet.write_merge(0, 0, 4, 6, u'网易')
          sheet.write(1, 4, u'歌曲名')
          sheet.write(1, 5, u'歌手')
          sheet.write(1, 6, u'专辑')
          sheet.write_merge(0, 0, 7, 9, u'spotify')
          sheet.write(1, 7, u'歌曲名')
          sheet.write(1, 8, u'歌手')
          sheet.write(1, 9, u'专辑')
      else:
          baidu_song = datas.get('baidu_song', {})
          wangyi_song = datas.get('wangyi_song', {})
          spotify_song = datas.get('spotify_song', {})

          if len(baidu_song) > 0:
              ll = l
              for data in qq_music_song:
                  sheet.write(ll, 1, data.song_name.encode('utf8'))
                  sheet.write(ll, 2, data.singer.encode('utf8'))
                  sheet.write(ll, 3, data.album.encode('utf8'))
                  ll += 1
              if max_l < ll:
                  max_l = ll
          if len(wangyi_song) > 0:
              ll = l
              for data in xiami_song:
                  sheet.write(ll, 4, data.song_name.encode('utf8'))
                  sheet.write(ll, 5, data.singer.encode('utf8'))
                  sheet.write(ll, 6, data.album.encode('utf8'))
                  ll += 1
              if max_l < ll:
                  max_l = ll
          if len(spotify_song) > 0:
              ll = l
              for data in wangyi_song:
                  sheet.write(ll, 7, data.song_name.encode('utf8'))
                  sheet.write(ll, 8, data.singer.encode('utf8'))
                  sheet.write(ll, 9, data.album.encode('utf8'))
                  ll += 1
              if max_l < ll:
                  max_l = ll

主程序:

  def main():
      # 数据写入开始行
      start_line = 2
      # 数据写入结束行
      end_line = 2
      # 读取待搜歌歌曲名excel
      list = read_excel_by_name(file='search_song.xls', sheet='song')
      # 创建搜索结果excel
      excel = create_excel()
      # 添加工作sheet, 名为 music        
      song_sheet = excel.add_sheet(u'music')
      # 写入表格标题栏
      write_song(song_sheet, l=0)

      # 遍历待搜索歌曲名。并存储搜索结果。    
      for song in list:
          baidu_song = search_baidu_song(song)
          wangyi_song = search_163_song(song)
          spotify_song = search_spotify_song(song)

          song_datas = {
              'baidu_song': baidu_song if baidu_song else {},
              'wangyi_song': wangyi_song if wangyi_song else {},
              'spotify_song': spotify_song if spotify_song else {}
          }

          # 写入单个单元格
          end_line = write_song(song_sheet, datas=song_datas, l=start_line)

          if start_line <= end_line:
              # 合并写入单元格
              song_sheet.write_merge(start_line, end_line, 0, 0, name)
              start_line = end_line + 1
  结果excel()

本文采用根据完成功能进行的学习,并逐步完善代码,本文记叙了我完成这个项目的过程,目前只关注功能的实现,对于工具原理并未涉及过多。在本次爬虫编写中,主要遇到的问题是在网易云音乐url加密解析的理解和破解能力。在上文贴出的代码可能有运行不成功的可能,这是从项目提取出来的相关代码。 如有错误请多多指教!

涉及的环境和工具:

python 2.7.13:开发环境
Pychcharm:IDE
pip包管理工具:python 包管理工具
urllib库:python内置的HTTP请求库
requests库:封装后的urllib库,使用更加方便
Beautiful Soup库:提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。
selenium库:可以模拟真实浏览器,自动化测试工具,支持多种浏览器,爬虫中主要用来解决JavaScript渲染问题。
PhantomJS浏览器:PhantomJS是一个基于webkit的JavaScript API。它使用QtWebKit作为它核心浏览器的功能,使用webkit来编译解释执行JavaScript代码。
ChromeDriver驱动:ChromeDriver是Chromium team开发维护的,它是实现WebDriver有线协议的一个单独的服务。ChromeDriver通过chrome的自动代理框架控制浏览器。
xlrd库:excel读取工具
xlwt库:excel写入工具
9/17/2017 2:25:41 PM 完


原文地址


双11红包