Python爬虫:现学现用xpath爬取豆瓣音乐

Stella981
• 阅读 1396

爬虫的抓取方式有好几种,**正则表达式,Lxml(xpath)与BeautifulSoup,**我在网上查了一下资料,了解到三者之间的使用难度与性能

三种爬虫方式的对比。

Python爬虫:现学现用xpath爬取豆瓣音乐

这样一比较我我选择了Lxml(xpath)的方式了,虽然有三种方式,但肯定是要选择最好的方式来爬虫,这个道理大家都懂,另外有兴趣的朋友也可以去了解另外两种爬虫方式!

好了现在来讲讲xpath

由于Xpath属于lxml模块,所以首先需要安装lxml库,老办法直接在file-->setting---project interpreter 一键添加lxml库。

xpath简单用法

from lxml import etree

s=etree.HTML(源码) #将源码转化为能被XPath匹配的格式

s.xpath(xpath表达式) #返回为一列表,

基础语法:

  1. // 双斜杠 定位根节点,会对全文进行扫描,在文档中选取所有符合条件的内容,以列表的形式返回。

  2. / 单斜杠 寻找当前标签路径的下一层路径标签或者对当前路标签内容进行操作

  3. /text() 获取当前路径下的文本内容

  4. /@xxxx 提取当前路径下标签的属性值

  5. | 可选符 使用|可选取若干个路径 如//p | //div 即在当前路径下选取所有符合条件的p标签和div标签。

  6. . 点 用来选取当前节点

  7. .. 双点 选取当前节点的父节点

学以致用,方能让我们能快速掌握xpath语法功能。

我们这次需要爬取豆瓣音乐前250条

打开豆瓣音乐:https://music.douban.com/top250

获取单条数据

1.获取音乐标题

打开网址,按下F12,然后查找标题,右键弹出菜单栏 Copy==> Copy Xpath

Python爬虫:现学现用xpath爬取豆瓣音乐

这里我们想获取音乐标题,音乐标题的xpath是:xpath://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/a

# coding:utf-8
from lxml import etree
import requests

url = 'https://music.douban.com/top250'

html = requests.get(url).text    #这里一般先打印一下html内容,看看是否有内容再继续。
s = etree.HTML(html)
title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/a')
print title

运行代码:
居然是空的。!!!
这里需要注意一下,浏览器复制的xpath只能作参考,因为浏览器经常会在自己里面增加多余的tbody标签,我们需要手动把这个标签删除
删除中间的/tbody后,是这样的,
title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a')
然后我们再运行代码。
得到:
<Element a at 0x53d26c8>

说明标题被获取到了。
因为要获取标题文本,所以xpath表达式要追加/text()
title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()

又因为这个s.xpath返回的是一个集合,且集合中只有一个元素所以我再追加一个[0]
新的表达式:
title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text(),再追加[0]

重新运行得到结果:
We Sing. We Dance. We Steal Things.
正是我们想要的标题。

2.获取音乐评分与评价人数

老办法,先用右键copy评分的xpath ://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/div/span[2]
复制评价人数的xpath://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/div/span[3]/text()

同样的我们要把tbody去掉,然后重新运行代码:

# coding:utf-8
from lxml import etree
import requests

url = 'https://music.douban.com/top250'

html = requests.get(url).text
s = etree.HTML(html)
title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()
score = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[2]/text()')[0]#因为要获取文本,所以我需要这个当前路径下的文本,所以使用/text()
numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[3]/text()')[0]#因为要获取文本,所以我需要这个当前路径下的文本,所以使用/text()
print title,score,numbers

得到:

       We Sing. We Dance. We Steal Things.
   9.1
               (
                       100395人评价
               )

3.获取音乐链接

copy标题的xpath,://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/a

想获取音乐连接href这里需要,获取这个标签属于,/@xxx可以提取当前路径标签下的属性值
//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/a/@href

代码:

# coding:utf-8
from lxml import etree
import requests

url = 'https://music.douban.com/top250'

html = requests.get(url).text
s = etree.HTML(html)
href = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/@href')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()
title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()
score = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[2]/text()')[0]#因为要获取文本,所以我需要这个当前路径下的文本,所以使用/text()
numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[3]/text()')[0]#因为要获取文本,所以我需要这个当前路径下的文本,所以使用/text()
print href,title,score,numbers

运行代码得到:

https://music.douban.com/subject/2995812/             We Sing. We Dance. We Steal Things.        9.1                     (                            100395人评价                    )
5.获取图片地址:

找到图片,复制他的xpath地址://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[1]/a/img

运行代码:

# coding:utf-8
from lxml import etree
import requests

url = 'https://music.douban.com/top250'

html = requests.get(url).text
s = etree.HTML(html)
href = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/@href')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()
title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()
score = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[2]/text()')[0]#因为要获取文本,所以我需要这个当前路径下的文本,所以使用/text()
numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[3]/text()')[0]#因为要获取文本,所以我需要这个当前路径下的文本,所以使用/text()
imgpath = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[1]/a/img/@src')[0]#因为要获取文本,所以我需要这个当前路径下的文本,所以使用/text()
print href,title,score,numbers,imgpath

老套路:
得到结果:

https://music.douban.com/subject/2995812/            We Sing. We Dance. We Steal Things.        9.1                     (                            100395人评价                    )                 https://img3.doubanio.com/spic/s2967252.jpg

但是这只是获取了一条数据,如果获取多条数据呢?

获取多条数据

Python爬虫:现学现用xpath爬取豆瓣音乐

我们再看第二条数据,第三条数据,第四条数据
得到他们的xpath:

# coding:utf-8
from lxml import etree
import requests

url = 'https://music.douban.com/top250'


html = requests.get(url).text
s = etree.HTML(html)
title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()
title2 = s.xpath('//*[@id="content"]/div/div[1]/div/table[2]/tr/td[2]/div/a/text()')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()
title3 = s.xpath('//*[@id="content"]/div/div[1]/div/table[3]/tr/td[2]/div/a/text()')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()
title4 = s.xpath('//*[@id="content"]/div/div[1]/div/table[4]/tr/td[2]/div/a/text()')[0]#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()
print title,title2,title3,title4

得到:

        We Sing. We Dance. We Steal Things.        Viva La Vida        华丽的冒险        范特西

对比他们的xpath,发现只有table序号不一样,我们可以就去掉序号,得到通用的xpath信息:
运行代码:

# coding:utf-8
from lxml import etree
import requests

url = 'https://music.douban.com/top250'

html = requests.get(url).text
s = etree.HTML(html)
titles = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/text()')#因为要获取标题,所以我需要这个当前路径下的文本,所以使用/text()

for title in titles:
    print title.strip()

得到:

We Sing. We Dance. We Steal Things.Viva La Vida华丽的冒险范特西後。青春期的詩是时候LenkaStart from Here旅行的意义太阳Once (Soundtrack)Not Going AnywhereAmerican IdiotOK無與倫比的美麗亲爱的...我还不知道城市OWake Me Up When September Ends叶惠美七里香21My Life Will...寓言你在烦恼什么

其它的信息如:链接地址,评分,评价人数都可以用同样的办法来获取,现在我同时获取多条数据,因为每页数据是25条,所以:
完整代码如下:

# coding:utf-8
from lxml import etree
import requests
url = 'https://music.douban.com/top250'
html = requests.get(url).text
s = etree.HTML(html)
hrefs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/@href')
titles = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/text()')
scores = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/div/span[2]/text()')
numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/div/span[3]/text()')
imgs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[1]/a/img/@src')
for i in range(25):
    print hrefs[i],titles[i],scores[i],numbers[i],imgs[i]

得到:
一大批数据了,我就不展示了。有兴趣可以直接copy代码运行.,注意你得装上lxml与requests库.

我们也发现了问题每一个xpath路径特别长,能不能精简一下呢?

5. 精简一下xpath路径
hrefs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/@href')
titles = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/text()')
scores = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/div/span[2]/text()')
numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/div/span[3]/text()')
imgs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[1]/a/img/@src')

观察发现获取几个关键字段的xpath前缀都是 //*[@id="content"]/div/div[1]/div/table/tr 那我能不能把这些东西提出来呢,让后面的不同的自己去追加,另外这样写也不用管每个页面到底有多少条数据,只管查就行了。所以代码做了一下精简。

url = 'https://music.douban.com/top250'

html = requests.get(url).text
s = etree.HTML(html)
trs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr')  #先提取tr之前的节点集合

for tr in trs: #遍历tr
    href = tr.xpath('./td[2]/div/a/@href')[0]  #注意新节点是tr下的节点
    title = tr.xpath('./td[2]/div/a/text()')[0]
    score = tr.xpath('./td[2]/div/div/span[2]/text()')[0]
    number = tr.xpath('./td[2]/div/div/span[3]/text()')[0]
    img = tr.xpath('./td[1]/a/img/@src')[0]
    print href,title,score,number,img

得到的结果和之前是一样的。

但是,但是,这只是一个页面的数据,我现在想爬取多个页面的数据,怎么办呢?

获取个多页面数据.

观察一下翻页路径:
https://music.douban.com/top250?start=0
https://music.douban.com/top250?start=25
https://music.douban.com/top250?start=50

有没有发现页面只是后面start参数发生了改变,且增长为每次25,并且250条数据正好是10页。
所以我可以遍历这个页面。
代码:

for i in range(10):url = 'https://music.douban.com/top250?start={}'.format(i*25)print url

得到:

https://music.douban.com/top250?start=0https://music.douban.com/top250?start=25https://music.douban.com/top250?start=50https://music.douban.com/top250?start=75https://music.douban.com/top250?start=100https://music.douban.com/top250?start=125https://music.douban.com/top250?start=150https://music.douban.com/top250?start=175https://music.douban.com/top250?start=200https://music.douban.com/top250?start=225

正是自己要的结果。

好了最后我们把代码拼装在一起,并注意每个方法的用途。

完整代码

# coding:utf-8
from lxml import etree
import requests

#获取页面地址
def getUrl():
    for i in range(10):
     url = 'https://music.douban.com/top250?start={}'.format(i*25)
     scrapyPage(url)


#爬取每页数据
def scrapyPage(url):
    html = requests.get(url).text
    s = etree.HTML(html)
    trs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr')

    for tr in trs:
        href = tr.xpath('./td[2]/div/a/@href')[0]
        title = tr.xpath('./td[2]/div/a/text()')[0]
        score = tr.xpath('./td[2]/div/div/span[2]/text()')[0]
        number = tr.xpath('./td[2]/div/div/span[3]/text()')[0]
        img = tr.xpath('./td[1]/a/img/@src')[0]
        print href, title, score, number, img

if  '__main__':
    getUrl()
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这