爬虫系列二
一切为了数据挖掘的准备
在中国大学MOOC网站上学习的北京理工大学嵩天老师的免费爬虫课程课件,简单易懂,感兴趣的戳 嵩天老师爬虫课程。侵删
六 正则表达式
编译:将符合正则表达式语法的字符串转化为正则表达式特征,只有在compile之后才算正则表达式
6.1常用操作符
操作符 | 说明 |
---|---|
r | 可不是用反斜杠,表示原始字符串 |
^ | 匹配字符串开头 |
$ | 匹配字符串的末尾 |
. | 匹配任意字符,常用’.*’,表示匹配任意个任意字符 |
[…] | 用来表示匹配字符的范围。常用’[a-z]’,或’[amk]’(amk中的任意一个) |
[^…] | 匹配不在[]中的字符:[^abc]匹配除abc外的字符 |
* | 匹配0个或多个字符 |
+ | 匹配1个或多个字符 |
? | 前一个字符0次或1次扩展’abc?’,表示ab,abc |
{n} | 大括号前面的表达式重复n次 |
{n,} | 重复>= n次 |
{n,m} | 重复n到m次 |
a|b | 匹配a或b |
() | 匹配括号内的表达式,表示一个组,内部只能使用 |
\w | 匹配字母、数字、下划线[A-Za-z0-9_] |
\W | 匹配非字母、数字、下划线 |
\d | 任意数字,等价于[0-9] |
\D | 任意非数字 |
\A | 匹配从字符串开始 |
\s | 匹配任意空白字符,等价于[\t\n\r\f] |
\S | 匹配任意非空字符 |
举例:
- 整数形式的字符串(包括正负):’^-?\d+$’
- 正整数形式的字符串:’1[0-9]*$ ’
- 中国境内邮政编码,6位:’[1-9]\d{5}’
- 中文字符:’[\u4e00-\u9fa5]’
- IP地址为4段,每段0-255,
- 0-99表达为’[1-9]?\d’,
- 100-199:‘1\d{2}’,
- 200-249:‘2[0-4]\d’,
- 250-255:‘25[0-5]’.
- 总体可以表达为’(([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5]).){3}([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])’
6.2正则表达的基本使用
import re
match = re.match(r'[1-9]\d{5}', str)
if match: #判断是否有匹配结果,否则会报错
match.group(0)
6.2.1 表示类型
- raw string类型,原生字符串类型,指不包含对转义字符再次转义的字符串,表示为: r’[1-9]\d{5}’
- string类型,表示为:’[1-9]\\d{5}’
6.2.2 re库主要功能函数
-
re.search(pattern,string,flags=0),
扫描整个字符串并返回第一个成功的匹配,返回一个match对象,否则返回none- pattern:正则表达式的字符串
- string:待匹配的字符串
- flags:控制标志,多个标志可以用’|'指定,,如re.I|re.M
- re.I,忽略正则表达式的大小写,[A-Z]能匹配小写字母
- re.M,表达式中的’^'操作符能将给定字符串的每行当作匹配开始
- re.S,使用’.'匹配所有字符,原本默认匹配除换行外的所有字符
-
re.match(pattern, string, flags = 0),
从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功,返回none -
re.findall(pattern, string, flags = 0),搜索字符串,以列表类型返回全部能匹配的字串
-
re.split(pattern, string[,maxsplit=0,flags=0]),
字符串按照正则表达式的结果进行分隔,返回列表类型。- 被匹配的部分去除掉,只剩下没有被匹配的部分分割开
- 可以设置最大分割次数,当能被匹配的数目超过设置的最大次数时,超过分割数的部分作为整体分隔开
>>> re.split(r'[1-9]\d{5}','BIT100081 TSU100084') ['BIT', ' TSU', ''] >>> re.split(r'[1-9]\d{5}','BIT100081 TSU100084',maxsplit=1) ['BIT', ' TSU100084']
-
re.finditer(),找到所有的匹配对象,并作为迭代器返回,每次迭代都返回一个match对象
re.finditer(pattern, string, flags=0)
例:
it = re.finditer(r"\d+","12a32bc43jf3")
for match in it:
print (match.group() )
- re.sub(pattern,repl,string,count=0,flags=0),
检索和替换,返回替换后的字符串- pattern:正则中的字符串
- repl:替换的字符串,也可以是个函数
- string:要被查找替换的原始字符串
- count:模式匹配后替换的最大次数,默认为0替换所有的匹配
6.2.3 re.compile
regrex = re.compile(pattern,flags)
- regrex.search()
- regrex.match()
- regrex.findall()
- regrex.split()
- regrex.finditer()
- regrex.sub()
6.3.re库的Match对象
match对象是一次匹配的结果,包含匹配的很多信息
6.3.1 Match对象的属性:
- .string,待匹配的文本
- .re,匹配时使用的pattern
- .pos,搜索文本的开始位置
- .endpos,所有文本的结束位置
6.3.2 对象的方法(用findall试试)
- .group(0)
- .groups()
- .start(),匹配字符串在原始字符串中的起始位置
- .end(),匹配字符串在原始字符串中的结束位置
- .span,返回(.start(), .end())
>>> line = "Cats are smarter than dogs"
>>> m = re.match( r'(.*) are (.*?) .*', line, re.M|re.I)
>>> m.string
'Cats are smarter than dogs'
>>> m.re
re.compile('(.*) are (.*?) .*', re.IGNORECASE|re.MULTILINE)
>>> m.pos
0
>>> m.endpos
26
>>> m.group()
'Cats are smarter than dogs'
>>> m.group(0)
'Cats are smarter than dogs'
>>> m.group(1)
'Cats'
>>> m.group(2)
'smarter'
>>> m.groups()
('Cats', 'smarter')
>>> m.start()
0
>>> m.end()
26
>>> m.span()
(0, 26)
>>> m=re.search(r'[1-9]\d{5}','BIT100081 TSU100084')
>>> m.groups()
()
>>> m.group()
'100081'
>>> m.group(0)
'100081'
6.4.贪婪匹配和最小匹配
默认贪婪匹配,匹配最长的字符串
>>> m = re.match(r'PY.*N','PYANBNCNDN')
>>> m.group(0)
'PYANBNCNDN'
当想要最小匹配时
- *?,前一个字符0次或无限次扩展,最小匹配
- +?,前一个字符1次或无限次扩展,最小匹配
- ??,前一个字符0次或1次扩展,最小匹配
- {m,n}?,扩展前一个字符m至n次,最小匹配
>>> m = re.match(r'PY.*?N','PYANBNCNDN')
>>> m.group(0)
'PYAN'
七 练习
7.1.淘宝商品爬取
目标:获取淘宝搜索页面的信息,提取其中的商品名称和价格。需要找到淘宝的搜索接口,并且处理翻页
7.1.1 思考
- 假设搜索一样东西
- 起始页网址:
https://s.taobao.com/search?q=书包&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.2017.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306 - 第二页网址
https://s.taobao.com/search?q=书包&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.2017.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306&bcoffset=3&ntoffset=3&p4ppushleft=1%2C48&s=44 - 第三页网址
https://s.taobao.com/search?q=书包&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.2017.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306&bcoffset=0&ntoffset=6&p4ppushleft=1%2C48&s=88
- 起始页网址:
搜索url:“https://s.taobao.com/search?" + “商品类别”;翻页url:“https://s.taobao.com/search?"+商品类别 +”&s=” + 44*页数
- 查看robots协议:https://s.taobao.com/robots.txt
User-agent: *
Disallow: /
当爬虫类人行为,可以有限制的爬取
7.1.2代码
import requests
import re
from bs4 import BeautifulSoup
#提交请求
def getHTMLText(url):
try:
r = requests.get(url,timeout = 30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
print('wrong')
# 从页面中提取信息
def parsePage(ilt,html):
try:
plt = re.findall(r'\"view_price\"\:\"[\d\.]*\"',html) #搜索所有的价格信息
tlt = re.findall(r'\"raw_title\"\:\".*?\"',html) #最小匹配,只取最后一个",搜索名称信息
for i in range(len(plt)):
price = eval(plt[i].split(':')[1]) #去掉最外层单双引号
title = eval(tlt[i].split(':')[1])
ilt.append([price,title])
except:
print(" ")
#打印信息
def printGoodsList(ilt):
tplt = "{:4}\t{:8}\t{:16}"
print(tplt.format("序号","价格","商品名称"))
count = 0
for g in ilt:
count = count+1
print(tplt.format(count,g[0],g[1]))
def main():
#商品,爬取页面的深度,url
goods = "书包"
depth = 2
start_url = 'https://s.taobao.com/search?q=' + goods
infoList = []
#访问每个页面
for i in range(depth):
try:
url = start_url + '&s=' + str(44*i)
html = getHTMLText(url)
parsePage(infoList,html)
except:
continue
printGoodsList(infoList)
main()
1-9 ↩︎