一、简单实例
1、需求:爬取熊猫直播某类主播人气排行
2、了解网站结构
分类——英雄联盟——"观看人数"
3、找到有用的信息
二、整理爬虫常规思路
1、使用工具chrome——F12——element——箭头——定位目标元素
目标元素:主播名字,人气(观看人数)
2、方法:使用正则表达式提取有用的信息
主播名字,人气(观看人数)
总结
- 爬虫前奏
1)明确目的
2)找到数据对应的网页
3)分析网页的结构找到数据所在的标签位置
- 步骤
1)模拟HTTP请求,向服务器发送这个请求,获取到服务器返回给我们的HTML
2)用正则表达式提取我们要的数据(名字,人气)
二、断点调试
我们以如下的一种常见错误,来演示如何通过PyCharm断点追踪的方式发现程序中的错误:
def foo(bar=[]):bar.append('bar') return bar >>>foo() ['bar'] >>>foo() ['bar', 'bar'] >>>foo() ['bar', 'bar', 'bar']
这里存在一个常见的错误就是误以为:函数在每次不提供可选形参时将参数设置为默认值,也就是本例中的[]
,一个空的list。
这里我们便可以通断点调试的方式进行追踪,在每次函数调用(不显示传递形参)时,观察形参值的变化。
如图所示为:
下图是以这段为例,来演示如何发现程序中的bug:
解决方案:
def foo(bar=None):if not bar: bar = [] bar.append('baz') return bar >>>foo() ['baz'] >>>foo() ['baz']
三、HTML结构分析基本原则
1、爬虫分析,最重要的一步,找到标签(即左右边界)
原则:
1)尽量选择有唯一标识性的标签
2)尽量选择离目标信息最近的标签
不同人选择的标签可能不同。
四、数据提取层级及原则
1、找到最近的定位标签(肉眼可见)
有关联的信息作为一组,找离这一组最近的定位标签
如:示例中的“主播姓名”和“人数”是有关联的,作为一组
2、判断选择的标签是否是唯一的(需代码验证)
3、尽量选择可闭合的定位标签
可闭合,是指可将目标信息包裹起来的定位标签。如:<... />
4、代码实战
1 # coding=utf-8
2 import re 3 from urllib import request 4 5 url = 'https://www.panda.tv/all' 6 r = request.urlopen(url) 7 htmls = r.read() 8 9 print(type(htmls)) # 打印type,结果是bytes类型 10 htmls = str(htmls, encoding='utf-8') # 将bytes转成utf-8 11 print(htmls)
运行结果
Traceback (most recent call last):File "E:/pyClass/thirtheen/spider.py", line 12, in <module>print(htmls) UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' in position 62321: illegal multibyte sequence
原因是使用的print()是win7系统的编码,但是win7系统的默认编码是GBK,解决方式,增加如下代码
1 import io
2 import sys 3 sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')
优化后代码
# coding=utf-8 import re from urllib import request import io import sys sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')class Spider():url = 'https://www.panda.tv/all'def __fetch_content(self):r = request.urlopen(Spider.url)htmls = r.read()htmls = str(htmls, encoding='utf-8') # 将bytes转成utf-8print(htmls)return htmlsdef go(self):self.__fetch_content()spider=Spider() spider.go()
五、正则分析HTML
1、获取root_html
正则表达式匹配<div class="video-info">和</div>之间的所有字符,有哪些方式?
匹配所有字符的方式
1)[\s\S]*?
2)[\w\W]*?
* 表示任意次
?表示贪婪
2、代码实战
1 # coding=utf-8 2 from urllib import request 3 import re 4 import io 5 import sys 6 sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030') 7 8 #以下代码解决SSL报错 9 import ssl 10 ssl._create_default_https_context = ssl._create_unverified_context 11 12 13 class Spider(): 14 15 #定义类变量 16 url = 'https://www.panda.tv/all' 17 root_html='<div class="video-info">([\s\S]*?)</div>' #([\s\S]*?)匹配任意字符 18 19 #获取服务器响应内容 20 def __fetch_content(self): 21 22 r=request.urlopen(url=Spider.url) 23 htmls = r.read() 24 htmls = str(htmls, encoding='utf-8') # 将bytes转成utf-8 25 return htmls 26 27 #分析并获取元素 28 def __analysis(self,htmls): 29 root_html=re.findall(Spider.root_html,htmls) 30 return root_html 31 32 33 def go(self): 34 htmls=self.__fetch_content() 35 self.__analysis(htmls) 36 37 38 spider=Spider() 39 spider.go()
遇到的问题:
1)乱码
参考python的编码问题整理
2)SSL错误
加入如下代码
import ssl ssl._create_default_https_context = ssl._create_unverified_context
3)类变量的引用
Spider.root_html
六、数据精炼
正则分析获取名字和人数
1、找到名字的左右边界、找到人数的左右边界
2、使用正则匹配,从root_html中获取名字和人数,并拼接成字典格式,列表接收
3、数据精炼——去掉多余的换行和空格(如果有),将value列表转化为str
代码示例:
1 # coding=utf-8 2 from urllib import request 3 import re 4 import io 5 import sys 6 7 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='gb18030') 8 9 # 以下代码解决SSL报错 10 import ssl 11 12 ssl._create_default_https_context = ssl._create_unverified_context 13 14 15 class Spider(): 16 # 定义类变量 17 url = 'https://www.panda.tv/all' 18 root_html = '<div class="video-info">([\s\S]*?)</div>' # ([\s\S]*?)匹配任意字符 19 name_pattern = '<span class="video-title" title="([\s\S]*?)">' 20 number_pattern = '<span class="video-number">([\s\S]*?)</span>' 21 22 # 获取服务器响应内容 23 def __fetch_content(self): 24 r = request.urlopen(url=Spider.url) 25 htmls = r.read() 26 htmls = str(htmls, encoding='utf-8') # 将bytes转成utf-8 27 return htmls 28 29 # 分析并获取元素 30 def __analysis(self, htmls): 31 anchors = [] # 使用list接收 32 root_html = re.findall(Spider.root_html, htmls) 33 for html in root_html: 34 name = re.findall(Spider.name_pattern, html) 35 number = re.findall(Spider.number_pattern, html) 36 anchor = {'name': name, 'number': number} # 组合成需要的字典格式 37 anchors.append(anchor) 38 39 return anchors 40 41 # 数据精炼——去掉多余的内容,转成需要的格式 42 def __refine(self, anchors): 43 l = lambda anchor: {'name': anchor['name'][0], 'number': anchor['number'][0]} # 上一步得到的name和number是列表类型,需要转成str 44 return map(l, anchors) 45 46 47 def go(self): 48 htmls = self.__fetch_content() 49 anchors = self.__analysis(htmls) 50 anchors=list(self.__refine(anchors)) 51 print(anchors) 52 53 54 spider = Spider() 55 spider.go()
九、业务处理——排序