目录
- 案例代码调整说明
- 前奏:爬取目的与分析网页结构
- 编写爬虫的整体思路
正式编写
- 模拟HTTP请求,发送至服务器
- 调试工具
- 调试变量HTML,字节码变str型
- 正则分析HTML
- 精炼数据
- 业务处理:sorted排序
- 写函数的技巧分享
(六)业务处理:sorted排序
以后,就与爬虫没什么关系了,主要是跟业务紧密挂钩。
能够进行排序、比较大小的数据,只能是观看人数。
1.目的
- 把人数从字典中取出来;
- 将取出来的数字字符串文本,转换成数字的数据类型。
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/lol'
list_pattern = '<li class="game-live-item" data-gid="1" data-lp="[\d]*">([\s\S]*?)</li>'
name_pattern = '<i class="nick" title="[\s\S]*?">([\s\S]*?)</i>'
number_pattern = '<i class="js-num">([\s\S]*?)</i>'
def __fetch_contents(self):
r = request.urlopen(Spider.url)
htmls = r.read()
htmls = str(htmls,encoding = 'utf-8')
return htmls
def __analysis(self,htmls):
list_html = re.findall(Spider.list_pattern,htmls)
anchors = []
for html in list_html:
name = re.findall(Spider.name_pattern,html)
number = re.findall(spider.number_pattern,html)
anchor = {'name':name,'number':number}
anchors.append(anchor)
return anchors
def __refine(self,anchors):
l = lambda anchor:{
'name':anchor['name'][0].strip(),
'number':anchor['number'][0]
}
return map(l,anchors)
def __sorted(self,anchors): #排序函数
anchors = sorted(anchors,key = self.__sort_seed) #系统内置函数sorted,提供排序功能
#anchors:表示要进行排序的字典
# key可以接受一个函数
return anchors
def __sort_seed(self,anchor): #定义种子函数,以指定字典中哪个元素来比较大小
#接受一个参数anchor,它是anchors中的一个单元字典
return anchor['number'] #上面sorted函数,指定的比较对象是观看人数
def __show(self,anchors):
for anchor in anchors:
print(anchor['name']+'------'+anchor['number'])
def go(self):
htmls = self.__fetch_contents()
anchors = self.__analysis(htmls)
anchors = list(self.__refine(anchors))
anchors = self.__sorted(anchors) #将排序函数放入主方法,集中调用
self.__show(anchors)
spider = Spider()
spider.go()
-->
小战------100.2万 #结果虽有,但升序排列,方向反了
黑店百地------116.2万
浪D------117.8万
陆雪琪------121.7万
骚男------136.2万
英雄联盟赛事------158.5万
岁月-搁浅------16.5万
Ning------181.5万
kRYST4L------198.2万
二兮------21.0万
凉艾------21.3万
瓜皮诺【919】------21.3万
PM-姚夜残------22.0万
曙望丶十三------22.2万
2.更换排序顺序
...
def __sorted(self,anchors):
anchors = sorted(anchors,key = self.__sort_seed,reverse = True)
#将reverse改为True
return anchors
def __sort_seed(self,anchor):
return anchor['number']
-->
盛世-叽歪鬼------99.8万 #虽成了降序
你的声优倩【招收主播】------99.1万
小绒尾------9.5万 #但仍不对。原因是:sorted里面,返回的是str,不是数字
曙望-臭臭没吃饱------85.3万 #因为,如果是str,比较的只是一个一个数字,非数值比较
陆雪琪------84.8万
黑店百地------82.6万
曙望-汉堡------82.2万
Zz1tai姿态------74.8万
盛世-纪小鹿【526】------68.6万
温稚------54.8万
3.应该将观看人数,全部换算成数字。依靠数字的数值大小,来排序
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/lol'
list_pattern = '<li class="game-live-item" data-gid="1" data-lp="[\d]*">([\s\S]*?)</li>'
name_pattern = '<i class="nick" title="[\s\S]*?">([\s\S]*?)</i>'
number_pattern = '<i class="js-num">([\s\S]*?)</i>'
def __fetch_contents(self):
r = request.urlopen(Spider.url)
htmls = r.read()
htmls = str(htmls,encoding = 'utf-8')
return htmls
def __analysis(self,htmls):
list_html = re.findall(Spider.list_pattern,htmls)
anchors = []
for html in list_html:
name = re.findall(Spider.name_pattern,html)
number = re.findall(spider.number_pattern,html)
anchor = {'name':name,'number':number}
anchors.append(anchor)
return anchors
def __refine(self,anchors):
l = lambda anchor:{
'name':anchor['name'][0].strip(),
'number':anchor['number'][0]
}
return map(l,anchors)
def __sort(self,anchors):
anchors = sorted(anchors,key = self.__sort_seed,reverse = True)
return anchors
def __sort_seed(self,anchor): #⭐注意!下面是\d,不是d\,否则会报错!
r = re.findall('\d*',anchor['number']) #将数字提取出来了。但此时的r,仍是str文本
number = float(r[0]) #将str的数字,转换成数字类型。此时不能是int(),因为会涉及小数
if '万' in anchor['number']:
number *= 10000 #如果有单位万,就要乘以10000
return number
def __show(self,anchors):
for anchor in anchors:
print(anchor['name']+'------'+anchor['number'])
def go(self):
htmls = self.__fetch_contents()
anchors = self.__analysis(htmls)
anchors = list(self.__refine(anchors))
anchors = self.__sort(anchors) #⭐注意!这里不是sorted(),而是sort()。前者与内置函数同名,会引发错误!
self.__show(anchors)
spider = Spider()
spider.go()
-->
TheShy------383.1万
Zz1tai姿态------273.1万
V1ncent丶文森特------262.0万
kRYST4L------234.0万
雨初歇-北枫------182.3万
黑店百地------161.9万
浪D------138.9万
小战------137.2万
骚男------129.2万
...
sort排序,是个开放函数,让别人来决定到底用哪个内容进行排序。
七月老师:
搜索引擎解决问题,是最高效的。有人觉得,花钱买课不提问,是吃亏。其实,你提问了,才是吃亏。因为你错过了一次自我学习、自我成长的机会。
上面的不足,在于没有主播的排名序号12345。
4.增加排名序号
因为列表已经排序好了,所以主播的序号,其实就是列表元素的序号。
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/lol'
list_pattern = '<li class="game-live-item" data-gid="1" data-lp="[\d]*">([\s\S]*?)</li>'
name_pattern = '<i class="nick" title="[\s\S]*?">([\s\S]*?)</i>'
number_pattern = '<i class="js-num">([\s\S]*?)</i>'
def __fetch_contents(self):
r = request.urlopen(Spider.url)
htmls = r.read()
htmls = str(htmls,encoding = 'utf-8')
return htmls
def __analysis(self,htmls):
list_html = re.findall(Spider.list_pattern,htmls)
anchors = []
for html in list_html:
name = re.findall(Spider.name_pattern,html)
number = re.findall(spider.number_pattern,html)
anchor = {'name':name,'number':number}
anchors.append(anchor)
return anchors
def __refine(self,anchors):
l = lambda anchor:{
'name':anchor['name'][0].strip(),
'number':anchor['number'][0]
}
return map(l,anchors)
def __sort(self,anchors):
anchors = sorted(anchors,key = self.__sort_seed,reverse = True)
return anchors
def __sort_seed(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 rank in range(0,len(anchors)): #用for in range 来获取列表元素的各个序号
print('rank '+str(rank + 1)
+' : '+anchors[rank]['name']
+' : '+anchors[rank]['number'])
#rank的起始值是0,所以要加上1 #str只能和str相加,不能是int
def go(self): #总控方法很好,清晰展现了数据处理的每个关键过程。因为是平级、没有嵌套,所以自己好维护,别人也一目了然
htmls = self.__fetch_contents() #获取页面
anchors = self.__analysis(htmls) #正则分析HTML
anchors = list(self.__refine(anchors)) #精炼数据
anchors = self.__sort(anchors) #排序
self.__show(anchors) #规范展示
spider = Spider()
spider.go()
-->
rank 1 : TheShy : 398.7万
rank 2 : Zz1tai姿态 : 292.5万
rank 3 : V1ncent丶文森特 : 252.8万
rank 4 : kRYST4L : 233.1万
rank 5 : 雨初歇-北枫 : 181.2万
rank 6 : 黑店百地 : 179.9万
rank 7 : 浪D : 165.4万
rank 8 : 芜湖神 : 141.9万
rank 9 : 骚男 : 141.4万
rank 10 : 小战 : 127.2万
小技巧:在VSCode中,如何在很长的文件里,找到想要的函数呢?
Ctrl + Shift + O,输入show,会快速跳转到该方法。
如果后续有处理,那就不是show方法了,那可能就是编写存取到数据库中的方法。
五、写函数的技巧分享
1.pyhton中的注释写法
(1)块注释
模块注释:多行字符串(str)形式,最高级
'''
This is a module.
'''
from urllib import request
类注释:多行字符串形式
class Spider():
'''
this is a class.
'''
url = 'https://www.huya.com/g/lol'
方法注释:多行字符串形式
def __fetch_content(self):
'''
this is a method.
'''
(2)单行代码注释
既可以在单行代码的后面,也可以在上面(推荐),并空行。
r = request.urlopen(Spider.url) #request对象下面,有个urlopen方法:接受抓取网页的url地址 #将类变量url传送到urlopen中来 #实例方法中读取类变量
htmls = r.read() #用read方法读取出html #html是一些字节码bytes
或
#request对象下面,有个urlopen方法:接受抓取网页的url地址 #将类变量url传送到urlopen中来 #实例方法中读取类变量
r = request.urlopen(Spider.url)
#用read方法读取出html #html是一些字节码bytes
htmls = r.read()
2.代码格式的规范性
(1)善用空行
有意义的空行,会方便阅读。但勿滥用。
(2)一个函数,建议小巧,勿写太多行
优点:
- 会确保函数名就是函数功能的最好描述
- 体积小,越灵活,复用越方便。你如果写太多逻辑,几乎不能复用。
国外建议:8行左右。
七月老师建议:控制在10-20行
3.需求变更
1.虎牙网站属于模板化建站,想爬取虎牙网站下其他的游戏,如王者荣耀,只需要在代码里,改个网站即可。
2.如果不抓取虎牙,可能就要全改。即抵御业务需求变化的能力太差。
因为本项目的爬虫,只是实现了功能,并没有融入面向对象的思维。
大部分人就是这个写法,很糟糕。
七月老师:
课程git上有面向对象版本的爬虫,可下载学习。
4.关于中大型爬虫
(1)爬虫框架
本案例属于小型爬虫,没使用任何的库或框架。
如果,想要写中大型的爬虫,比如分布式、多线程的爬虫,那就可以使用以下库:
- BeautifulSoup
- Scrapy
上述两个爬虫框架,在小的、简单的爬虫项目中,没必要用。
七月老师:
个人习惯,能不用就不用,专注于业务处理、解决问题、
不要为了学框架而学框架,非常耗时。
爬虫只是个过渡技术,非终极技术。真正重要、更有意义的是,是精炼数据、与业务结合处理。而不是,为了爬而爬。
当然,除了上述需求,还有爬虫效率、爬虫海量数据的存取等。
(2)IP被封
最有可能遇到的问题是,IP被封。因为你爬取的频率太高了。所以,建议将频率设小一点。
如果被封,那就要寻找这样一个代理IP库。
Comments | NOTHING