用Python爬取斗鱼网站的一个小案例
思路解析:
1、我们需要明确爬取数据的目的:为了按热度查看主播的在线观看人数
2、浏览网页源代码,查看我们需要的数据的定位标签
3、在代码中发送一个http请求,获取到网页返回的html(需要注意的是,许多网页都有反爬虫机制,所以需要在请求中添加user-agent,伪装成客户端访问)
4、对获取到的html进行分析,使用正则表达式提取我们需要的部分(需要注意的是要把主播名称和观看人数所在的块整个提取,分别提取的话如果网页设计不规律的话很难对应)
5、将得到的单个主播的数据存储在字典中,并把所有主播的数据存储在list中
6、如果抓取到的数据中包含空格换行等无用字符,还需要对数据进行精炼。
7、对抓取到的数据从大到小进行排序(需要注意的是:我们抓取到的数据是字符串,并且单位可能是人或者万人,所以要对观看人数进行处理)
8、将排好序的数据遍历输出。
由于斗鱼网站的网页是采用模板实现的,案例是抓取王者荣耀的主播的数据,想抓取别的类目的话,只需要修改url即可~
代码实现:
''' 爬取斗鱼网站的王者荣耀分类主播的观看人数和主播名字,并按热度排名 ''' from urllib import request from io import BytesIO import gzip import re class Spider(): url = 'https://www.douyu.com/g_wzry' # 根节点的字符串匹配正则表达式,匹配除了根节点中间的所有字符,非贪婪模式,找到第一个</div>就结束 root_pattern = '<div class="DyListCover-info">([\s\S]*?)</div>' headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36'} # 观看人数匹配字符串 number_pattern_str = '<span class="DyListCover-hot is-template">([\s\S]*?)</span>' # 观看人数的字符串,删除前面的icon部分 number_pattern = '<svg><use xlink:href="#icon-hot_8a57f0b"></use></svg>' name_pattern_str = '<h2 class="DyListCover-user is-template">([\s\S]*?)</h2>' name_pattern = '<svg><use xlink:href="#icon-user_c95acf8"></use></svg>' # 抓取自定网页内容并解码 def __fetch_content(self): # 发送一个http的请求,获取返回的html代码 req = request.Request(Spider.url, headers=Spider.headers) htmls = request.urlopen(req).read() # 解码 buff = BytesIO(htmls) f = gzip.GzipFile(fileobj=buff) htmls = f.read().decode('utf-8') return htmls # 分析抓取内容,选取标签时尽量选取闭合标签,标签成组的选择好对应 def __analysis(self, htmls): # 获取到需要的全部数据 root_html = re.findall(Spider.root_pattern, htmls) # 由于网页中一个块有两个相同的class类,其中第一个主播介绍 # 第二个才是需要的数据,所以选取奇数下标元素 root_info_html = root_html[1::2] # 最后获取到的数据列表 anchors = [] # 遍历列表,提取用户名和观看人数 for html in root_info_html: # 提取到的是带icon的部分 watch_num_str = re.findall(Spider.number_pattern_str, html) # 剔除icon部分 watch_num = re.sub(Spider.number_pattern, '', watch_num_str[0]) # 提取带icon的name的部分 name_str = re.findall(Spider.name_pattern_str, html) name = re.sub(Spider.name_pattern, '', name_str[0]) # 将名字和观看人数用字典存储,最后再用列表存储每个主播的数据 anchor = {'name': name, 'number': watch_num} anchors.append(anchor) return anchors # 精炼函数 # def __refine(self, anchors): # lam = lambda anchor: {'name': anchor['name'][0], 'number': anchor['number'][0]} # return map(lam, anchors) # 排序 def __sort(self, anchors): anchors = sorted(anchors, key=self.__sort_key, reverse=True) return anchors # 排序规则 def __sort_key(self, anchor): # 提取数字并计算 r = re.findall('\d*', anchor['number']) number = float(r[0]) if '万' in anchor['number']: number *= 10000 return number # 显示数据 def __show(self, anchors): # for anchor in anchors: # print(anchor['name'], ':', anchor['number']) for rank in range(0, len(anchors)): print("Rank ", rank+1, ": ", anchors[rank]['name'], " ", anchors[rank]['number']) # 入口方法 def go(self): htmls = self.__fetch_content() anchors = self.__analysis(htmls) anchors = self.__sort(anchors) self.__show(anchors) spider = Spider() spider.go()
唯有热爱方能抵御岁月漫长。