一、前言


     前段时间尝试爬取了网易云音乐的歌曲,这次打算爬取QQ音乐的歌曲信息。网易云音乐歌曲列表是通过iframe展示的,可以借助Selenium获取到iframe的页面元素,

  而QQ音乐采用的是异步加载的方式,套路不一样,这是主流的页面加载方式,爬取有点难度,不过也是对自己的一个挑战。

二、Python爬取QQ音乐单曲


之前看的慕课网的一个视频, 很好地讲解了一般编写爬虫的步骤,我们也按这个来。

          爬虫步骤

1.确定目标

首先我们要明确目标,本次爬取的是QQ音乐歌手刘德华的单曲。

(百度百科)->分析目标(策略:url格式(范围)、数据格式、网页编码)->编写代码->执行爬虫

2.分析目标

歌曲链接:https://y.qq.com/n/yqq/singer/003aQYLo2x8izP.html#tab=song&

从左边的截图可以知道单曲采用分页的方式排列歌曲信息,每页显示30条,总共30页。点击页码或者最右边的">"会跳转到下一页,浏览器会向服务器发送ajax异步请求,从链接可以看到begin和num参数,分别代表起始歌曲下标(截图是第2页,起始下标是30)和一页返回30条,服务器响应返回json格式的歌曲信息(MusicJsonCallbacksinger_track({"code":0,"data":{"list":[{"Flisten_count1":......]})),如果只是单独想获取歌曲信息,可以直接拼接链接请求和解析返回的json格式的数据。这里不采用直接解析数据格式的方法,我采用的是Python Selenium方式,每获取和解析完一页的单曲信息,点击 ">" 跳转到下一页继续解析,直至解析并记录所有的单曲信息。最后请求每个单曲的链接,获取详细的单曲信息。

右边的截图是网页的源码,所有歌曲信息都在类名为mod_songlist的div浮层里面,类名为songlist_list的无序列表ul下,每个子元素li展示一个单曲,类名为songlist__album下的a标签,包含单曲的链接,名称和时长等。

3.编写代码

1)下载网页内容,这里使用Python 的Urllib标准库,自己封装了一个download方法:

 def download(url, user_agent='wswp', num_retries=2):
if url is None:
return None
print('Downloading:', url)
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
request = urllib.request.Request(url, headers=headers) # 设置用户代理wswp(Web Scraping with Python)
try:
html = urllib.request.urlopen(request).read().decode('utf-8')
except urllib.error.URLError as e:
print('Downloading Error:', e.reason)
html = None
if num_retries > 0:
if hasattr(e, 'code') and 500 <= e.code < 600:
# retry when return code is 5xx HTTP erros
return download(url, num_retries-1) # 请求失败,默认重试2次,
return html

2)解析网页内容,这里使用第三方插件BeautifulSoup,具体可以参考BeautifulSoup API 。

 def music_scrapter(html, page_num=0):
try:
soup = BeautifulSoup(html, 'html.parser')
mod_songlist_div = soup.find_all('div', class_='mod_songlist')
songlist_ul = mod_songlist_div[1].find('ul', class_='songlist__list')
'''开始解析li歌曲信息'''
lis = songlist_ul.find_all('li')
for li in lis:
a = li.find('div', class_='songlist__album').find('a')
music_url = a['href'] # 单曲链接
urls.add_new_url(music_url) # 保存单曲链接
# print('music_url:{0} '.format(music_url))
print('total music link num:%s' % len(urls.new_urls))
next_page(page_num+1)
except TimeoutException as err:
print('解析网页出错:', err.args)
return next_page(page_num + 1)
return None
 def get_music():
try:
while urls.has_new_url():
# print('urls count:%s' % len(urls.new_urls))
'''跳转到歌曲链接,获取歌曲详情'''
new_music_url = urls.get_new_url()
print('url leave count:%s' % str( len(urls.new_urls) - 1))
html_data_info = download(new_music_url)
# 下载网页失败,直接进入下一循环,避免程序中断
if html_data_info is None:
continue
soup_data_info = BeautifulSoup(html_data_info, 'html.parser')
if soup_data_info.find('div', class_='none_txt') is not None:
print(new_music_url, ' 对不起,由于版权原因,暂无法查看该专辑!')
continue
mod_songlist_div = soup_data_info.find('div', class_='mod_songlist')
songlist_ul = mod_songlist_div.find('ul', class_='songlist__list')
lis = songlist_ul.find_all('li')
del lis[0] # 删除第一个li
# print('len(lis):$s' % len(lis))
for li in lis:
a_songname_txt = li.find('div', class_='songlist__songname').find('span', class_='songlist__songname_txt').find('a')
if 'https' not in a_songname_txt['href']: #如果单曲链接不包含协议头,加上
song_url = 'https:' + a_songname_txt['href']
song_name = a_songname_txt['title']
singer_name = li.find('div', class_='songlist__artist').find('a').get_text()
song_time =li.find('div', class_='songlist__time').get_text()
music_info = {}
music_info['song_name'] = song_name
music_info['song_url'] = song_url
music_info['singer_name'] = singer_name
music_info['song_time'] = song_time
collect_data(music_info)
except Exception as err: # 如果解析异常,跳过
print('Downloading or parse music information error continue:', err.args)

4.执行爬虫

爬虫跑起来了,一页一页地去爬取专辑的链接,并保存到集合中,最后通过get_music()方法获取单曲的名称,链接,歌手名称和时长并保存到Excel文件中。

三、Python爬取QQ音乐单曲总结


1.单曲采用的是分页方式,切换下一页是通过异步ajax请求从服务器获取json格式的数据并渲染到页面,浏览器地址栏链接是不变的,不能通过拼接链接来请求。一开始想过都通过Python Urllib库来模拟ajax请求,后来想想还是用Selenium。Selenium能够很好地模拟浏览器真实的操作,页面元素定位也很方便,模拟单击下一页,不断地切换单曲分页,再通过BeautifulSoup解析网页源码,获取单曲信息。

2.url链接管理器,采用集合数据结构来保存单曲链接,为什么要使用集合?因为多个单曲可能来自同一专辑(专辑网址一样),这样可以减少请求次数。

 class UrlManager(object):
def __init__(self):
self.new_urls = set() # 使用集合数据结构,过滤重复元素
self.old_urls = set() # 使用集合数据结构,过滤重复元素
def add_new_url(self, url):
if url is None:
return
if url not in self.new_urls and url not in self.old_urls:
self.new_urls.add(url) def add_new_urls(self, urls):
if urls is None or len(urls) == 0:
return
for url in urls:
self.add_new_url(url) def has_new_url(self):
return len(self.new_urls) != 0 def get_new_url(self):
new_url = self.new_urls.pop()
self.old_urls.add(new_url)
return new_url
 

3.通过Python第三方插件openpyxl读写Excel十分方便,把单曲信息通过Excel文件可以很好地保存起来。

 def write_to_excel(self, content):
try:
for row in content:
self.workSheet.append([row['song_name'], row['song_url'], row['singer_name'], row['song_time']])
self.workBook.save(self.excelName) # 保存单曲信息到Excel文件
except Exception as arr:
print('write to excel error', arr.args)

四、后语


最后还是要庆祝下,毕竟成功把QQ音乐的单曲信息爬取下来了。本次能够成功爬取单曲,Selenium功不可没,这次只是用到了selenium一些简单的功能,后续会更加深入学习Selenium,不仅在爬虫方面还有UI自动化。

后续还需要优化的点:

1.下载的链接比较多,一个一个下载起来比较慢,后面打算用多线程并发下载。

2.下载速度过快,为了避免服务器禁用IP,后面还要对于同一域名访问过于频繁的问题,有个等待机制,每个请求之间有个等待间隔。

3. 解析网页是一个重要的过程,可以采用正则表达式,BeautifulSoup和lxml,目前采用的是BeautifulSoup库, 在效率方面,BeautifulSoup没lxml效率高,后面会尝试采用lxml。

Python爬虫小白---(二)爬虫基础--Selenium PhantomJS的更多相关文章

  1. [Python爬虫] 之二十七:Selenium +phantomjs 利用 pyquery抓取今日头条视频

    一.介绍 本例子用Selenium +phantomjs爬取今天头条视频(http://www.tvhome.com/news/)的信息,输入给定关键字抓取图片信息. 给定关键字:视频:融合:电视 二 ...

  2. [Python爬虫] 之二十三:Selenium +phantomjs 利用 pyquery抓取智能电视网数据

    一.介绍 本例子用Selenium +phantomjs爬取智能电视网(http://news.znds.com/article/news/)的资讯信息,输入给定关键字抓取资讯信息. 给定关键字:数字 ...

  3. [Python爬虫] 之二十一:Selenium +phantomjs 利用 pyquery抓取36氪网站数据

    一.介绍 本例子用Selenium +phantomjs爬取36氪网站(http://36kr.com/search/articles/电视?page=1)的资讯信息,输入给定关键字抓取资讯信息. 给 ...

  4. [Python爬虫] 之二十:Selenium +phantomjs 利用 pyquery通过搜狗搜索引擎数据

    一.介绍 本例子用Selenium +phantomjs 利用 pyquery通过搜狗搜索引擎数据()的资讯信息,输入给定关键字抓取资讯信息. 给定关键字:数字:融合:电视 抓取信息内如下: 1.资讯 ...

  5. [Python爬虫] 之十五:Selenium +phantomjs根据微信公众号抓取微信文章

    借助搜索微信搜索引擎进行抓取 抓取过程 1.首先在搜狗的微信搜索页面测试一下,这样能够让我们的思路更加清晰 在搜索引擎上使用微信公众号英文名进行“搜公众号”操作(因为公众号英文名是公众号唯一的,而中文 ...

  6. [Python爬虫] 之十九:Selenium +phantomjs 利用 pyquery抓取超级TV网数据

    一.介绍 本例子用Selenium +phantomjs爬取超级TV(http://www.chaojitv.com/news/index.html)的资讯信息,输入给定关键字抓取资讯信息. 给定关键 ...

  7. [Python爬虫] 之十八:Selenium +phantomjs 利用 pyquery抓取电视之家网数据

    一.介绍 本例子用Selenium +phantomjs爬取电视之家(http://www.tvhome.com/news/)的资讯信息,输入给定关键字抓取资讯信息. 给定关键字:数字:融合:电视 抓 ...

  8. [Python爬虫] 之十六:Selenium +phantomjs 利用 pyquery抓取一点咨询数据

    本篇主要是利用 pyquery来定位抓取数据,而不用xpath,通过和xpath比较,pyquery效率要高. 主要代码: # coding=utf-8 import os import re fro ...

  9. [Python爬虫] 之十四:Selenium +phantomjs抓取媒介360数据

    具体代码如下: # coding=utf-8import osimport refrom selenium import webdriverimport selenium.webdriver.supp ...

随机推荐

  1. JTable常见用法细则+设置某列可编辑+滚动表格

    JTable常见用法细则 JTable是Swing编程中很常用的控件,这里总结了一些常用方法以备查阅.欢迎补充,转载请注明作者与出处. 一.创建表格控件的各种方式: 1)  调用无参构造函数. JTa ...

  2. 对类对象使用new时地址分配的情况

    我们知道,string类内部的构造函数是采用new来分配地址的.当创建对象时,会调用string的构造函数,从而实质上也使用了new.那么问题来了,如果我用new再创建一个string类型的指针呢?下 ...

  3. php原生curl接口的请求

    /** * @desc 接口请求处理 * @date 2017/5/19 11:39 * @param [$url请求的接口地址,$way为false为get请求,true为post请求] * @au ...

  4. axis2 和 xfire 接口调用问题排查

    背景: 1个运营商厂家开发人员离职,我们为了上线对接接口,迁就对方客户端调用.对方客户端框架用的是xfire.调用方式基本为:   Service serviceModel = new ObjectS ...

  5. Python一切皆对象

    Python从设计之初就是一门面向对象的语言,它有一个重要的概念,即一切皆对象. Java虽然也是面向对象编程的语言,但是血统没有Python纯正.比如Java的八种基本数据类型之一int,在持久化的 ...

  6. 关于php内存释放问题 内存溢出问题(二)

    今天抽了一上午时间,来看了看之前解决过内存问题的代码,相对来说,我对自己代码的优化程序非常不满意,一次性导入四万条数据就使代码变得如此繁琐,我想这不是根本的解决方法.通过网上检索,对问题有进一步的分析 ...

  7. Thrift总结(一)介绍

    这段时间,一直在整理公司的内部 rpc 服务接口,面临的一个问题就是:由于公司内部的系统由几个不同的语言编写的.C# ,java,node.js 等,如何实现这些内部系统之间的接口统一调用,确实是比较 ...

  8. cpp(第四章)

    1.索引比数组长度少1: 2.c++中不能数组赋给另一个数组:只能定义时才能使用初始化: 3.c++11中{}内为空,默认赋值为0,而c++中{}如果只对部分初始化,其他部分将被设置为0:c++11使 ...

  9. JDBC加载数据库驱动的方式

    JDBC作为数据库访问的规范接口,其中只是定义一些接口.具体的实现是由各个数据库厂商来完成. 一.重要的接口: 1.public interface Driver 每个驱动程序类必须实现的接口.Jav ...

  10. 浅谈JavaScript递归

    递归:是指函数/过程/子程序在运行过程序中直接或间接调用自身而产生的重入现象.递归指的是一个过程:函数不断引用自身,直到引用的对象已知. //公园里面有200个桃子,每天吃掉一半,扔掉一个烂的,第6天 ...