学习Python有一段时间了,基础的语法过了一遍,也做了一些小例子,但总是感觉不能熟练的运用。为了巩固所学知识,增加实战经验,解锁更多知识点,本篇文章做了一次爬虫练习,通过具体的需求,明确爬虫的具体思路,掌握Python常用的数据结构,以此提高对Python学习的兴趣,加深基础知识的了解。


环境:

  • OS:Windows7 x64
  • IDE:PyCharm
  • Python Version:3.6
  • Browser:Google Chrome

建议安装Anaconda,它是一个开源的Python发行版本,里面包含了Python需要的许多依赖安装包,在导入模块的时候十分方面。

关于HTML的一些基础知识,在这里就不展开讲解了,你可以在W3school上学到,祝你好运!

实战中用于访问网络的模块Requests在这里也不展开讲解,你可以点击它阅读其文档,贵在坚持!

另外,用于解析HTML或XML文件数据的模块BeautifulSoup也不展开讲解,你可以自行翻阅文档,加油!


1.测试网页

在爬取网页数据之前,我们需要知道这个网站是否可以访问。判断其方法很简单,首先导入requests模块,其次添加一个headers告诉这个网站说,我们是通过浏览器访问的,然后再写上一个待会儿我们要爬取的网址url,再次通过requestsget方法将该页面的HTML信息保存到wb_data中,我们可以用status_code查看是否可以访问,如下所示:

1
2
3
4
5
6
7
8
9
import requests

headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"
}

url = 'http://www.kugou.com/yy/rank/home/1-8888.html?from=rank'
wb_data = requests.get(url, headers=headers)
print(wb_data.status_code)

输出结果如下:

1
200

返回200就说明此网页可以访问。我们也可以使用print(wb_data.text)来输出整个HTML数据,如下图: Python_Spider 输出的东西很多,包括此url下全部的HTML信息。

如何获取User-Agent?

打开网页后右击空白区域,选择检查,点击上方的Network后刷新网页,在左侧Name栏中单击1-8888.html?from=rank,在右侧的Request Headers中可也以找到User-Agent,我们将其复制过来即可,如下图: Python_Spider

2.分析网页

打开酷狗 http://www.kugou.com/yy/rank/home/1-8888.html?from=rank 后可以看到,在未登录的情况下,这一页共有22首歌曲。我们修改上面的连接,将其中的1修改为2,可以看到浏览器跳转到了第二页。再次将-8888前面的数字改成23即可看到最后一页排名500的歌曲,这些歌曲就是我们待会儿要爬取的东西。

爬取什么呢?

Python_Spider 从上面的图片中可以看到,我们爬取的数据主要分三类:排名、歌手名与歌曲名、歌曲时间。

好了,需求知道了,现在就开始吧。

右击歌曲左边排名为1的地方,点击检查,如下图: Python_Spider 进来以后,可以看到1在一对strong标签中,我们可以通过它的上级标签span class="pc_temp_num"来获取排名信息,如下图: Python_Spider

3.爬取数据

我们先导入BeautifulSoup,用它来解析HTML数据。导入requests用于发送网络请求,紧接着添加headersurl,利用requests.get()将服务器响应的内容读取到wb_data,再使用BeautifulSoupwb_data中的数据返回一个对象到soup,最后再用soup.select()方法筛选歌曲的排行,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from bs4 import BeautifulSoup
import requests

headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"
}

url = 'http://www.kugou.com/yy/rank/home/1-8888.html?from=rank'
wb_data = requests.get(url, headers=headers)
soup = BeautifulSoup(wb_data.text, 'lxml')
ranks = soup.select('.pc_temp_num')
print(ranks)

输出结果如下: Python_Spider 你可以看到歌曲的排名被放在了一对strong标签中,我们使用get_text()方法可以获取到它的排名,用strip()方法去除多余的空格,如下所示:

1
print(ranks[0].get_text().strip())  # 输出 1

利用这种方法可以同样的获得歌手名与歌曲名、歌曲时间,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from bs4 import BeautifulSoup
import requests

headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"
}

url = 'http://www.kugou.com/yy/rank/home/1-8888.html?from=rank'
wb_data = requests.get(url, headers=headers)
soup = BeautifulSoup(wb_data.text, 'lxml')
ranks = soup.select('.pc_temp_num')
titles = soup.select('.pc_temp_songlist > ul > li > a')
song_times = soup.select('.pc_temp_time')
print(ranks[0].get_text().strip())
print(titles[0].get_text().split('-')[0].strip())
print(titles[0].get_text().split('-')[1].strip())
print(song_times[0].get_text().strip())

输出结果如下:

1
2
3
4
1
小潘潘小峰峰
学猫叫
3:29

这样我们就得到了一首歌曲的信息,然后将其放在for循环中遍历整页的歌曲,(这里用到zip()字典等知识)如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from bs4 import BeautifulSoup
import requests

headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"
}

url = 'http://www.kugou.com/yy/rank/home/1-8888.html?from=rank'
wb_data = requests.get(url, headers=headers)
soup = BeautifulSoup(wb_data.text, 'lxml')
ranks = soup.select('.pc_temp_num')
titles = soup.select('.pc_temp_songlist > ul > li > a')
song_times = soup.select('.pc_temp_time')
for rank, title, song_time in zip(ranks, titles, song_times):
    data = {
        'rank': rank.get_text().strip(),
        'singer': title.get_text().split('-')[0].strip(),
        'song': title.get_text().split('-')[1].strip(),
        'time': song_time.get_text().strip(),
    }
    print(data)

输出结果如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{'rank': '1', 'singer': '小潘潘、小峰峰', 'song': '学猫叫', 'time': '3:29'}
{'rank': '2', 'singer': '王北车', 'song': '陷阱', 'time': '4:40'}
{'rank': '3', 'singer': 'Olly Murs', 'song': 'That Girl', 'time': '2:57'}
{'rank': '4', 'singer': '曾惜', 'song': '讲真的', 'time': '3:59'}
{'rank': '5', 'singer': '杨凯莉', 'song': '让我做你的眼睛', 'time': '1:46'}
{'rank': '6', 'singer': '胡66、单色凌', 'song': '浪人琵琶', 'time': '3:44'}
{'rank': '7', 'singer': '阿里郎', 'song': '隔壁泰山', 'time': '3:19'}
{'rank': '8', 'singer': '刘子璇', 'song': '嘴巴嘟嘟', 'time': '4:08'}
{'rank': '9', 'singer': '杨坤、郭采洁', 'song': '答案', 'time': '3:51'}
{'rank': '10', 'singer': 'Fitz & The Tantrums', 'song': 'Hand Clap', 'time': '3:13'}
{'rank': '11', 'singer': '庄心妍', 'song': '再见只是陌生人', 'time': '4:12'}
{'rank': '12', 'singer': '烟把儿乐队', 'song': '纸短情长 (完整版)', 'time': '2:53'}
{'rank': '13', 'singer': '萧亚轩', 'song': '突然想起你', 'time': '3:53'}
{'rank': '14', 'singer': '于文文', 'song': '体面', 'time': '4:42'}
{'rank': '15', 'singer': '孙语赛、萧全', 'song': '不仅仅是喜欢', 'time': '3:48'}
{'rank': '16', 'singer': '萧全', 'song': '海草舞', 'time': '4:37'}
{'rank': '17', 'singer': '张北北', 'song': '拥抱你离去', 'time': '4:02'}
{'rank': '18', 'singer': '周笔畅', 'song': '最美的期待', 'time': '3:30'}
{'rank': '19', 'singer': '洛晴', 'song': '可能否', 'time': '3:49'}
{'rank': '20', 'singer': '王力宏', 'song': '需要人陪', 'time': '4:09'}
{'rank': '21', 'singer': '旭日阳刚刘刚', 'song': '怀念青春', 'time': '4:28'}
{'rank': '22', 'singer': '新乐尘符', 'song': '123我爱你', 'time': '3:19'}
Process finished with exit code 0

4.数据保存

在保存数据之前,我们首先声明一个total列表,用append()方法将每条数据添加到total中,如下所示:

1
2
3
4
5
6
7
8
9
total = []
for rank, title, song_time in zip(ranks, titles, song_times):
    data = {
        'rank': rank.get_text().strip(),
        'singer': title.get_text().split('-')[0].strip(),
        'song': title.get_text().split('-')[1].strip(),
        'time': song_time.get_text().strip(),
    }
    total.append(data)

使用open将爬取到的数据保存在文本文档中,如下所示:

1
2
3
with open('kugouTop500.txt', 'a', encoding='utf-8') as file:
    for i in total:
        file.write(str(i) + '\n')

5.整理代码

我们分别将数据爬取部分写入文本文档部分整理成函数,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import time
import requests
from bs4 import BeautifulSoup

headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"
}

# 获取数据信息
def get_info(url):
    wb_data = requests.get(url, headers=headers)
    soup = BeautifulSoup(wb_data.text, 'lxml')
    ranks = soup.select('.pc_temp_num')
    titles = soup.select('.pc_temp_songlist > ul > li > a')
    song_times = soup.select('.pc_temp_time')
    total = []
    for rank, title, song_time in zip(ranks, titles, song_times):
        data = {
            'rank': rank.get_text().strip(),  # 歌曲排名
            'singer': title.get_text().split('-')[0].strip(),  # 歌手名
            'song': title.get_text().split('-')[1].strip(),  # 歌曲名
            'time': song_time.get_text().strip(),  # 歌曲时长
        }
        total.append(data)
    write_to_txt(total)

# 保存到txt中
def write_to_txt(total):
    with open('kugouTop500.txt', 'a', encoding='utf-8') as file:
        for i in total:
            file.write(str(i) + '\n')


if __name__ == '__main__':
    # 这里一共有23页,用格式化字符串的方式,每次将1到23中的数字放到花括号内,从而生成新的网页
    urls = ['http://www.kugou.com/yy/rank/home/{}-8888.html?from=rank'.format(str(i)) for i in range(1, 24)]  
    for url in urls:
        get_info(url)
        time.sleep(1)  # 这里调用time的sleep方法,等待一秒再继续爬取下一页数据信息,避免过度频繁爬取

6.总结

看完本篇文章后,你可以了解到爬虫的一般步骤:

  • 抓取网页信息
  • 筛选有用信息
  • 保存数据信息

当然,本文还有一些关于爬虫需要注意的地方没有涉及到,这仅仅是一个简单的上手实例。我想,通过不断亲手实践,逐渐学习新方法、新思路,以此来加深对Python基础知识的了解,才是我们想要达到的目的。