【个人】爬虫实践,利用xpath方式爬取数据之爬取虾米音乐排行榜

实验网站:虾米音乐排行榜
难度系数:★☆☆☆☆
依赖库:request、lxml的etree (安装lxml:pip install lxml)
IDEA开发工具:PyCharm_2017.3
Python版本:Python3
期望结果:爬取出排行版歌名以及对应歌手

运行效果图:
音乐排行榜:
 
爬取数据结果图:

    像这种简单的爬取就没必要使用Scrapy框架进行处理,是在有点大材小用,不过如果你刚开始学Scrapy的话,拿这些简单的练手是必须的,从简单的开始,慢慢去熟悉、掌握Scrapy框架。用BeautifulSoup也可以,简单实用,尤其元素选择器方面和Jquery很类似,上手比较快,但是最近我还是感觉XPath比较好用,所以今天就用XPath方式对数据进行提取吧。不过为了分享更多内容,让初学者少踩坑,快进步,我打算把这个实验再用Scrapy实现一遍,对于老鸟来说这实在没多大意义,但是对于初学者来说却能更好的理清思路,少走弯路。
    这篇入门教程和未来Scrapy爬取虾米音乐排行榜的教程我都会以最简单直白的方式进行说明,基本从第一句代码开始写起,期间对遇到的问题进行分析、解决、对代码逐步完善,同时辅之更清晰明了的截图,让各位初学者更直观的看到代码逐步完善的过程,更好的理清思路。
 
    首先用浏览器(推荐Chrome内核的浏览器,例如谷歌Chrome浏览器,Cent百分浏览器,此处用的Cent浏览器,感觉比Chrome更好用)打开网站 http://www.xiami.com/chart  ,基本就是我们看到的这样:
 
 
    我们此时先试着写简单的代码,我们一步一步处理,遇到问题去解决:
import requests
from lxml import etree

url_ = "http://www.xiami.com/chart"
page_source = requests.get(url=url_)
print("响应结果:
",page_source.text)
model = etree.HTML(page_source.text)
 
    以上代码就是最简单的获取网页源码的方式,我们运行发现好像没有达到我们的预期结果:
 
    出现了400错误,什么问题呢?下面蓝色区域画出来的部分已经很明确的说明了,因为我们浏览器发送的请求服务端无法识别,仔细一想浏览器打开这个网址正常,而我们通过代码则不行,很明显就是Use-Agent(UA)不匹配了,找到问题原因就好办了,我们可以模拟一个UA,例如:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87,接着我们修改一下代码:
 
 1 import requests
 2 
 3 from lxml import etree
 4 
 5 
 6 url_ = "http://www.xiami.com/chart"
 7 
 8 headers_ = {
 9 
10     'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87'
11 
12 }
13 
14 page_source = requests.get(url=url_,headers=headers_)
15 
16 print("响应结果:
",page_source.text)
17 
18 model = etree.HTML(page_source.text)
 
    可以看到此时网站已经正确响应了,我们拿到了我们的“理想数据“,但仔细看了看响应的数据,然后搜索了一下,发现好像没有看到某首歌曲
的信息,也就是说目前返回的数据并不包含歌曲排行榜数据:
 
    我明明是打开的这个 http://www.xiami.com/chart 网址啊,而且用浏览器打开后也加载出音乐排行榜的歌曲了啊,为什么返回的网页源码中找不到歌曲信息呢?不知道你发现没有,当浏览器打开这个网址时,页面中间区域排行榜数据处于一个正在加载中状态,并不是一下子整个页面出来的(你网速好可以忽略了...)
    就类似于上图这样,一部分页面出来了而一部分还未加载完成,这时候你是不是就想到了Ajax异步加载了?这就对了,这块数据是Ajax后期加载出来的,是有一个新请求的,直接通过代码获取网页源码肯定不会得到期望结果的,我们应该想办法知道Ajax通过什么方式请求了哪个地址以及得到了什么类型的结果,于是乎浏览器的开发者工具就派上用场了:
    我们可以看到当我们打开这个页面时都进行了右侧的请求响应处理,我们响应的Ajax请求肯定就在其中了,我们可以慢慢找,不过我们知道是Ajax请求了,那我们直接过滤“XHR”:
    进过过滤我们发现就这一个请求,然后看了一下Response,最后确认就是它了!!
    歌曲就在其中了,于是我们确定这就是我们最终要请求的URL了,然后我们再看一下该请求的相关信息:
    于是我们知道了URL是:http://www.xiami.com/chart/data?c=103&type=0&page=1&limit=100&_=1517477892257 请求方式是:GET方式 等信息,那我们就接着修改代码吧:
    (URL的请求参数,比如page、limit等我们就不细究了,一看就知道当前页码和每页返回数据大小)
 
import requests
from lxml import etree

url_ = "http://www.xiami.com/chart/data?c=103&type=0&page=1&limit=100&_=1517477892257"

headers_ = {

    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87'

}
page_source = requests.get(url=url_,headers=headers_)

print("响应结果:
",page_source.text)

model = etree.HTML(page_source.text)
    运行之后:
    可以看到这就是我们理想数据了,相关音乐排行榜数据都在其中了,剩下的就是提取数据了,这里我们使用XPath语法提取,首先还是在浏览器那边定位一下我们想要的歌曲名、歌手所在元素位置,然后确定XPath路径写法。
    我们看到歌曲名在<a>标签里面,而且这个标签是在样式类型为“info”的div标签里面嵌套的,层次为:
<div class="info">
    <p>
        <strong>
            <a>歌曲名等信息  在第一个p标签里面</a>
        </strong>
    </p>
    <p>
        ...
        ...
    </p>
</div>
    那我们根据这个层级初步写出一个XPath路径:
    //div[@class="info"]/p[1]/strong/a/text()    
    我们使用XPath Helper插件进行验证一下:
    
 
    我们发现这句XPath选择是可以把当前页面所有的歌曲名称选择出来的,然后就是再选择歌曲对应的歌手,我们再看一下歌手标签所在的DOM结构:
 
    我们看到歌曲名在<a>标签里面,而且这个标签是在样式类型为“info”的div标签里面嵌套的,层次为:
 
<div class="info">
    <p>
        ...
        ...
    </p>
    <p>
       <a>歌手名称,在第二个p标签里面</a>
    </p>
 
    基于以上结构,我们初步下一个XPath路径:
    //div[@class="info"]/p[2]/a
    我们使用XPath Helper插件进行验证一下:
    
    此时我们发现这个XPath选择确实把歌手名称选中了,但是好像有个问题,明明查询出来100首歌曲,怎么却有129位歌手呢?对应不起来啊。别急,那我们就仔细看一下每一首歌曲的歌手吧
    
 
    从中发现,一首歌曲可能有多位演唱者,每一位演唱者占用一个<a>标签,且用“;”分隔,如下:
    这样的话我们改进一下XPath选择,试试选择<a>标签所在的p标签,这样把多位歌手(即多个a标签)看成一个list集合,我们在Python代码处理即可,修改后的XPath选择如下:
    //div[@class='info']/p[2]
    
    此时我们看到这次选择的就不是每一位歌手所在a标签了,二是上一级的整个p标签,而且选择出来的歌手一共100位,正好和歌曲个数对应起来了,那么就到此为止,我们用代码处理:
import requests
from lxml import etree

url_ = "http://www.xiami.com/chart/data?c=103&type=0&page=1&limit=100&_=1517477892257"

headers_ = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87'
}

page_source = requests.get(url=url_, headers=headers_)
print("响应结果:
", page_source.text)
model = etree.HTML(page_source.text)
songs_list = model.xpath('//div[@class="info"]/p[1]/strong/a/text()')

songer = model.xpath("//div[@class='info']/p[2]")
print("歌曲个数:{}   歌手个数:{}".format(len(songs_list),len(songer)))

 
 
 
    运行结果:
    对代码继续进行处理完善:
import requests
from lxml import etree
url_ = "http://www.xiami.com/chart/data?c=103&type=0&page=1&limit=100&_=1517477892257"

headers_ = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87'
}
page_source = requests.get(url=url_, headers=headers_)

print("响应结果:
", page_source.text)

model = etree.HTML(page_source.text)
songs_list = model.xpath('//div[@class="info"]/p[1]/strong/a/text()')
songer = model.xpath("//div[@class='info']/p[2]")
print("歌曲个数:{}   歌手个数:{}".format(len(songs_list), len(songer)))
for index, item in enumerate(songs_list):
    # 继续处理一首歌曲有多位演唱者的情况,因为每一位演唱者都在一个a标签里面,我们把多个a标签看成一个list集合处理
    songer_list = songer[index].xpath(".//a/text()")
    # 每一首歌曲的每一位演唱者组成一个list集合,我们利用join方法对list集合的每一项进行拼接,组成一个字符串结果
    dealed_songer = ",".join(songer_list)
    # 最后按格式输出结果
    print("{}、{}	【{}】".format(index + 1, item, dealed_songer))

 
 
 
最终结果:
歌曲个数:100   歌手个数:100
1、Bad Boy    【Red Velvet(레드벨벳)】
2、Electric Kiss    【EXO(엑소)】
3、Cosmic Railway    【EXO(엑소)】
4、说散就散    【袁娅维(Tia)】
5、追光者    【岑宁儿】
6、我们不一样    【大壮】
7、带你去旅行    【校长(张驰)】
8、刚好遇见你    【李玉刚】
9、123我爱你    【新乐尘符】
10、空空如也    【胡66】
11、成都    【赵雷(雷子)】
12、红昭愿    【音阙诗听(Interseting)】
13、等你下课    【周杰伦,杨瑞代】
14、9420    【麦小兜】
15、Ei Ei    【偶像练习生】
16、广东爱情故事    【广东雨神】
17、小幸运    【田馥甄】
18、九张机    【叶炫清】
19、岁月神偷    【金玟岐】
20、BINGBIAN病变 (女声版)    【鞠文娴】
21、你就不要想起我    【田馥甄(Hebe)】
22、最美情侣    【白小白】
23、全部都是你    【Dragon Pig,Cloud Wang】
24、告白气球    【周杰伦(Jay Chou)】
25、사랑을 했다    【iKON(TEAM B)】
26、过往    【邹施如】
27、文爱    【CG(CHIGGA GANG黄鬼帮)】
28、差一步    【大壮】
29、在人间    【王建房】
30、Panama    【Matteo】
31、没有你陪伴真的好孤单    【梦然】
32、童话镇    【陈一发儿(陈一发)】
33、尽头    【音阙诗听(Interseting)】
34、Lovin’ You Mo’    【EXO(엑소)】
35、勉为其难    【王冕】
36、最美的期待    【周笔畅(BiBi Chou)】
37、非酋    【薛明媛,朱贺】
38、平凡之路    【朴树】
39、多幸运    【韩安旭】
40、再也没有    【Ryan.B,AY杨佬叁】
41、说散就散    【冯提莫(Ti mo Feng)】
42、空空如也    【任然】
43、Faded    【Alan Walker,Iselin Solheim】
44、你曾是少年    【S.H.E(SHE)】
45、小半    【陈粒(粒粒)】
46、All Right    【Red Velvet(레드벨벳)】
47、春风吹    【阿冷】
48、Shape of You    【Ed Sheeran(艾德·希兰)】
49、默    【那英】
50、喜欢你    【G.E.M.邓紫棋(邓紫棋)】
51、盗心贼    【黑龙】
52、演员 (Live)    【薛之谦】
53、恋人心    【魏新雨】
54、Please Don't Go    【Joel Adams】
55、其实都没有    【杨宗纬(Aska Yang)】
56、过客    【阿涵(周思涵)】
57、我们    【杨清柠,王乐乐】
58、丑八怪 (Live)    【薛之谦】
59、最久的瞬间    【炎亚纶(Aaron)】
60、大鱼    【周深(卡布叻)】
61、Time    【MKJ】
62、回忆总想哭    【南宫嘉骏,姜玉阳】
63、Despacito (Remix)    【Luis Fonsi,Daddy Yankee,Justin Bieber】
64、山丘    【李宗盛(Jonathan Lee)】
65、回忆那么伤    【孙子涵(Niko)】
66、后来的我们    【五月天(Mayday)】
67、一笑倾城    【汪苏泷】
68、陪你度过漫长岁月    【陈奕迅(Eason Chan)】
69、林中鸟    【葛林】
70、半壶纱    【刘珂矣】
71、生活不止眼前的苟且    【许巍】
72、给我一个理由忘记    【A-Lin(黄丽玲)】
73、光年之外    【G.E.M.邓紫棋(邓紫棋)】
74、Dream It Possible    【Delacey】
75、Sugar    【Maroon 5(魔力红)】
76、Havana    【Camila Cabello,Young Thug】
77、如果我们不曾相遇    【五月天(Mayday)】
78、全世界谁倾听你    【林宥嘉(Yoga Lin)】
79、短发    【LAMPHO(lampho猴子)】
80、退    【蒋蒋(蒋家驹)】
81、小公主    【蒋蒋,杨清柠】
82、奇妙能力歌    【陈粒(粒粒)】
83、雨后人去楼也空    【王乐乐】
84、歌在飞    【苏勒亚其其格(格格)】
85、我要你    【任素汐】
86、后会无期    【G.E.M.邓紫棋(邓紫棋)】
87、Зая    【Бамбинтон】
88、微微一笑很倾城    【杨洋】
89、Psycho (Pt. 2)    【Russ】
90、闪光 (Shinin’)    【钟铉(종현)】
91、四年半    【杨清柠】
92、情话微甜    【王圣锋,李朝】
93、愿得一人心    【李行亮】
94、病变remix    【前男友】
95、爱的就是你    【十二星宿风之少年,刘佳】
96、Grandpa    【阿克江LilAkin,Bohan Phoenix,Lofimaker】
97、好好 (想把你写成一首歌)    【五月天(Mayday)】
98、无问西东    【王菲(Faye Wong)】
99、理想三旬    【陈鸿宇】
100、走在冷风中    【刘思涵(Koala)】
 

    至此,本教程结束,通过本教程,你清晰的看到了这个简单数据爬取是怎么一步一步完成的,见证了从需求明确、问题原因分析、问题解决、代码完善、最终完成的整个生命周期,那么此时你也就对爬虫编写基本思路有了一个简单的认识,爬取数据的代码都不会太复杂,复杂就复杂在发现问题、解决问题方式的寻找中,好的爬虫在于数据价值、涵盖范围以及相关算法确定中。当然了,本教程爬取的数据没多大价值,仅仅是抛砖引玉而已,希望各位灵活运用。
原文地址:https://www.cnblogs.com/geekworld/p/8410751.html