基于python多线程爬取网易云音乐歌手名(超详细注释)

基于python网易云音乐歌手名爬取

import os
import time
import requests
import threading
from lxml import etree
from queue import Queue
# 创建获取所有类型路由的线程类
class GetMain(threading.Thread):
    # 初始化函数
    def __init__(self,url,headers,BASE_URl):
        super(GetMain, self).__init__()
        self.url = url
        self.headers = headers
        self.BASE_URl = BASE_URl
    # 重写线程启动函数
    def run(self):
        # 请求url返回的响应
        resp = requests.get(self.url,headers=self.headers).content.decode()
        # 将相应的字符串转换成Html对象
        tree = etree.HTML(resp)
        # 通过xpath获取类型的列表
        country_list = tree.xpath('//div[@class="g-sd2"]/div//div')
        # TODO 获取各个分类下的名称和链接
        for country in country_list:
            # 各个国家分类的名称
            country_name = country.xpath('h2/text()')[0]
            # 各个分类下的分类
            style_list = country.xpath('ul/li')
            for style in style_list:
                # 分类名称
                style_name = style.xpath('a/text()')[0]
                # 分类的路由
                style_href = self.BASE_URl+style.xpath('a/@href')[0]
                # 将所有类型的路由放入到类型队列之中
                styleList_queue.put(style_href)
# 创建获取各个类型下面的所有对应字母的路由的线程类
class GetName(threading.Thread):
    # 初始化函数
    def __init__(self,headers,BASE_URl):
        super(GetName, self).__init__()
        self.headers = headers
        self.BASE_URl = BASE_URl
    # 重写启动函数
    def run(self):
        while True:
            # 通过判断类型列表是否为空,flag1是否为True,来判断什么时间该结束循环
            if styleList_queue.empty() and flag1:
                break
            try:
                # 从类型队列中拿去类型的url,并添加block=False(为了防止阻塞)
                style_url = styleList_queue.get(block=False)
                # 获取请求的响应字符串
                response = requests.get(style_url,headers = headers).content.decode()
                # 将相应字符串转换成一个html对象
                tree = etree.HTML(response)
                # 获取字母对应的列表
                letter_li = tree.xpath('//ul[@class="n-ltlst f-cb"]/li[position()>1]')
                # ABC-Z
                for letter in letter_li:
                    # 拼接字母对应的url
                    letter_url = BASE_URl+letter.xpath('a/@href')[0]
                    # 将字母url放到字母路由队列当中
                    letter_url_queue.put(letter_url)
            except:
                pass
# 创建获取所有歌星姓名的线程类
class GetStarName(threading.Thread):
    # 初始化函数
    def __init__(self,headers):
        super(GetStarName, self).__init__()
        self.headers = headers
    # 重写启动函数
    def run(self):
        while True:
            # 通过判断字母路由队列是否为空,flag2是否为True,来判断什么时间该结束循环
            if letter_url_queue.empty() and flag2:
                break
            try:
                # 从字母路由队列中拿去字母的url,并添加block=False(为了防止阻塞)
                letter_url = letter_url_queue.get(block=False)
                # 获取请求的响应字符串
                response = requests.get(letter_url,headers=self.headers).content.decode()
                # 将相应字符串转换成一个html对象
                tree = etree.HTML(response)
                # 获取字母对应的无序列表
                name_ul = tree.xpath('//ul[@class="m-cvrlst m-cvrlst-5 f-cb"]')[0]
                # 获取有图片的下面的名字
                name1 = name_ul.xpath('li/p/a[1]/text()')
                # 获取没有图片下面的名字
                name2 = name_ul.xpath('li/a/text()')
                # 循环名字将名字写入到文件中
                for name in name1:
                    # 添加线程锁防止线程发生写入错误
                    with lock:
                        with open('star_name_thread.txt', 'a', encoding='utf-8')as f:
                            f.write(name + '\n')
                for name in name2:
                    # 添加线程锁防止线程发生写入错误
                    with lock:
                        with open('star_name_thread.txt', 'a', encoding='utf-8')as f:
                            f.write(name + '\n')
            except:
                pass

# 标志1
flag1 = False
# 标志2
flag2 = False
# 创建类型队列
styleList_queue = Queue()
# 创建字母路由队列
letter_url_queue = Queue()
# 创建线程锁
lock = threading.Lock()
if __name__ == '__main__':
    # 设置起始时间
    start = time.time()
    # 设置请求url
    url_main = 'https://music.163.com/discover/artist'
    # 设置请求头
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
    }
    # 设置主url
    BASE_URl = 'https://music.163.com'
    # 实例化获取类型路由的线程
    t1 = GetMain(url=url_main,headers=headers,BASE_URl=BASE_URl)
    # 开启线程
    t1.start()
    # 创建存储线程的第一个列表
    t1_list = []
    # 循环创建并开启,获取字母路由的线程类
    for i in range(5):
        t = GetName(headers=headers,BASE_URl=BASE_URl)
        t.start()
        # 将线程添加到第一个存储线程的列表
        t1_list.append(t)
    # 创建存储线程的第二个列表
    t2_list = []
    # 循环创建并开启,获取歌星名称的线程
    for i in range(5):
        t2 = GetStarName(headers=headers)
        t2.start()
        # 将线程添加到第二个存储线程的列表
        t2_list.append(t2)
    # 将t1线程阻塞
    t1.join()
    # t1线程执行完成后将flag1配置成True
    flag1 = True
    # 循环遍历线程第二个线程类的列表,并对每一个线程进行阻塞
    for i in t1_list:
        i.join()
    # 第二个线程类所有的线程结束之后将flag2配置成True
    flag2 = True
    # 循环遍历线程第三个线程类的列表,并对每一个线程进行阻塞
    for i in t2_list:
        i.join()
    # 获取结束时间
    end = time.time()
    # 打印从开始到结束的时间总长
    print('程序结束耗时%s秒'%(end-start))



你可能感兴趣的:(爬虫)