爬虫的五个步骤

  • 明确需求,想想爬什么数据
  • 确定含有需要数据的网站
  • 分析请求类别,请求时所携带的参数,模拟发送请求
  • 下载页面,分析页面,通过re,xpath来过滤response中返回的数据
  • 将数据储存起来

正则表达式

正则表达式的定义

描述了一种字符串的匹配模式,可以用来检查一个串是否含有某种字串,见匹配到的字串替换成其他的字符或者取出

应用场景

测试字符串的是否符合某个模式

批量替换文本中符合某个模式的字符

正则表达式的使用

re.match 尝试从字符串的起始位置匹配

re.search 扫描整个字符串,匹配一次

re.findall 扫描整个字符串,匹配出所有符合模式的子串,返回列表

re.finditer 扫描方法是和findall相似,只是返回的结果是一个生成器

re.compile 在匹配模式不变,但是被匹配对象变化的时候使用

# compile 的使用
compile(pattern,flags=0)
pet = r'^1(([3597]\d)|(47))\d{8}$'
print(re.match(pet,'13600000000'))

re_telephone = re.compile(pet)
print(re_telephone.match('13600000000'))

re.sub:

re.sub(pattern, repl, string, count=0, flags=0) 匹配目标串,将目标串中被匹配到的子串替换成指定的字符串,可以指定替换次数,不指定将替换所有

re.subn:

re.subn(pattern, repl, string, count=0, flags=0):

和re.sub一样,前者返回的是被替换的字符串,后者返回一个元组,第一个元素表示被替换的字符串,第二个元素表示被替换的次数

匹配模式

模式 描述
^ 匹配字符串的开头
$ 匹配字符串的末尾。
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[…] 用来表示一组字符,单独列出:[amk] 匹配 ‘a’,‘m’或’k’
[^…] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
re* 匹配0个或多个的表达式。
re+ 匹配1个或多个的表达式。
re? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re{ n} 精确匹配 n 个前面表达式。例如, o{2} 不能匹配 “Bob” 中的 “o”,但是能匹配 “food” 中的两个 o。
re{ n,} 匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。“o{1,}” 等价于 “o+”。“o{0,}” 则等价于 “o*”。
re{ n, m} 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
a| b 匹配a或b
(re) 匹配括号内的表达式,也表示一个组
(?imx) 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
(?-imx) 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
(?: re) 类似 (…), 但是不表示一个组
(?imx: re) 在括号中使用i, m, 或 x 可选标志
(?-imx: re) 在括号中不使用i, m, 或 x 可选标志
(?#…) 注释.
(?= re) 前向肯定界定符。如果所含正则表达式,以 … 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。
(?! re) 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功
(?> re) 匹配的独立模式,省去回溯。
\w 匹配字母数字及下划线
\W 匹配非字母数字及下划线
\s 匹配任意空白字符,等价于 [\t\n\r\f].
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0-9].
\D 匹配任意非数字
\A 匹配字符串开始
\Z 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。
\z 匹配字符串结束
\G 匹配最后匹配完成的位置。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。
\B 匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
\n, \t, 等. 匹配一个换行符。匹配一个制表符。等
\1…\9 匹配第n个分组的内容。
\10 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。

正则匹配列表

# 校检数字

数字:^[0-9]*$
n位的数字:^\d{n}$
至少n位的数字:^\d{n,}$
m-n位的数字:^\d{m,n}$
零和非零开头的数字:^(0|1-9*)$
非零开头的最多带两位小数的数字:^(1-9*)+(.[0-9]{1,2})?$
带1-2位小数的正数或负数:^(-)?\d+(.\d{1,2})?$
正数、负数、和小数:^(-|+)?\d+(.\d+)?$
有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
非零的正整数:^[1-9]\d$ 或 ^(1-9){1,3}^\+?[1-9][0-9]*
非零的负整数:^-1-90-9"$ 或 ^-[1-9]\d$
非负整数:^\d+^[1-9]\d*|0
非正整数:^-[1-9]\d*|0^((-\d+)|(0+))
非负浮点数:^\d+(.\d+)? 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0
非正浮点数:^((-\d+(.\d+)?)|(0+(.0+)?))^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0
正浮点数:^[1-9]\d.\d|0.\d[1-9]\d 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))
负浮点数:^-([1-9]\d.\d|0.\d[1-9]\d)^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))
浮点数:^(-?\d+)(.\d+)? 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)

# 校检字符
汉字:^[\u4e00-\u9fa5]{0,}$
英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
长度为3-20的所有字符:^.{3,20}$
由26个英文字母组成的字符串:^[A-Za-z]+$
由26个大写英文字母组成的字符串:^[A-Z]+$
由26个小写英文字母组成的字符串:^[a-z]+$
由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+
禁止输入含有~的字符:[^~\x22]+

# 特殊需求的匹配
Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
InternetURL:[a-zA-z]+://[^\s]*^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
电话号码("XXX-XXXXXXX""XXXX-XXXXXXXX""XXX-XXXXXXX""XXX-XXXXXXXX""XXXXXXX"和"XXXXXXXX)^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$ 
国内电话号码(0511-4405222021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
身份证号(15位、18位数字)^\d{15}|\d{18}$
短身份证号码(数字、字母x结尾)^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)^[a-zA-Z][a-zA-Z0-9_]{4,15}$
密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线)^[a-zA-Z]\w{5,17}$
强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间)^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$  
日期格式:^\d{4}-\d{1,2}-\d{1,2}
一年的12个月(0109112)^(0?[1-9]|1[0-2])$
一个月的31(0109131)^((0?[1-9])|((1|2)[0-9])|30|31)$ 
钱的输入格式:
有四种钱的表示形式我们可以接受:"10000.00""10,000.00", 和没有 "分""10000""10,000"^[1-9][0-9]*$ 
这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$ 
一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$ 
这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$ 
必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10""10.2" 是通过的:^[0-9]+(.[0-9]{2})?$ 
这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$ 
这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$ 
13个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$ <br>
备注:这就是最终结果了,别忘了"+"可以用"\*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
中文字符的正则表达式:[\u4e00-\u9fa5]
双字节字符:[^\x00-\xff]    (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
空白行的正则表达式:\n\s*\r    (可以用来删除空白行)
HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? />    (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$)    (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
腾讯QQ号:[1-9][0-9]{4,}    (腾讯QQ号从10000开始)
中国邮政编码:[1-9]\d{5}(?!\d)    (中国邮政编码为6位数字)
IP地址:\d+\.\d+\.\d+\.\d+    (提取IP地址时有用)
IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))

进程,线程,协程

进程

一个任务就是一个进程,拥有自己独立的堆栈段,代码段,数据段以及状态的保存,进程是系统中程序执行和资源分配的基本单位

进程在计算机系统中是按照固定的时常轮播调度执行的

进程和进程之间通过内核,网络,或者文件的方式来相互通信

线程

一个进程任务的完成需要一个或者多个子任务组合成完成,所以一个进程至少有一个线程,子任务就是这个进程的线程

线程共享进程的数据,多线程变相的延长了这个进程在计算机系统中所执行的时间

进程和线程的区别

  • 进程是计算机系统进行资源分配和调度的独立单位,是数据层面,然而去实现进程中任务的是线程,线程是进程的实体,是进程的生命所在,是CPU调度和分配的基本单位,线程本身没有资源或者只拥有很少的一部分资源(线程计数器,寄存器,栈),但是它与其他线程共享所属进程的所有资源

  • 一个程序至少需要一个进程,而一个进程至少需要用有一个线程

  • 线程划分尺度小于进程,所以多线程的程序并发性高

  • 进程执行的时候拥有独立的寄存单元,多线程共享进程的内存,提高了程序的运行效率

  • 线程没有只能共享进程的资源,所以线程不能单独存在

  • 优缺点:

    • 多线程执行效率高,但是高并发不利于数据的管理和保护
    • 进程中如果只有一个线程,实现便于数的保护,但是效率低

协程

微线程,比线程更小,是用户态的轻量级的线程

协程 拥有自己的寄存器,上下文和栈,完全由用户控制调度,调度时会将自己的状态保存到寄存器,当重新切换回来的时候,还可以继续这调度之前

的状态执行

协程的优缺点:

  • 无需线程上下文切换的开销,避免了无意义 的调度,提高性能,但是程序员必须自己承担调度任务,而且失去了标准线程使用多CPU的能力
  • 无需原子操作,同步锁,减低开销
  • 无需切换控制流程,简化编程
  • 高并发,高扩展,低成本 一个CPU支持上万的协程不是问题,很适合用于高并发的处理
  • 缺点:
    • 无法利用多核,协程还是在单线程的层面上运行,所以想利用多核,还需要和线程配合
    • 发生阻塞操作的时候会阻塞整个程序

scrapy框架

关于python爬虫的学习总结

scrpay中,数据流的走向:

  1. spider中 的start_url或者start_requests形成初始的request对象发送给Engine
  2. Engine判断出是request对象,将对象发送给Schedue
  3. Engine将从Schedule中获取request对象
  4. 获取到的request对象经过Download Middle Wares发送给Downloader
  5. Downloader从网络上获取数据后会生成response,这个response会再次经过Downloader Middle Ware 将response发送个Engine
  6. Engine获取到response之后,将response经过Spider Middle Ware 发送给Spiders,Spiders会处理response中的数据
  7. Spiders将处理之后的结果经过spider Middle Ware 发送给Engine
  8. Engine接收到Spider发送的结果之后进行判断:
    1. 如果结果是Item,那么发送个Item Pipeline处理这个Item
    2. 如果结果是request,那么发送给Schedule,重新进入请求循环

所涉及到的模块的作用

Scrapy Engine

负责整个数据流的控制以及事件触发的作用

Schedule

存储request

Downloader

负责request请求网络后的下载工作

Spiders

负责数据清洗,用户验证,以及数据储存的业务逻辑****

Download Middle Ware

处理发送到Downloader的request以及从Downloader返回的response

应用场景:

  • 下载器下载前,必须添加一些信息
  • 一个request想要生成另一个request
  • 需要删除一部分拥有某个特征的request的时候

Spiders Middle Ware

介于Engine 和 Spiders之间

处理 Request,Response以及Item

应用场景:

  • 处理输出的Item或者request
  • 隐藏输入的response的错误
  • 继续处理start_request中的内容
  • 自定义去重算法

分布式爬虫

关于python爬虫的学习总结

将scrapy中的Schedule更换成为Redis数据库,实现将Spider输出的request请求共享

共享的request请求就可以在多台机器上处理,这就实现了分布式

优点:

  • 充分利用多台机器的带宽
  • 充分利用多台机器的IP

相关文章: