文章目录
简介
- 这几篇博客是一个系列,最终目标是能独立编写爬虫项目
- 技术点包括反爬处理手段、正则表达式使用、抓包技术、模拟请求等,熟练掌握urllib模块,最终还要学习Scrapy框架
- 当然,也可能会结合其他模块,提升效率必须要有模块化的思想
- 如果你是小白,想快速感受一下爬虫到底要怎么做,可以看我的github博客
Python基础
- 这里要用到的Python基础包括
- 基础语法
- 函数及模块
- 文件操作
- 异常处理
- 面向对象编程
- 在我的Python基础专栏有详细介绍,这里就不赘述,但这些东西是必须要会的!
Web基础
- 爬虫是在爬网页信息(当然也包含APP等),了解基本的网页HTML+CSS代码是必须的
- 网页分类:
- 静态网页(包括一些拖管博客)
- 动态网络
- WebService(RestAPI)
- 这部分在我的PythonWeb专栏中有详细介绍,花一天时间即可!
爬虫基础
- 技术选型
- 很多人使用request+beautifulsoup,这里主要使用Scrapy,为什么?
- 前面这俩只是库,而S是框架
- S中可以加入r/b
- S基于twisted,是一个异步框架,性能好(gevent了解一下)
- S方便扩展,也提供了很多内置功能
- S内置CSS+Xpath Selector(lxml是基于C语言写的,快),beautifulsoup最大的确点是慢!
- 很多人使用request+beautifulsoup,这里主要使用Scrapy,为什么?
- 爬虫作用
- 搜索引擎
- 推荐引擎
- 机器学习样本
- 数据分析
- 正则表达式
- 处理字符串的必备,我的github博客中有详细介绍
- 把握两个关键点:特殊字符和元字符
import re # . 匹配除换行符(\n、\r)之外的任何单个字符 # * 匹配前面的子表达式零次或多次 # + 匹配前面的子表达式一次或多次 # 特殊字符:^ $ string = 'Roooooooooyy is handsome!' regex = '^R.*\!$' match = re.match(regex, string) print(match) # Roooooooooyy is handsome! # 特殊字符:? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符 regex = '.*(R.*y).*' print(match.group(1)) # Roooooooooyy 贪婪的意思就是从右往左匹配(整个字符串都参与) regex = '.*?(R.*?y).*' # 非贪婪,针对后面的y print(match.group(1)) # Roooooooooy 这里的括号是字符串提取模式,配合group # ? * +一般都是针对前面字符的 # 特殊字符:{} string = 'bbybaaby' regex = '.*(b.{3,}y).*' # 前面的字符出现3次及以上;{2,5}表示2次到5次都可 print(match.group(1)) # baaby regex = '.*(b.{1}y).*' # bby # 特殊字符:[] 中括号中的任何一个都可以 string = 'roy233kun666*' regex = '([rst]oy)|([opq]ykun)' print(match.group(1)) # roy regex = '.*([0-9]{3})' # 输入一个范围,还可以[a-z] print(match.group(1)) # 666 这就叫贪婪模式,从后开始! regex = '.*?([0-9 ^6]{3})' print(match.group(1)) # 233 匹配不是6的数,^这里代表 非 regex = '.*([*])' print(match.group(1)) # * 中括号中的特殊字符不再有特殊意义,或者有其他含义 # 元字符:\s 匹配任何空白字符 # \S 匹配任何非空白字符,等价于 [^\f\n\r\t\v] string = 'Hello World' regex = '(Hello\sWorld)' # Hello World # 元字符:\w 匹配非字母、数字、下划线,等价于 [^A-Z a-z 0-9 _] # 同样,\W 相反 string = 'aAb157_' regex = '\w*' print(match) # aAb157_ # 元字符: \d 匹配数字 string = '出生日期:2007年' regex = '.*?(\d+)' # 非贪婪,否则只能匹配到7 # 匹配连续的数字:非贪婪或者用{4}指定,这两种方式 print(match.group(1)) # 2007 # 提取汉字:\u4E00-\u9FA5 string = 'roy杨瑞nidaye大爷' regex = '.*([\u4E00-\u9FA5]+)' # 注意,前面的 .* 必须加上 print(match.group(1)) # 爷
- 如果匹配的字符前有其他字符,必须加上
.*
,否则无法开始匹配;要通盘考虑! - 如果
?
被当做非贪婪模式,可以理解成从左开始匹配;默认贪婪(非左贪右) - regex规则只有一个,为什么提取字串有group(1/2/3…)呢?这是针对一个规则多处匹配,别在pattern并列写多个
()
- 看个实例:提取用户出生日期
BFS和DFS
- 网页结构和URL采用树结构
- 以伯乐在线网站为例:URL层次设计
- 明白了这一点,可以让我们在系统爬取的时候制定策略:
- 还要注意链接环路,例如每个页面都会加上“首页”,如果按序爬取会出现死循环;此时需要加上爬虫去重
- 明白了这一点,可以让我们在系统爬取的时候制定策略:
- 如何爬取上面的URL结构,抽象出来就是如何遍历此树形结构
- 学过数据结构就知道,树的遍历分两种:深度优先(递归)和广度优先(队列)
- 深度优先以访问根节点的次序,又包括三种:先序、中序、后序遍历
- 结构抽象如下:
- 深度优先代码:
- 以上是先序遍历:ABDEICFGH
- 注意这里递归出口直接
return
调用了,也可以不写子节点的None判断,直接走else:return
- 广度优先代码:只需要一个队列即可!
- 相当于对应树的层序遍历:ABCDEFGHI
- 去重策略
- 将访问过的URL保存到数据库中,获取下一个URL时去查询,看看有没有爬过
- 显然,效率非常低下,但是最简单!
- 将访问过的URL保存到内存中
- 查的快,但吃内存啊!
- URL经过MD5等方法后保存到set中
- md5算法的好处是可以将任意长度的字符串压缩到相同的固定长度
- 数据内容相差很小,其值也有较大差别
- 原数据计算出md5值很容易
- 配合上集合set,其特点就是不重复,和Python的字典键值一样
- 用bitmap方法,将访问过的URL通过hash函数映射到某一位
- 只要是hash,就必须考虑冲突处理
- 用bloomfilter方法对bitmap改进,主要是多重hash函数降低冲突
- 这种用位映射级别的压缩,压的是相当猛的!所以此方法常用!
- 怎么用?后面说!
- 将访问过的URL保存到数据库中,获取下一个URL时去查询,看看有没有爬过
字符编码
- 这个问题在Python基础中也说过,这里再次强调
- Unicode想一统天下,但是存储的时候会多浪费空间,于是出现了utf8可变长编码
- 英文就用一字节,中文就用三字节,这样在存储或者传输的时候能节省空间提高效率
- 但是问题来了,长度不一样处理器就不好办了,于是:
- 看个例子:
- 把握一点,
encode
中会用默认编码decode
,但默认的是ASCII
编码,所以直接报错! - Windows中编码环境为gb2312,Linux编码环境为utf8,但同理,还是要先decode
- 当然,以上是python2的问题,Python3中用str对象解决了
- 定义时全部Unicode,如果文件操作需要utf8会提示!咱再整(就上图那个流程)
- 但是问题来了,长度不一样处理器就不好办了,于是:
- 下面我们开始具体学习使用Scrapy框架
小结
- 这篇重点回顾了Python和爬虫基础,对技术选型和正则表达式详细介绍
- 强烈推荐看一下里面链接的文章,很多东西有了基础概念才能相互联系,知识点融会才能提升