Datawhale-爬虫-Task2(正则表达式)

什么是正则表达式

  • 定义:一套规则,可以在字符串文本中进行搜查替换等
  • 使用步骤:
    • 1.使用 compile() 函数将正则表达式的字符串编译成一个 pattern 对象
    • 2.通过 pattern 对象的一些方法对文本进行匹配,匹配结果是一个 match对象
    • 3.用 match 对象的方法,对结果进行操作
  • 常用方法:
    • match:从开始位置开始查找,一次匹配,即1次匹配成功则退出
      一般使用形式:
      match(string[,pos[,endpos]])
    • search:从任何位置开始查找,一次匹配
      search[string[, pos[, endpos]]]
      例子:
>>>import re
>>>pattern = re.compile(r'\d+')  #用于匹配至少一个数字

>>>m = pattern.match('one12twothree34four')  #查找头部,没有匹配
>>>print(m) #输出None

>>>m = pattern.match('one12twothree34four', 2, 10) #从'e'的位置开始匹配,没有匹配到
>>>print(m)
>>>m = pattern.match('one12twothree34four', 3, 10) #从'1' 的位置开始匹配,正好匹配上
>>>print(m)
<_sre.SRE_Match object at 0x10a42aac0>
>>>m.group(0)   #可忽略0
'12'
>>>m.start(0)   #可忽略0
3
>>>m.end(0)    #可忽略0
5
>>>m.span(0)  #可忽略0
(3, 5)

>>>m = pattern.search('one12twothree34four')  #这里如果使用match方法则不匹配
>>>m
<_sre.SRE_Match object at 0x10cc03ac0>
>>>m.group()
'12'

注:match方法只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而search匹配整个字符串,直到找到一个匹配。match和search都是一次匹配,只要找到一个匹配结果就返回,而不是查找所有匹配结果

  • findall:全部匹配,返回列表
    findall(string[, pos[, endpos]])
  • finditer:全部匹配,返回迭代器
    例子:
import re
pattern = re.compile(r'\d+')  #查找数字

result1 = pattern.findall('hello 123456 789')
result2 = pattern.findall('one1two2three3four4', 0, 3)

print(result1) #输出:['123456', '789']
print(result2) #输出:[]

r1 = pattern.finditer('hello 123456 789')
r2 = pattern.finditer('one1two2three3four4', 0, 10)
print(r1)
print(r2)
print('r1....')
for m1 in r1:
    print("matching string:{} position:{}".format(m1.group(), m1.span()))
""" 输出结果:matching string:123456 position:(6, 12)
			 matching string:789 position:(13, 16)"""
print('r2....')
for m2 in r2:
    print("matching string:{} position:{}".format(m2.group(), m2.span()))
 """ 输出结果:matching string:1 position:(3, 4)
			 matching string:2 position:(7, 8)"""

findall是以列表形式返回全部能匹配到的子串,如果没有匹配,则返回一个空列表。
finditer方法的行为跟findall的行为类似,也是搜索整个字符串,获得所有匹配的结果。但它返回一个顺序访问每一个匹配结果(Match对象)的迭代器

  • split:分割字符串,返回列表
    split(string[, maxsplit])
    其中maxsplit用于说明最大的分割次数,默认为全部分割
    例子:

    import re
    p = re.compile(r'[\s\,;]+')
    print(p.split('a,b;;c   d'))
    #输出结果:['a', 'b', 'c', 'd']
    
  • sub:替换
    re.sub(pattern, repl, string, count=0)
    其中,repl可以是字符串也可以是一函数:

    • 如果repl是字符串,则会使用repl去替换字符串每一个匹配的子串,并返回替换后的字符串,repl还可以使用id的形式来引用过分组,但不能使用编号0;
    • 如果repl是函数,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
    • count用于指导最多替换次数,不指定时全部替换
    • pattern:正则中的模式字符串

    例子:

import re
p = re.compile(r'(\w+) (\w+)')  #\w=[A-Za-z0-9]
s = 'hello 123, hello 456'

print(p.sub(r'hello world', s))   #使用'hello world'替换'hello 123'和'hello 456'
print(p.sub(r'\2 \1', s))
""" hello world, hello world
	123 hello, 456 hello"""
def func(m):
    return 'hi' + ' ' + m.group(2)

print(p.sub(func, s))
print(p.sub(func, s, 1))
"""hi 123, hi 456
hi 123, hello 456"""
  • 匹配中文
    • 中文是Unicode编码(utf-8也是Unicode编码),范围:主要在[u4e00-u9fa5]
    • 中文全角逗号一类的不在[u4e00-u9fa5]范围内
  • 贪婪与非贪婪模式
    • 贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配
    • 非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配
    • python里面数量词默认是贪婪模式
      例子:
import re
s = "abbbbbbc"
pattern1 = re.compile("ab+")
pattern2 = re.compile("ab*?")
m1 = pattern1.match(s)
m2 = pattern2.match(s)
s1 = m1.group()
s2 = m2.group()
print(s1) #abbbbbb
print(s2) #a 尽可能少匹配b所以没有b

案例

  • 结合requests、re两者的内容爬取豆瓣电影 Top 250里的内容
  • 要求抓取名次、影片名称、国家、导演等字段。

思路分析:

  • 首先由于豆瓣网页默认只有25部电影信息,所以URL是变化的。观察首页的URL为https://movie.douban.com/top250,下一页的URL为https://movie.douban.com/top250?start=25&filter=,下下页为https://movie.douban.com/top250?start=50&filter=所以可以使用一个while循环来遍历网页
while page <= 225:
	url = "https://movie.douban.com/top250?start=%s&filter=" % page
  • 分析网页信息,鼠标右键点击网页选择查看网页源代码即可查看页面信息。
  • 首先找到我们需要爬取的字段:名次,影片名称,国家,导演。
    名次:
    在这里插入图片描述
    影片名称:这里观察到名称有中英文两种title,这里只选择第一个中文title
    在这里插入图片描述
    国家和导演在同一个标签下:
    在这里插入图片描述相应的正则表达式:
    名次:'<em class="">(.*?)</em>
    名称:'<span class="title">(.*?)</span>
    导演:'.*?<p class="">.*?导演:(.*?)&nbsp;'
    国家:'<br>.*?/&nbsp;(.*?)&nbsp;/.*?</p>'
  • 最后在代码中加上请求头防止爬虫被阻止

完整代码:

import requests
import re
import json

class Movie(object):
    def __init__(self, rnd, n):
        # rnd: 名次,名字,导演
        # n:   国家
        self.info = {
            '排名': rnd[0],
            '电影名': rnd[1],
            '导演': rnd[2],
            '国家': n
        }

    def __repr__(self):
        return str(self.info)
    
def parse_html(url):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"}
    response = requests.get(url, headers=headers)
    text = response.text
    regix = '<em class="">(.*?)</em>.*?<span class="title">(.*?)</span>''.*?<p class="">.*?导演:(.*?)&nbsp;'
    nation = '<br>.*?/&nbsp;(.*?)&nbsp;/.*?</p>'
    rank_name_dire = re.findall(regix, text, re.S)
    r3 = re.findall(nation,text,re.S)
    info = [Movie(i[0], i[1]) for i in zip(rank_name_dire, r3)]
    for i in info:
        print(i)


def main():
    for offset in range(0, 250, 25):
        url = 'https://movie.douban.com/top250?start=' + str(offset) +'&filter='
        for item in parse_html(url):
            print(item)
            write_movies_file(item)

def write_movies_file(str):
    with open('douban_film.txt','a',encoding='utf-8') as f:
        f.write(json.dumps(str,ensure_ascii=False) + '\n')


if __name__ == '__main__':
    main()

猜你喜欢

转载自blog.csdn.net/TNTZS666/article/details/88081050