学习资源:慕课网课程 聚焦Python分布式爬虫必学框架Scrapy 打造搜索引擎
一.基本概念
技术选型比较
scrapy 与 request +beautifulsoup
1.1两者是框架,后者是两个库,是不同层级的概念
1.2.前者基于twisted,是异步io框架,在性能上非常有优势,它也可以加入request 和beautifulsoup两个库
1.3.前者方便扩展,提供非常多的内置功能
1.4.前者内置css和xpath selector非常方便,beautifulsoup是纯python写的,缺点就是慢,可以慢好几百倍
网页类型
静态网页
基于css和js,服务器一次就返回页面,不做更改
具体应用:博客
动态页面
基于不同的请求动态地填充不同的数据
爬虫的作用:
1.搜索引擎:百度,Google,垂直领域的搜索引擎(与前两者的区别是,后者是知道自己要爬取哪些数据和网站的,而前者不知道,爬去互联网上的全部数据)
2.推荐引擎:今日头条
3.机器学习数据样本
4.数据分析:如金融分析,舆情分析等
二.正则表达式
1.特殊字符:
1.:^ $ * ? + {2} {2,} {2,5} |
2.:[] ,[0-9],[a-z],[^]
3. \s ,\S,\w,\W
4.[\u4E00-\u9FA5],\d
.
1.1 “^”表示以某个符号开头的
例如: ^b.*
"^b"表示 以b开头
"."表示任意字符可以是中文也可以是英文
"*"表示可以出现0-n次(任意次)
1.2 "$"字符表示以某个符号结尾的
例如:".*3$"表示以3结尾的任何字符串
1.3 “*”表示任意次
对比没有用“*”的结果:
1.4 “?”表示非贪婪匹配
贪婪匹配的概念:反向地匹配,从后往前找,找到最右边的那个
提取练习:
line = “boooooobby123”---------提取----->boooooob
例子1:regex_str = '.(b.b).’
前面和后面的".*"代表不限定这个模式是开头或者结尾,()里面的是真正需要提取的
看看结果:
为啥不是boooooob?因为这个是贪婪匹配,从后往前找的,找到了就直接结束了
例子2:regex_str = '.?(b.b). '?'让匹配从左边开始,我们看效果:
还是不行,为什么多了个b呢?
答案是:前面设了非贪婪,但是后面的部分还是从后面匹配的,前面找到了第一个b停止,后面的从后面找到第一个b,也停止,最后返回的是中间的结果所以是boooooobb
例子3:regex_str = ‘.?(b.?b).*’
这样的两次非贪婪,即从左往右找,找到了第一个就返回的策略就可以达到我们的目的,再来一个例子加深体会:
1.5 “+" 与“*”的功能类似,是用来限定出现次数的
“+"表示出现1-n次,“*”代表出现0-n次
例子1:
例子2:
例子3:
1.6 {2} {2,} {2.5}
这些都是次数限制,意思分别是:出现两次,出现两次以上,出现2次以上5次以下
例子:regex_str = ‘.(b.{2,4}b).’
1.7 “|” 代表或者
例子1:regex_str = ‘(bobby|bobby123)’ 注意加括号,不然group(1)函数就不能用了
结果:
例子2:regex_str = ‘(bobby123|bobby)’
结果:
例子3:
注意3个例子的区别,加上括号还可以这样使用:
如果想要整个字符串,还可以这样:
[] 表示任何一个都可以,相当于数据库的IN()函数
2.1:里面放想要的字符
例子:regex_str = ‘([abcd]obby123)’
2.2:里面放区间
例子:提取电话号码:regex_str = ‘1[2345678][0-9]{9}’
2.3:里面放^,表示取反
例子:regex_str = ‘1[2,3,4,5,6,7,8,9][^1]{9}’ 表示不等于1出九次都可以
需要注意的是:中括号里面放.和*就不表示任意字符了,而是代表两个字符
3.1" \s"表示是个空格
例子:
line = ‘你 好’
regex_str = ‘(你\s好)’
3.2 “\S” 表示只要不是空格都可以
例子:
line = ‘你好哇’
regex_str = ‘(你好\S)’
3.3"\w" 等同于[a-zA-Z_0-9]加上中文字符
3.4"\W"等同于[^(a-zA-Z_0-9)] 可以匹配空格
4.1[\u4E00-\u9FA5]表示汉字
注意与括号的配合使用,".*“的贪婪模式和”?"的反贪婪
4.2 “\d”表示数字
2.练习
2.1 提取生日
line = “XXX出生于1997年12月30日”
line = “XXX出生于1997/12/30”
line = “XXX出生于1997-12-30”
line = “XXX出生于1997年12月”
line = “XXX出生于1997-12”
regex_str = ‘.*出生于(\d{4}[年/-]\d{1,2}([月/-]\d{1,2}[日]|[月/-]\d{1,2}|[月/-]$|$))’
match_obj = re.match(regex_str,line)
if match_obj:
# 表示取第一个括号里面的东西
print(match_obj.group(1))
这个regex_str可以完美地对上面的几种格式提取
三.深度优先算法与广度优先算法
感受一下网站的url分层结构设计
深度优先算法
用递归实现
广度优先算法
用队列实现
四.爬虫url去重策略:
1.访问过的url存到数据库
2.访问过的url存到set中
3.经过md5等方法哈希后保存到set中
4.用bitmap方法,将访问过的url通过hash函数映射到某一位
5.用bloomfilter对bitmap进行改进,多重哈希函数降低冲突
五.字符串编码
unicode为了统一,由于定长很容易处理
utf-8为了节省空间,但是不好处理
聪明的办法:
不同系统的编码方式:
window下:gb2312
python3:Unicode
python2:系统的和Unicode的都支持
Linux:utf-8
一个例子说明不同系统的编码:
cmd下输入python2
\>>>s = 'abc'
\>>>su =u 'abc'#su是Unicode编码的
\>>>s.encode(“utf8”)#将gb2312字符串转换成utf-8编码
\>>>su.encode(“utf8”)#将Unicode类型的字符串转换成utf-8编码
上述的操作看起来没有任何问题
但是换成中文试一下:
\>>>s = '你好哇'
\>>>su = '你好哇'#su是utf8编码的
\>>>s.encode(“utf8”)#将中文字符串转换成utf-8编码
会出错
\>>>su.encode(“utf8”)#将中文字符串转换成utf-8编码
会出错
出错原因:
字符串s在window下是gb2312编码的,而encode函数是需要s为Unicode编码的对象
所以应该要先转换编码:
\>>>s.decode("gb2312").encode(“utf8”)#将中文字符串转换成utf-8编码
#如果是Linux系统,就是这样写的:s.decode("utf8").encode(“utf8”)
#为何已经是utf8了,不能直接用encode?因为encode函数需要s是Unicode编码的
python3中全都用Unicode来表示,python2工程需要设置编码是‘utf-8’,但是python3不需要