爬虫(三):正则表达式 ---数据的分类,json数据,re模块的使用,案例:爬取猫眼电影

一、数据的分类

1、结构化数据

特点:数据以行为单位,每一个数据表示一个实体。每一行数据的属性都是一样的。

举例:关系型数据库中的表就是结构化数据。

处理方法:sql

2、半结构化数据

特点:结构化数据的另一种形式。他并不符合关系型数据的特点,不能用关系型模型来描述。但是这种数据包含相关标记,有用来分割语义元素以及字段进行分层的描述。因此也被称为自描述结构

举例:xml,html,json

处理方法:正则,xpath,jsonpath,css选择器。

3、非结构化数据

特点:没有固定结构的数据。

举例:文档、图片、音频、视频。

处理方法:常常用二进制形式来做整体保存。

二、json数据

1、json是什么语言的内容

json 是 js 语言中用来保存对象和数组的一种数据结构。json 数据本质上是字符串。

2、js中数组和对象

js 的数组:

var array = ['aaa','bb','cc'] // 和python中的列表对应

js 的对象:

var obj = {name:'zhangsan',age:10} //和python中的字典对应
name = obj.name

3、json数据的解析方法

json模块。

对字符串的操作:

json.loads(json_str) --->python的list或者dict
json.dumps(python的list或者dict) --->json_str

对 json文件的操作:

json.load(fp) --->从json文件中读出json数据,返回一个python的list或者dict
json.dump(python的list或者dict,fp) --->python的list或者dict转化成json_str保存到fp对应的文件中。

4、json的意义

json 作为数据格式进行传输,具有较高的效率。

json 不像 xml 那样具有严格的闭合标签,所以 json 作为数据传输的时候,他的数据有效占比比 xml 高很多。在相同流量下, json 比 xml 作为数据传输,传输的数据更多。

三、正则表达式

1、元字符

(1)匹配边界

^ 行首
$ 行尾

(2)重复次数

0次或1次
* > =0次
+ > =1次
{n} n次
{n,} > n次
{n,m} >=n ,<=m

(3)各种字符的表示

[ ] 匹配括号中任意一个字符,单字符
[abc] 匹配 a 或者 b 或者 c
\d 0-9任意数字
\w 数字字母下划线
\s 空白字符:换行符、制表符、空格
\b 单词边界
. 除换行符以外的任意字符

2、re模块的使用

python 中 re 模块是用来做正则处理的。

(1)re 模块的使用步骤

# 1、导包
import re
# 2、将正则表达式编译成一个pattern对象
pattern = re.compile(
    r'正则表达式', # r表示元字符
    '匹配模式'
)
# 3、用pattern对象来使用相应的方法来匹配内容。

(2)pattern 对象的方法

Pattern 对象的一些常用方法主要有:

match 从起始位置开始查找,只匹配一次
search 从任何位置开始查找,只匹配一次
findall 全部匹配,返回列表
finditer 全部匹配,返回迭代器
split 分割字符串,返回列表
sub 替换

I. match方法

# match方法:默认从头开始,只匹配一次,返回一个match对象。
pattern.match(
    '匹配的目标字符串',
    start, # 开始的位置 ---缺省,默认为start=0
    end, # 结束的位置 ---缺省,默认为end=-1
)

match 对象的属性:

match.group() # 获取匹配内容
match.span() # 匹配的范围
match.start() # 开始位置
match.end() # 结束位置

# 这些方法都可以带一个参数0,但是不能写1,1表示取第一分组。
match.group(0)
match.span(0)
match.start(0)
match.end(0)

match.groups() # 将所有分组的内容,按顺序放到一个元组中返回

match方法示例:

示例一

import re
pattern = re.compile(r'\d+')
content = 'one1two2three3four4'
m1 = pattern.match(content) # 从0位置开始匹配
m2 = pattern.match(content,2,10) # 从e位置开始匹配
m3 = pattern.match(content,3,10) # 从1位置开始匹配

print(m1)
print(m2)
print(m3.group())
print(m3.group(0)) # 可以省略0
print(m3.span(0))
print(m3.start(0))
print(m3.end(0))
运行结果:
None
None
12
12
(3,5)
3
5

示例二

import re
pattern = re.compile(r'([a-z]+) ([a-z]+)',re.I) # re.I表示忽略大小写
m = pattern.match('Hello World Wide Web')
# 匹配成功,返回一个match对象
print(m)
# 返回匹配成功的子串
print(m.group())
# 返回匹配成功的整个子串的索引
print(m.span(0))
# 返回第一个分组匹配成功的子串
print(m.group(1))
# 返回第一个分组匹配成功的子串索引
print(m.span(1))
# 等价于m.group(1),m.group(2)
print(m.groups()) # 将所有分组的内容,按顺序放到一个元组中返回
运行结果:
<re.Match object; span=(0, 11), match='Hello World'>
Hello World
(0, 11)
Hello
(0,5)
('Hello','World')

II. search方法

# search方法:从任意位置开始匹配,只匹配一次,返回一个match对象。
pattern.search(
    '匹配的目标字符串',
    start, # 开始的位置 ---缺省,默认为start=0
    end, # 结束的位置 ---缺省,默认为end=-1
)

search方法示例:

示例一

import re
pattern = re.compile(r'\d+')
content = 'one12twothree34four'
m = pattern.search(content)
print(m)
print(m.group())
m = pattern.search(content,10,30) # 指定字符串区间
print(m)

print(m.group())
print(m.span())
运行结果:
<re.Match object; span=(3, 5), match='12'>
12
<re.Match object; span=(13, 15), match='34'>
34
(13, 15)

示例二

import re
# 将正则表达式对象编译成pattern对象
pattern = re.compile(r'\d+')
content2 = 'hello 123456 789'
# 使用search查找匹配的子串,如果不匹配就返回None
m = pattern.search(content2)
if m:
    # 使用 match 获取分组信息
    print(m.group())
    # 起始位置和结束位置
    print(m.span())
运行结果:
123456
(6, 12)

III. findall

# findall方法:全文匹配,匹配多次,将每次匹配到的结果放到list中返回。
pattern.findall(
    '匹配的目标字符串',
    start, # 开始的位置 ---缺省,默认为start=0
    end, # 结束的位置 ---缺省,默认为end=-1
)

findall 方法实例:

示例一

import re
# 将正则表达式编译成一个pattern对象
pattern = re.compile(r'we')
# 使用findall方法全局搜索 we 返回列表 ['we','we','we']
m = pattern.findall('we work well welcome')
print(m)

示例二

# 使用\b元字符进行匹配,只匹配出 we 单词,不匹配出其他的含有 we的单词。
import re
# 将正则表达式编译成一个pattern对象
pattern = re.compile(r'\bwe\b')
# 使用findall方法全局搜索 we 返回列表 ['we','we','we']
m = pattern.findall('we work well welcome')
print(m)

示例三

import re
# 生成pattern对象,查找连续的数字
pattern = re.compile(r'\d+')
result1 = pattern.findall('hello 123456 789')
# 从0开始 1对应的是下标3,2对应的是下标7,匹配下标0-7的数字
result2 = pattern.findall('one1two2three3four4',0,8)
print(result1) # ['123456','789']
print(result2) # ['1','2']

IV. finditer

# finditer方法:全文匹配,匹配多次,返回一个迭代器。
pattern.finditer(
    '匹配的目标字符串',
    start, # 开始的位置 ---缺省,默认为start=0
    end, # 结束的位置 ---缺省,默认为end=-1
)

例子:

import re
pattern = re.compile(r'\d+')
content1 = 'hello 123456 789'
content2 = 'one1two2three3four4'
result_iter1 = pattern.finditer(content1)
result_iter2 = pattern.finditer(content2,0,10)

print(type(result_iter1))
print(type(result_iter2))
print('result1.....')
for m1 in result_iter1:
    print('matching string:{},position:{}'.format(m1.group(), m1.span()))
print('result2.....')
for m2 in result_iter2:
    print('matching string:{},position:{}'.format(m2.group(), m2.span()))
运行结果:
<class 'callable_iterator'>
<class 'callable_iterator'>
result1.....
matching string:123456,position:(6, 12)
matching string:789,position:(13, 16)
result2.....
matching string:1,position:(3, 4)
matching string:2,position:(7, 8)

V. split

# split方法:切分,按照正则所表示内容进行切分字符串,返回切分后的每个字串。
pattern.split(
    '要切分的字符串',
    '切分次数', # 默认是全部切分
)

例子:

import re
pattern = re.compile(r'[\s\,\;]+')
a = pattern.split('a,b;,;c ;,;   d')

print(a) # ['a', 'b', 'c', 'd']

VI. sub

# sub方法:用指定字符串,替换正则表达式所匹配到的内容。
pattern.sub(
    repl, # 替换成什么
    content, # 替换什么
    count, # 替换次数,默认替换所有
) --->替换后的字符串。

"""
repl替换内容可以使用函数:
	函数要求:
		1、函数必须有参数,参数就是正则匹配目标字符串所得到的每个match对象。
		2、这个函数必须要有返回值,返回值必须是字符串,这个字符串将来就作为替换的内容。
"""

例子:

import re

p = re.compile(r'(\w+) (\w+)')

s = 'hello 123,hello 456'
# 使用'hello world'替换'hello 123'和'hello 456'
print(p.sub(r'hello world',s))
# 将 二分组和一分组互换位置
print(p.sub(r'\2 \1',s)) # 引用分组


import re
def add(m):
    return str(int(m.group()) + 1000)
# 涨工资每个人涨1000
content = 'zhangsan:3000,lisi:4000'
p = re.compile('\d+')
result = p.sub(add,content)
print(result)

VII. 分组

分组在正则表达式中使用()来表示,一个括号就是一个分组。

分组的作用:

1、筛选特定内容。

2、可以在同一个表达式中引用前面的分组。

  \1:引用第一分组

  \2:引用第二分组

3、findall配合分组使用

import re
content = '

正则表达式

'
p = re.compile(r'<(html)><(h1)>(.*)') print(p.findall(content)) # [('html', 'h1', '正则表达式')]

VIII. 贪婪模式和非贪婪模式

  1. 贪婪和非贪婪的区别在于匹配内容的多少。
  2. 贪婪使用 * 来控制匹配次数的。正则默认是贪婪模式。
  3. 非贪婪使用?来控制的。
  4. 在表示数量控制元字符后面加一个?,此时就表示这个数量控制符取最小值,也就是非贪婪。
import re
# 贪婪和非贪婪来控制表示次数的元字符
# pattern = re.compile(r'ab??') # [0,3][0,1]
# result = pattern.findall('abbbc')
# print(result)

s = 'aa
test1
bb
test2
cc'
# ['
test1
', '
test2
']
pattern = re.compile('
.*?
'
) # ['
test1
bb
test2
']
# pattern = re.compile('
.*
')
result = pattern.findall(s) print(result)

IX. 匹配模式

re.S .可以匹配换行符
re.I 忽略大小写

X. 万能正则匹配表达式(.*?)

.*?(尽可能少匹配任意内容)配合re.S使用

案例:爬取猫眼电影

import json
import re

import requests

# 将数据写入json文件中
def write_to_json(infos):
    with open('movie.json','w',encoding='utf-8') as fp:
        json.dump(infos,fp)

# 解析页面内容
def parse_page(html_str):
    # 测试页面内容是否能拿到,这一步必须要测试
    # print(html_str)
    # 正则筛选页面的原则:一步步缩小匹配范围。
    dl_p = re.compile(r'
(.*?)
'
,re.S) dl_content = dl_p.search(html_str).group() dd_p = re.compile(r'
(.*?)
'
,re.S) dd_list = dd_p.findall(dl_content) # print(dd_list) """ 电影名称 主演 上映时间 评分 详情url """ for dd in dd_list: name_p = re.compile(r'" title="(.*?)" class="',re.S) name = name_p.search(dd).group(1) # 电影名称 # print(name) actor_p = re.compile(r'

(.*?)

'
,re.S) actor = actor_p.search(dd).group(1).strip() # 主演 # print(actor) releasetime_p = re.compile(r'

(.*?)

'
,re.S) releasetime = releasetime_p.search(dd).group(1) # 上映时间 # print(releasetime) score_p = re.compile(r'

(.*?)(.*?)

'
,re.S) # score = score_p.search(dd).group(1) + score_p.search(dd).group(2) score = score_p.findall(dd) # [('9.', '5')] score = ''.join(score[0]) # 评分 # print(score) detail_url_p = re.compile(r',re.S) detail_url = 'https://maoyan.com' + detail_url_p.search(dd).group(1) # 详情页url # print(detail_url) # 设置爬取数据 item = {} item['name'] = name item['actor'] = actor item['releasetime'] = releasetime item['score'] = score item['detail_url'] = detail_url # print(item) infos.append(item) # 保存到json文件 # 一个方法做一件事情 def main(): # 确定基础url base_url = 'https://maoyan.com/board/4?offset=%s' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36' } # 分页 for i in range(10): response = requests.get(base_url % (i*10),headers=headers) parse_page(response.text) if __name__ == '__main__': infos = [] # 提取数据,将数据添加到infos中 main() # 将数据写入文件 write_to_json(infos) # 从 movie.json文件中读出电影数据 # infos = json.load(open('movie.json','r')) # for item in infos: # print(item)

作业:

1、正则匹配

  • 匹配正整数
  • 匹配非负整数
  • 非正整数
  • qq邮箱 (qq号5-14位)
  • 匹配11位电话号码(1 3-9)
  • 匹配日期(2020-02-04)
  • 长度为8-10的用户密码(开头字母:必须大写,每一位可以是数字、字母、下划线)

2、股吧

1、字段

阅读

评论

标题

作者

更新时间

详情页

2、10页内容保存到 json 文件。

你可能感兴趣的:(爬虫)