使用 Python 3 编写简单爬虫

2 篇文章 0 订阅
2 篇文章 0 订阅

使用 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.22 AzZ丶小古子-----7.53 AzZ丶小炜-----3.94 AzZ丶仇冬生-----2.15 AzZ丶杰哥助您圆梦-----1.6

结语

至此,该简单爬虫已经基本实现。
主要是前期的网页代码分析,找到关键位置的代码
然后就是获取html文本,注意编码格式
分析和截取等等是爬取后的工作
爬虫的基本思想应该就是获取和分析提炼
其中又分成其他细小工作


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值