爬虫基础知识
一、技术选型 爬虫能做什么
1、scrapy VS requests+beautifulsoup
(1) requests和beautifulsoup都是库,scrapy是框架;
(2) scrapy框架可以加入requests和beautifulsoup;
(3) scrapy基于twisted,性能是最大的优势;
(4) scrapy方便扩展,提供了很多内置功能;
(5) scrapy内置的css和xpath selector非常方便,beautifulsoup最大的缺点就是慢。
2、常见类型的服务
(1) 静态网页
(2) 动态网页
(3) webservice(restapi)
3、爬虫作用
(1) 搜索引擎---百度、google、垂直领域搜索引擎
(2) 推荐引擎---今日头条
(3) 机器学习数据样本
(4) 数据分析(如金融数据分析)、舆情分析等
二、正则表达式
1、特殊字符
(1) ^ $ * ? + {2} {2,} {2,5} |
示例一:(^: 表示以什么开头)
import re
line = 'caoxuejin520'
regex_str = "^c.*"
if re.match(regex_str, line):
print("yes")
else:
print("no")
输出:yes
示例二:($: 表示以什么结尾)
import re
line = 'caoxuejin520'
regex_str = ".*0$"
if re.match(regex_str, line):
print("yes")
else:
print("no")
输出:yes
示例三:(*: 表示前面的字符可以出现任意多次)
import re
line = 'caoxuejin520'
regex_str = "^c.*0$"
if re.match(regex_str, line):
print("yes")
else:
print("no")
输出:yes
示例四:(?: 表示非贪婪匹配模式--从左往右匹配;贪婪匹配模式--从右往左匹配)
import re
line = 'caocaoxxuejin520'
regex_str = ".*?(c.*?x).*"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:caocaox
示例五:(+: 表示加号前面的字符至少出现一次)
import re
line = 'caocaoxxuejin520'
regex_str = ".*(c.+?x).*"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:caox
示例六:({1}: 表示前面的字符出现1次)
line = 'caocaoxxuejin520'
regex_str = ".*(c.{2}x).*"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:caox
示例七:({2,}: 表示前面的字符出现2次或2次以上)
import re
line = 'caocaoxxuejin520'
regex_str = ".*(c.{2,}x).*"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:caoxx
示例八:({2,5}: 表示前面的字符出现最少2次最多5次)
import re
line = 'caocaoxxuejin520'
regex_str = ".*(c.{2,5}x).*"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:caoxx
示例八:(|: 表示或)
import re
line = 'caoxuejin520'
regex_str = "(caoxuejin|caoxuejin520)"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:caoxuejin
import re
line = 'caoxuejin520'
regex_str = "(caoxuejin520|caoxuejin)"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:caoxuejin520
import re
line = 'caoxuejin520'
regex_str = "(caoxxuejin|caoxuejin)520"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:caoxuejin
import re
line = 'caoxuejin520'
regex_str = "((caoxxuejin|caoxuejin)520)"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:caoxuejin520
(2) [] [^] [a-z] .
示例:([34578]: 表示应该是集合中的任何一个数值;[0-9]: 表示在0-9区间内;[^1]: 表示不等于1)
import re
line = '15736170030'
regex_str = "(1[34578][0-9]{9})"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出: 15736170030
import re
line = '15sssssssss'
regex_str = "(1[34578][^1]{9})"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:15sssssssss
(3) \s \S \w \W
示例一:(\s: 表示空格)
line = '你 好'
regex_str = "(你\s好)"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:你 好
示例二:(\S: 表示匹配一个任意字符)
import re
line = '你很好'
regex_str = "(你\S好)"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:你很好
import re
line = '你非常好'
regex_str = "(你\S+好)"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:你非常好
示例三:(\w: 表示[A-Za-z0-9_]---数字、字母、下划线)
import re
line = '你_好'
regex_str = "(你\w好)"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:你_好
示例四:(\W: 与\w相反,表示非[A-Za-z0-9_]---非数字、字母、下划线)
import re
line = '你-好'
regex_str = "(你\W好)"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:你-好
(4) [\u4E00-\u9FA5] () \d
示例一:([\u4E00-\u9FA5]: unoicode编码,匹配汉字)
import re
line = 'study in 北京大学'
regex_str = ".*?([\u4E00-\u9FA5]+大学)"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:北京大学
示例二:(\d: 表示数字)
import re
line = 'xxx出生于2001年'
regex_str = ".*?(\d+)年"
match_obj = re.match(regex_str, line)
if match_obj:
print(match_obj.group(1))
输出:2001
附加:.: 表示任意字符;(): ()内的内容表示的是一个子表达式
2、正则表达式的简单应用及python示例
实例:检索出生年月日
#line = 'xxx出生于2001年6月1日' # 2001年6月1日
#line = 'xxx出生于2001/6/1' # 2001/6/1
#line = 'xxx出生于2001-6-1' # 2001-6-1
#line = 'xxx出生于2001-06-01' # 2001-06-01
line = 'xxx出生于2001-06' # 2001-06
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))
三、深度优先和广度优先
1、网站的树结构
(1) 网站url的结构图
(2) 网站url链接的结构图
2、深度优先算法实现
输出:A、B、D、E、I、C、F、G、H(递归实现)
过程:
def depth_tree(tree_node):
if tree_node is not None:
print(tree_node._data)
if tree_node._left is not None:
return depth_tree(tree_node._left)
if tree_node._right is not None:
return depth_tree(tree_node._right)
3、广度优先算法和实现
输出:A、B、C、D、E、F、G、H、I(队列实现)
过程:
def level_queue(root):
"""利用队列实现树的广度优先遍历"""
if root is not None:
return
my_queue = []
node = root
my_queue.append(node)
while my_queue:
node = my_queue.pop(0)
print(node.elem)
if node.lchild is not None:
my_queue.append(node.lchild)
if node.rchild is not None:
my_queue.append(node.rchild)
四、url去重方法
1、将访问过的URL保存到数据库中
2、将访问过的URL保存到set中,只需要o(1)的代价就可以查询URL
1000000000*2byte*50个字符/1024/1024/1024 = 9G
3、URL经过md5等方法哈希后保存到set中
4、用bitmap方法,将访问过的URL通过hash函数映射到某一位
5、bloomfilter方法对bitmap进行改进,多重hash函数降低冲突
五、彻底搞清楚unicode和utf8编码
1、字符串编码
(1) 计算机只能处理数字,文本转换为数字才能处理。计算机中8个bit作为一个字节,所以一个字节能表示最大的数字就是255;
(2) 计算机是美国人发明的,所以一个字节可以表示所有字符了,所以ASSCII(一个字节)编码就成为美国人的标准编码;
(3) 但是ASCII处理中文明细是不够的,中文不止255个汉字,所以中国制定了GB2312编码,用两个字节表示一个汉字。GB2312还把ASCII包含进去了,同理,日文,韩文等等上百个国家为了解决这个问题就都发展了一套字节编码,标准就越来越多,如果出现多种语言混合显示就一定会出现乱码;
(4) 于是unicode出现了,将所有语言统一到一套编码里;
(5) 看一下ASCII和unicode编码:
a. 字母A用ASCII编码十进制是65,二进制是0100 0001
b. 汉字“中”已经超出了ASCII编码的范围,用unicode编码是20013,二进制是01001110 00101101
c. A用unicode编码只需要前面补0,二进制是00000000 0100 0001
(6) 乱码问题解决了,但是如果内容全是英文,unicode编码比ASCII需要多一倍的存储空间,同时如果传输需要多一倍的传输;
(7) 所以出现了可变长的编码"utf-8",把英文变为一个字节,汉字3个字节。特别生僻的变成4-6字节,如果传输大量的英文,utf8作用就很明显了。
2、实例
(1) python2
Python 2.7.14 (default, Sep 23 2017, 22:06:14)
[GCC 7.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> s = 'abc'
>>> su = u'abc'
>>> s.encode("utf-8")
'abc'
>>> su.encode("utf-8")
'abc'
>>> s = "我用python"
>>> su = u"我用python"
>>> s.decode("utf-8").encode("utf-8")
'\xe6\x88\x91\xe7\x94\xa8python'
>>> su.encode("utf-8")
'\xe6\x88\x91\xe7\x94\xa8python'
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
(2) python3
Python 3.5.6 (default, Sep 10 2018, 10:36:11)
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> s="我用python"
>>> s.encode("utf-8")
b'\xe6\x88\x91\xe7\x94\xa8python'
>>> import sys
>>> sys.getdefaultencoding()
'utf-8'
>>>