用23行代码爬取豆瓣音乐top250
豆瓣对于爬虫十分友好,而且豆瓣上面又有各种电影图书音乐等资源,是我们学习爬虫一个很不错的锻炼目标。基本上每个学习爬虫的人都会拿豆瓣来练练手。
网上有各种爬取豆瓣电影top250的教程,虽然豆瓣音乐top250和豆瓣电影top250的爬取十分类似,但是我大致对比了一下,我这种方法应该是最简单的,仅需要23行代码。好吧,如果你有更简单的方法,欢迎给我评论留言交流交流,共同进步。
抓取目标:豆瓣音乐top250的歌名、作者(专辑)、评分和歌曲链接
使用工具:requests + lxml + xpath。
我认为这种工具组合是最适合初学者的,requests比python自带的urllib库好用,功能更强大。关于requests的使用方法,建议看它的官方文档: http://docs.python-requests.org/zh_CN/latest/user/quickstart.html
使用lxml来解析网页,速度是最快的,至少比BeatifulSoup快。关于lxml的使用方法,建议看这个:http://lxml.de/
而xpath更是傻白甜的使用方式:直接在浏览器中复制即可,如果想要掌握xpath更具体的语法,建议看w3school的xpath教程:http://www.w3school.com.cn/xpath/
首先目标url是:https://music.douban.com/top250?start=0
点击下一页,我们多观察几个页面的url:
我们会发现url的start=是以25的倍数增长的,起始为start=0,然后是start=25,每页25首音乐,一共十页。
所以,我们可以使用一个for循环来得出这十个页面的url:
for i in range(10):
# 一共有10个页面,每个页面25首音乐
url = "https://music.douban.com/top250?start={}".format(i * 25)
关于format()函数的使用方法,可以看菜鸟教程上的讲解:http://www.runoob.com/python/att-string-format.html
关于range()函数的使用方法,可以看菜鸟教程上的讲解:http://www.runoob.com/python/python-func-range.html
然后我们使用requests.get()来进行网页请求:
import requests
headers = {"User_Agent": "Mozilla/5.0(compatible; MSIE 5.5; Windows 8)"}
data = requests.get(url, headers=headers).text
使用lxml来解析网页:
from lxml import etree
s = etree.HTML(data)
接下来我们开始提取数据,打开目标页面,将每首音乐的完整信息的xpath信息复制下来:
重复这样的操作,多复制几首音乐的xpath信息,观察它的规律:
//*[@id="content"]/div/div[1]/div/table[1]
//*[@id="content"]/div/div[1]/div/table[2]
//*[@id="content"]/div/div[1]/div/table[3]
这样子规律就很明显了,每首音乐就是table后的序号不同。只要我们将序号去掉,就可以提取所有音乐的全部信息了:
# 歌曲整体的xpath
musics = s.xpath('//*[@id="content"]/div/div[1]/div/table')
我们再使用同样的方法来获取歌名的xpath信息:
多复制几个歌名信息的xpath来对比一下:
# 歌名的xpath
# //*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/a
# //*[@id="content"]/div/div[1]/div/table[2]/tbody/tr/td[2]/div/a
这是注意一下, 上面的xpath路径中,/tbody是多余的,我们要将其删掉,不然会出错,所以,有时候直接在浏览器复制xpath信息也不一定靠谱,这里要注意一下。
这样我们就可以提取出每首歌的歌名:
for music in musics:
music_name = music.xpath('./tr/td[2]/div/a/text()')[0].strip()
如果不加[0],我们得到的结果将是一个list类型,每个结果都被中括号[]裹起来,比较碍眼,所以使用[0]取出list的第一个值就可以了。strip()函数可以去除多余的空格。
还有,音乐的链接和名字的xpath路径是相同的,只是一个取href值,一个取text值。 所以每首歌的链接如下:
for music in musics:
music_name = music.xpath('./tr/td[2]/div/a/@href')[0]
我们再使用同样的方法复制作者(专辑)的xpath信息:
多复制几个作者的xpath信息来对比一下:
# //*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/p[1]
# //*[@id="content"]/div/div[1]/div/table[2]/tbody/tr/td[2]/div/p[1]
同样要将/tbody从xpath路径中删掉,这样我们可以提取到每首歌的作者信息:
for music in musics:
music_author = music.xpath('./tr/td[2]/div/p[1]/text()')[0].strip()
我们还使用同样的方法复制评分的xpath信息:
多复制几个评分的xpath信息来对比一下:
# //*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/div/span[2]
# //*[@id="content"]/div/div[1]/div/table[2]/tbody/tr/td[2]/div/div/span[2]
同理,我们就可以提取出每首歌的评分了:
for music in musics:
music_score = music.xpath('./tr/td[2]/div/div/span[2]/text()')[0]
到现在,我们已经获取了我们想要的所有信息。现在来整合一下代码,存储为TXT文件:
import requests
from lxml import etree
import time
with open("豆瓣音乐top250.txt", "w", encoding="utf-8") as f:
for i in range(10):
# 一共有10个页面,每个页面25首音乐
url = "https://music.douban.com/top250?start={}".format(i * 25)
headers = {"User_Agent": "Mozilla/5.0(compatible; MSIE 5.5; Windows 8)"}
data = requests.get(url, headers=headers).text
s = etree.HTML(data)
# 歌曲整体的xpath
musics = s.xpath('//*[@id="content"]/div/div[1]/div/table')
time.sleep(2)
for music in musics:
music_name = music.xpath('./tr/td[2]/div/a/text()')[0].strip() # 歌名
music_author = music.xpath('./tr/td[2]/div/p[1]/text()')[0].strip() # 作者
music_score = music.xpath('./tr/td[2]/div/div/span[2]/text()')[0] # 评分
music_href = music.xpath('./tr/td[2]/div/a/@href')[0] # 链接
f.write("{} {} {} {}n".format(music_name, music_author, music_score, music_href))
如上,一共23行代码。
爬取结果如下:
本来我想存为CSV文件的,结果发现有的歌曲有多个歌手,而且几个歌手之间是用逗号(,)分开的,这就和CSV中的逗号产生了混淆,导致结果比较错乱。好吧,我暂时不知道存为CSV格式时,怎样消除其本身携带的逗号产生的影响。
当然,同样的爬取思路,我们还可以爬取豆瓣电影top250、豆瓣图书top250。只需要将url和xpath路径修改一下就可以了。
每天学习一点点,每天进步一点点。
- ios app url scheme跳转到淘宝商品详情页 唤醒app
- ThreadPoolExecutor运行机制
- Spring Cloud构建微服务架构:服务容错保护(Hystrix依赖隔离)【Dalston版】
- UIPickView的简单使用
- java开发中几种常见的线程池
- 传统多线程之前如何共享数据
- Spring Cloud构建微服务架构:服务容错保护(Hystrix断路器)【Dalston版】
- 调整渐变下降的学习率
- 多线程之传统多线程
- ios 常用的正则表达式(手机号邮箱md5加密验证空字符串等)
- Spring Cloud构建微服务架构:Hystrix监控面板【Dalston版】
- 云原生应用的12要素
- Universal-Image-Loader源码分析,及常用的缓存策略
- ios textView跟随键盘的移动
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 一篇文章就能了解Rxjava
- 二叉树遍历非递归程序 -- 使用栈模拟系统栈
- Android开发之多媒体文件获取工具类实例【音频,视频,图片等】
- 二叉树及leetcode练习题
- 利用递归函数的返回值
- Android DataBinding的官方双向绑定示例
- 整数拆分
- Android Drawerlayout实现侧滑菜单效果
- 分割等和子集
- LeetCode--打家劫舍问题
- Android 中ListView和GridView赋值错位
- 谈谈类加载器
- Android中AlarmManager+Notification实现定时通知提醒功能
- Java垃圾回收相关面试题
- Android中backgroundDimEnabled的作用