使用 Python 3 编写简单爬虫
本文主要是学习Python3一个小阶段的记录,边看视频边做的一个爬虫——抓取主播名字和人气然后进行降序排序,当作复习。
学习视频:https://coding.imooc.com/class/136.html
···准备工作
1)明确目的:直播平台某个游戏的主播和人气
2)找到对应的页面例如:https://www.huya.com/g/dnf
3)使用浏览器的审查元素,查看对应文本位置
···编码部分
1)模拟http请求发送到服务器,返回html
2)使用正则表达式提取关键数据
3)对关键数据进行精炼和排序
准备工作
打开浏览器,进入网址https://www.huya.com/g/dnf
按F12打开浏览器的审查元素,Ctrl+B使用鼠标选择元素,找到对应代码位置,如图所示:
开始编码
1)引入模拟http请求所需的内置模块。
创建一个Spider类,
定义私有方法 __fetch_content(),
定义公开方法 go 作为入口方法:
from urllib import request
class Spider(): # 私有方法,模拟请求,获取html内容
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read() # 这里保存的是字节流文件
html = str(htmls,encoding='UTF-8') # 全部转换成字符串
print (html) # 输出结果测试
return html
def go(self): # 公开的入口方法
html = self.__fetch_content() # 调用
spider = Spider()
spider.go()
2)成功获取到html文本内容之后,找到如图片所示的代码块
可见, class=”txt” 的父标签 span 下的两个子标签 span,正是需要获取的数据。
所以正则表达式的 pattern 应该为<span class="txt">[\s\S]*?</span>
其中,中括号里的是匹配的字符集,包含了
\s
(空白字符)
\S
(非空白字符)
*
(后续内容)
这样就可以代表匹配的是所有的文本内容。而中括号后的
?
代表了非贪婪模式
意思是后续内容中匹配到</span>
就停止匹配。
3)导入正则匹配所需要的re模块
定义一个分析方法 __analysis()来分析并截取对应的数据:
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read()
html = str(html,encoding='UTF-8')
return html
def __analysis(self,html):
root_pattern = '<span class="txt">[\s\S]*?</span>' # 正则表达式
root_html = re.findall(root_pattern,html)
print(root_html[0]) # 输出结果测试
return root_html
def go(self):
html = self.__fetch_content()
html = self.__analysis(html)
spider = Spider()
spider.go()
得到的结果是:
<span class="txt">
<span class="avatar fl">
<img data-original="https://huyaimg.msstatic.com/avatar/1069/a8/028a1c545ab90184f502cd7a9b1a2a_180_135.jpg" src="//a.msstatic.com/huya/main/assets/img/default/84x84.jpg" onerror="this.onerror=null; this.src='//a.msstatic.com/huya/main/assets/img/default/84x84.jpg';" alt="AzZ丶狂人" title="AzZ丶狂人">
<i class="nick" title="AzZ丶狂人">AzZ丶狂人</i>
</span>
发现由于使用了非贪婪模式的正则表达式,匹配到第一个</span>
只包含了一个数据。所以需要把右边界字符换成相对唯一的</li>
再次运行测试,成功的包含了两个需要的数据:
<span class="txt">
<span class="avatar fl">
<img data-original="https://huyaimg.msstatic.com/avatar/1069/a8/028a1c545ab90184f502cd7a9b1a2a_180_135.jpg" src="//a.msstatic.com/huya/main/assets/img/default/84x84.jpg" onerror="this.onerror=null; this.src='//a.msstatic.com/huya/main/assets/img/default/84x84.jpg';" alt="AzZ丶狂人" title="AzZ丶狂人">
<i class="nick" title="AzZ丶狂人">AzZ丶狂人</i>
</span>
<span class="num"><i class="num-icon"></i><i class="js-num">11.6万</i></span>
</span>
</li>
4)对得到的结果再次进行正则匹配,分别获取主播名字和人气:
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read()
html = str(html,encoding='UTF-8')
return html
def __analysis(self,html):
root_pattern = '<span class="txt">[\s\S]*?</li>'
# 下列表达式([\s\S]*?)外面还加了一层小括号,仅保留小括号里的内容
name_pattern = '<i class="nick" title="[\s\S]*?">([\s\S]*?)</i>'
number_pattern = '<i class="js-num">([\s\S]*?)</i>'
root_html = re.findall(root_pattern,html)
anchors = [] # 创建一个list
for html in root_html: # 使用for循环,将每个结果存储为字典,添加到list中。
name = re.findall(name_pattern, html)
number = re.findall(number_pattern, html)
anchor = {'name': name, 'number': number}
anchors.append(anchor)
print(anchors[0]) # 输出结果测试
return anchors
def go(self):
html = self.__fetch_content()
html = self.__analysis(html)
spider = Spider()
spider.go()
得到示例结果:
{'name': ['AzZ丶七天'], 'number': ['6.1万']}
5)定义一个__refine()方法,对anchors里的数据进行精炼,即只保留主干字符:
strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。
在go()方法中将返回的结果存储为list
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read()
html = str(html,encoding='UTF-8')
return html
def __analysis(self,html):
root_pattern = '<span class="txt">[\s\S]*?</li>'
name_pattern = '<i class="nick" title="[\s\S]*?">([\s\S]*?)</i>'
number_pattern = '<i class="js-num">([\s\S]*?)</i>'
root_html = re.findall(root_pattern,html)
anchors = []
for html in root_html:
name = re.findall(name_pattern, html)
number = re.findall(number_pattern, html)
anchor = {'name': name, 'number': number}
anchors.append(anchor)
return anchors
def __refine(self, anchors):
#匿名函数
f = lambda x: {
'name': x['name'][0].strip(), # strip()用于移除空白字符
'number': x['number'][0]
}
return map(f, anchors) # map()方法,将上述函数作用于list中的每一个元素
def go(self):
html = self.__fetch_content()
html = self.__analysis(html)
html = list(self.__refine(html)) # 将对象转换成list
print (html[0])
spider = Spider()
spider.go()
测试的输出结果为:
{'name': 'AzZ丶七天', 'number': '5.4万'}
6)定义一个__sort()方法,对anchors进行排序
定义一个__show()方法,将结果打印输出:
from urllib import request
import re
class Spider():
url = 'https://www.huya.com/g/dnf'
def __fetch_content(self):
r = request.urlopen(Spider.url)
html = r.read()
html = str(html,encoding='UTF-8')
return html
def __analysis(self,html):
root_pattern = '<span class="txt">[\s\S]*?</li>'
name_pattern = '<i class="nick" title="[\s\S]*?">([\s\S]*?)</i>'
number_pattern = '<i class="js-num">([\s\S]*?)</i>'
root_html = re.findall(root_pattern,html)
anchors = []
for html in root_html:
name = re.findall(name_pattern, html)
number = re.findall(number_pattern, html)
anchor = {'name': name, 'number': number}
anchors.append(anchor)
return anchors
def __refine(self, anchors):
l = lambda x: {
'name': x['name'][0].strip(),
'number': x['number'][0]
}
return map(l, anchors)
def __sort(self, anchors):
# 作为key的number里存在中文“万”字,需要重新设定;reverse将升序改为降序
return sorted(anchors,key=self.__sort_seed,reverse=True)
def __sort_seed(self, anchors):
r = re.findall('\d*', anchors['number'])
# r在这里是list,[0]是上面表达式匹配的数字,其他则仍然是字符串
number = float(r[0])
if '万' in anchors['number']:
number *= 10000
return number
def __show(self, anchors):
for rank in range(0, range(0,5): #len(anchors)这里改成输出前五
print(rank + 1, anchors[rank]['name']
+ '-----' + anchors[rank]['number'])
def go(self):
html = self.__fetch_content()
anchors = self.__analysis(html)
anchors = list(self.__refine(anchors))
anchors = self.__sort(anchors)
anchors = self.__show(anchors)
spider = Spider()
spider.go()
输出结果:
1 胜哥002-----10.2万
2 AzZ丶小古子-----7.5万
3 AzZ丶小炜-----3.9万
4 AzZ丶仇冬生-----2.1万
5 AzZ丶杰哥助您圆梦-----1.6万
结语
至此,该简单爬虫已经基本实现。
主要是前期的网页代码分析,找到关键位置的代码
然后就是获取html文本,注意编码格式
分析和截取等等是爬取后的工作
爬虫的基本思想应该就是获取和分析提炼
其中又分成其他细小工作