re模块

一、正则基础

1、正则使用方法

1. [] 原子表
[a]  匹配小写字母a
[ab]  匹配小写字母a或者b
ab    匹配小写字母ab
[abc] 匹配小写字母a或者b或者c
[AaBbCc]  匹配字母a或者b或者c
[123]  匹配数字1或者2或者3
123     匹配数字123
[a-z]   匹配任意一位小写字母
[A-Z]   匹配任意一位大写字母
[a-zA-Z] 匹配任意字母
[0-9]  匹配任意一个数字
[a-zA-Z0-9] 匹配任意一个数字或字母
[] 中的不管写多少都只能匹配一个

2 {m}  限定符  控制前面的正则表达式的匹配m次  不能单独使用
a{2}  匹配2个a  等同于aa
[a-z]{2} 匹配任意2个小写字母
[A-Z]{2} 匹配任意2个大写字母
[A-Za-z]{2} 匹配任意2个字母
1a{2}   匹配1aa
[0-9][a-z]{2}  匹配一个数字和2个小写字母


3 {m,} 限定符  控制前面的正则表达式的匹配至少m次  不能单独使用
a{2,}  匹配至少2个a
[a-z]{2,} 匹配至少任意2个小写字母
[A-Z]{2,} 匹配任意至少2个大写字母
[A-Za-z]{2,} 匹配至少任意2个字母
1a{2,}   匹配至少为 1aa
[0-9][a-z]{2,}  匹配一个数字和至少2个小写字母

4 {m, n} 限定符  控制前面的正则表达式的匹配m-n次  不能单独使用
a{2, 4}  匹配2-4个a
[a-z]{2,4} 匹配至少2-4个小写字母
[A-Z]{2,4} 匹配任意2-4个大写字母
[A-Za-z]{2,4} 匹配至少2-4个字母
1a{2,4}   匹配 1aa 1aaa 1aaaa
[0-9][a-z]{2,4}  匹配一个数字和2-4个小写字母

5 ?  匹配0次或1次 等同于{0,1}
a?   匹配0个或1个a
-?[1-9]  匹配正负1-9
-{0,1}[1-9]  匹配正负1-9

6 . 匹配换行符以外的任意一位字符 \n

7. *  匹配任意次  {0,}

8. .*?  匹配除换行符以外的任意字符任意次

9. +  匹配至少一次  {1,}

10. .+?   匹配除换行符以外的任意字符至少一次

11. ^   以...开头
ab     cab
^ab    cab   abcccccasd
12. $   以...结尾
ab    abc
ab$    abc

13 ^$  完全匹配
18826123678
1[3-9][0-9]{9}   18826123678
1[3-9][0-9]{9}   18826123678123
^1[3-9][0-9]{9}$   18826123678123
^1[3-9][0-9]{9}$   18826123678

14  () 一个单元和子存储

15 \w  匹配数字字母下划线

16 \W  前面的取反

17 \d  匹配数字0-9  [0-9]

18 \D  上面的取反

19 \s  匹配空白字符

20 \S  匹配非空白字符

二、正则表达式

1、匹配单个字符与数字

匹配 说明
. 匹配除换行符以外的任意字符,当flags被设置为re.S时,可以匹配包含换行符以内的所有字符
[] 里面是字符集合,匹配[]里任意一个字符
[0123456789] 匹配任意一个数字字符
[0-9] 匹配任意一个数字字符
[a-z] 匹配任意一个小写英文字母字符
[A-Z] 匹配任意一个大写英文字母字符
[A-Za-z] 匹配任意一个英文字母字符
[A-Za-z0-9] 匹配任意一个数字或英文字母字符
[^lucky] []里的^称为脱字符,表示非,匹配不在[]内的任意一个字符
[1] 以[]中内的某一个字符作为开头
\d 匹配任意一个数字字符,相当于[0-9]
\D 匹配任意一个非数字字符,相当于[^0-9]
\w 匹配字母、下划线、数字中的任意一个字符,相当于[0-9A-Za-z_]
\W 匹配非字母、下划线、数字中的任意一个字符,相当于[^0-9A-Za-z_]
\s 匹配空白符(空格、换页、换行、回车、制表),相当于[ \f\n\r\t]
\S 匹配非空白符(空格、换页、换行、回车、制表),相当于[^ \f\n\r\t]

2、匹配锚字符

锚字符:用来判定是否按照规定开始或者结尾

匹配 说明
^ 行首匹配,和[]里的^不是一个意思
$ 行尾匹配
\A 匹配字符串的开始,和^的区别是\A只匹配整个字符串的开头,即使在re.M模式下也不会匹配其他行的行首
\Z 匹配字符串的结尾,和$的区别是\Z只匹配整个字符串的结尾,即使在re.M模式下也不会匹配其他行的行尾

3、限定符

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。

匹配 说明
(xyz) 匹配括号内的xyz,作为一个整体去匹配 一个单元 子存储
x? 匹配0个或者1个x,非贪婪匹配
x* 匹配0个或任意多个x
x+ 匹配至少一个x
x 确定匹配n个x,n是非负数
x 至少匹配n个x
x 匹配至少n个最多m个x
x|y |表示或的意思,匹配x或y

三、re模块中常用函数

通用flags(修正符)

说明
re.I 是匹配对大小写不敏感
re.M 多行匹配,影响到^和$
re.S 使.匹配包括换行符在内的所有字符

通用函数

  • 获取匹配结果

    • 使用group()方法 获取到匹配的值

    • groups() 返回一个包含所有小组字符串的元组(也就是自存储的值),从 1 到 所含的小组号。

1、match()函数

  • 原型

    def match(pattern, string, flags=0)
    
  • 功能

    匹配成功返回 匹配的对象

    匹配失败 返回 None

  • 获取匹配结果

    • 使用group()方法 获取到匹配的值

    • groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。

  • 注意:从第一位开始匹配 只匹配一次

  • 参数

    参数 说明
    pattern 匹配的正则表达式(一种字符串的模式)
    string 要匹配的字符串
    flags 标识位,用于控制正则表达式的匹配方式
  • 代码

    import re
    # 匹配一次  成功返回对象 失败返回None  从第一位开始匹配 相当于^
    res = re.match('\d{2}','123')
    print(res.group())
    print(res.span())
    
    #给当前匹配到的结果起别名
    s = '3G4HFD567'
    re.match("(?P<value>\d+)",s)
    print(x.group(0))
    print(x.group('value'))
    

2、search()函数

  • 原型

    def search(pattern, string, flags=0)
    
  • 功能

    扫描整个字符串string,并返回第一个pattern模式成功的匹配

    匹配失败 返回 None

  • 参数

    参数 说明
    pattern 匹配的正则表达式(一种字符串的模式)
    string 要匹配的字符串
    flags 标识位,用于控制正则表达式的匹配方式
  • 注意:

    只要字符串包含就可以

    只匹配一次

  • 示例

    import re
    
    # 匹配一次  成功返回对象 失败返回None
    res = re.search('[a-z]', '131A3ab889s')
    print(res)
    print(res.group()
    
  • 注意

    与search的区别

    相同点:

    都只匹配一次

    不同点:

    • search是在要匹配的字符串中 包含正则表达式的内容就可以
    • match 必须第一位就开始匹配 否则匹配失败

3、findall()函数(返回列表)

  • 原型

    def findall(pattern, string, flags=0)
    
  • 功能

    扫描整个字符串string,并返回所有匹配的pattern模式结果的字符串列表

  • 参数

    参数 说明
    pattern 匹配的正则表达式(一种字符串的模式)
    string 要匹配的字符串
    flags 标识位,用于控制正则表达式的匹配方式
  • 示例

    myStr = """
    <a href="http://www.baidu.com">百度</a>
    <A href="http://www.taobao.com">淘宝</A>
    <a href="http://www.id97.com">电
    影网站</a>
    <i>我是倾斜1</i>
    <i>我是倾斜2</i>
    <em>我是倾斜2</em>
    """
    # html里是不区分大小写
    # (1)给正则里面匹配的 加上圆括号 会将括号里面的内容进行 单独的返回
    res = re.findall("(<a href=\"http://www\.(.*?)\.com\">(.*?)</a>)",myStr) #[('<a href="http://www.baidu.com">百度</a>', 'baidu', '百度')]
    
    # 括号的区别
    res = re.findall("<a href=\"http://www\..*?\.com\">.*?</a>",myStr) #['<a href="http://www.baidu.com">百度</a>']
    
    #(2) 不区分大小写的匹配
    res = re.findall("<a href=\"http://www\..*?\.com\">.*?</a>",myStr,re.I) #['<a href="http://www.baidu.com">百度</a>', '<A href="http://www.taobao.com">淘宝</A>']
    res = re.findall("<[aA] href=\"http://www\..*?\.com\">.*?</[aA]>",myStr) #['<a href="http://www.baidu.com">百度</a>']
    

    3.1 使.支持换行匹配

res = re.findall("<a href=\"http://www\..*?\.com\">.*?</a>",myStr,re.S)

3.2支持换行 支持不区分大小写匹配

res = re.findall("<a href=\"http://www\..*?\.com\">.*?</a>",myStr,re.S|re.I)
- 贪婪与非贪婪模式

**贪婪:** ".*"

**非贪婪:** ".*?"

4、finditer()函数

  • 原型

    def finditer(pattern, string, flags=0)
    
  • 功能

    与findall()类似,返回一个迭代器

  • 参数

    说明 参数
    匹配的正则表达式(一种字符串的模式) pattern
    要匹配的字符串 string
    标识位,用于控制正则表达式的匹配方式 flags
  • 代码

    import re
    # 匹配所有  返回迭代器
    res = re.finditer('\w', '12hsakda1')
    print(res)
    print(next(res))
    
    for i in res:
        print(i)
    

5、split()函数

  • 作用:切割字符串

  • 原型:

    def split(patter, string, maxsplit=0, flags=0)
    
  • 参数

    pattern 正则表达式

    string 要拆分的字符串

    maxsplit 最大拆分次数 默认拆分全部

    flags 修正符

  • 示例

    import re
    myStr = "asdas\rd&a\ts12d\n*a3sd@a_1sd"
    
    # 通过特殊字符 对其进行拆分 成列表
    res = re.split("[^a-z]",myStr)
    res = re.split("\W",myStr)
    

6、修正符

  • 作用

    对正则进行修正

  • 使用

    search/match/findall/sub/subn/finditer 等函数 flags参数的使用

  • 修正符

    re.I 不区分大小写匹配

    re.M 多行匹配 影响到^ 和 $ 的功能

    re.S 使.可以匹配换行符 匹配任意字符

  • 使用

    re.I

    print(re.findall('[a-z]','AaBb'))
    print(re.findall('[a-z]','AaBb', flags=re.I))
    

    re.M(实际)

    myStr = """asadasdd1\nbsadasdd2\ncsadasdd3"""
    print(re.findall('^[a-z]',myStr, ))
    print(re.findall('\A[a-z]',myStr))
    print(re.findall('\d$',myStr))
    print(re.findall('\d\Z',myStr))
    # re.M
    print(re.findall('^[a-z]',myStr, flags=re.M))
    print(re.findall('\A[a-z]',myStr, flags=re.M))
    print(re.findall('\d$',myStr, flags=re.M))
    print(re.findall('\d\Z',myStr, flags=re.M))
    

    re.S

    print(re.findall('<b>.*?</b>','<b>b标签</b>'))
    print(re.findall('<b>.*?</b>','<b>b标\n签</b>', flags=re.S))
    

四、正则高级

1、分组&起名称

  • 概念

    处理简单的判断是否匹配之外,正则表达式还有提取子串的功能,用()表示的就是要提取的分组

  • 代码

    #给当前匹配到的结果起别名
    s = '3G4HFD567'
    re.match("(?P<value>\d+)",s)
    print(x.group(0))
    print(x.group('value'))
    
  • 说明

    • 正则表达式中定义了组,就可以在Match对象上用group()方法提取出子串来
    • group(0)永远是原始字符串,group(1)、group(2)……表示第1、2、……个子串

2、编译

  • 概念

    当在python中使用正则表达式时,re模块会做两件事,一件是编译正则表达式,如果表达式的字符串本身不合法,会报错。另一件是用编译好的正则表达式提取匹配字符串

  • 编译优点

    如果一个正则表达式要使用几千遍,每一次都会编译,出于效率的考虑进行正则表达式的编译,就不需要每次都编译了,节省了编译的时间,从而提升效率

  • compile()函数

    • 原型

      def compile(pattern, flags=0)
      
    • 作用

      将pattern模式编译成正则对象

    • 参数

      参数 说明
      pattern 匹配的正则表达式(一种字符串的模式)
      flags 标识位,用于控制正则表达式的匹配方式
    • flags

      说明
      re.I 是匹配对大小写不敏感
      re.M 多行匹配,影响到^和$
      re.S 使.匹配包括换行符在内的所有字符
    • 返回值

      编译好的正则对象

    • 示例

      import re
      
      re_phone = re.compile(r"(0\d{2,3}-\d{7,8})")
      print(re_phone, type(re_phone))
      
  • 编译后其他方法的使用

    原型

    def match(self, string, pos=0, endpos=-1)
    def search(self, string, pos=0, endpos=-1)
    def findall(self, string, pos=0, endpos=-1)
    def finditer(self, string, pos=0, endpos=-1)
    

    参数

    参数 说明
    string 待匹配的字符串
    pos 从string字符串pos下标开始
    endpos 结束下标

    示例

    s1 = "lucky's phone is 010-88888888"
    s2 = "kaige's phone is 010-99999999"
    ret1 = re_phone.search(s1)
    print(ret1, ret1.group(1))
    ret2 = re_phone.search(s2)
    print(ret2, ret2.group(1))
    

3、贪婪与非贪婪

  • 贪婪模式

    贪婪概念:匹配尽可能多的字符

    • .+ 匹配换行符以外的字符至少一次
    • .* 匹配换行符以外的字符任意次

    实例

    res = re.search('<b>.+</b>', '<b></b><b>b标签</b>')
    res = re.search('<b>.*</b>', '<b>b标签</b><b>b标签</b><b>b标签</b><b>b标签</b>')
    
  • 非贪婪模式

    非贪婪概念:尽可能少的匹配称为非贪婪匹配,*?、+?即可

  • .+? 匹配换行符以外的字符至少一次 拒绝贪婪

  • .*? 匹配换行符以外的字符任意次 拒绝贪婪

    实例

    res = re.search('<b>.+?</b>', '<b>b标签</b><b>b标签</b>')
    res = re.search('<b>.*?</b>', '<b>b标签</b><b>b标签</b><b>b标签</b><b>b标签</b>')
    

五、re模块用法

1.findall 查找所有. 返回list

lst = re.findall("m", "mai le fo len, mai ni mei!") 
print(lst)    # ['m', 'm', 'm'] 
lst = re.findall(r"\d+", "5点之前. 你要给我5000万")
print(lst)   # ['5', '5000']

2.search 会进行匹配. 但是如果匹配到了第一个结果. 就会返回这个结果. 如果匹配不上search返回的则是None

ret = re.search(r'\d', '5点之前. 你要给我5000万').group()
print(ret) # 5

3.finditer 和findall差不多. 只不过这时返回的是迭代器(重点)

it = re.finditer("m", "mai le fo len, mai ni mei!")
for el in it:
   print(el.group()) # 依然需要分组

4.compile() 可以将一个长长的正则进行预加载. 方便后面的使用

obj = re.compile(r'\d{3}')  # 将正则表达式编译成为一个 正则表达式对象, 规则要匹配的是3个数字
ret = obj.search('abc123eeee') # 正则表达式对象调用search, 参数为待匹配的字符串
print(ret.group())  # 结果: 123

5.正则中的内容如何单独提取?

单独获取到正则中的具体内容可以给分组起名字

s = """
<div class='西游记'><span id='10010'>中国联通</span></div>
"""
obj = re.compile(r"<span id='(?P<id>\d+)'>(?P<name>\w+)</span>", re.S)

result = obj.search(s)
print(result.group())  # 结果: <span id='10010'>中国联通</span>
print(result.group("id"))  # 结果: 10010 # 获取id组的内容
print(result.group("name"))  # 结果: 中国联通 # 获取name组的内容

这里可以看到我们可以通过使用分组. 来对正则匹配到的内容进一步的进行筛选.

6. 正则表达式本身是用来提取字符串中的内容的. 也可以用作字符串的替换

import re
# 分割
r = re.split(r"\d+", "我今年19岁了, 你知道么, 19岁就已经很大了. 周杰伦20岁就得奖了")
print(r)  # ['我今年', '岁了, 你知道么, ', '岁就已经很大了. 周杰伦', '岁就得奖了']
# 替换
r = re.sub(r"\d+", "18", "我今年19岁了, 你知道么, 19岁就已经很大了. 周杰伦20岁就得奖了")
print(r)  # 我今年18岁了, 你知道么, 18岁就已经很大了. 周杰伦18岁就得奖了

六、案例一(爬取文章)

# 需求: 文章标题, 来源, 作者, 时间, 内容

# http://www.animationcritics.com/chinese_aniamtion.html

# 1. 在首页中获取到10个详情页的url地址.
# 经过循环.拿到每一个url地址
# 2. 访问详情页的url. 得到详情页的内容
# 3. 在详情页内容中提取到最终你需要的内容

# 你要的东西在页面源代码里??

import requests
import re
import time

url = "http://www.animationcritics.com/chinese_aniamtion.html"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36"
}

resp = requests.get(url, headers=headers)

resp_text = resp.text

# 取到首页url正则
main_url_re = re.compile(r'<li style="margin-bottom:10px;">.*?href="(?P<url>.*?)" title="(?P<title>.*?)"', re.S)

shut_url_re = re.compile(r'来源:</span>(?P<ly>.*?)</span>', re.S)
shut_zuozhe = re.compile(r'作者:</span>(?P<zz>.*?)</span>', re.S)
shut_fabu = re.compile(r'发布时间: </span>(?P<fb>.*?)</span>', re.S)

section_obj = re.compile(r"<section.*?>(?P<content>.*?)</section>", re.S)

content_filter = re.compile(r'<.*?>', re.S)

xiao_niao = re.compile(r'<p data-track=.*?>(?P<xiao>.*?)</p>', re.S)

# 取到多个规则一样的内容
text_url = main_url_re.finditer(resp_text)
for it in text_url:
    res = it.group("url")  # 详情页url
    scp = it.group("title")  # 首页标题
    # print(scp + '\t' + url)
    # time.sleep(2)

    # 请求拿到详情页内容
    shut_requ = requests.get(res, headers=headers)
    shut_text = shut_requ.text

    # 解析内容数据拿到来源信息
    shut_res = shut_url_re.search(shut_text)
    if shut_res:
        laiyun = shut_res.group("ly")
    else:
        laiyun = ""

    # 作者
    shut_zuozhes = shut_zuozhe.search(shut_text)
    if shut_zuozhes:
        zuozhe = shut_zuozhes.group("zz")
    else:
        zuozhe = ""

    # 时间
    shut_time = shut_fabu.search(shut_text)
    if shut_time:
        fabu = shut_time.group("fb")
    else:
        fabu = ""
    print(scp + '\t' + laiyun + '\t' + zuozhe + '\t\t' + fabu)
    # time.sleep(1)

    list_l = []
    shut_content = section_obj.finditer(shut_text, re.S)
    for i in shut_content:
        shut_all = i.group("content")
        list_l.append(shut_all)  # 把拿到的section的内容放到列表中

    xiaoniao = xiao_niao.finditer(shut_text, re.S)
    for s in xiaoniao:
        xn = s.group("xiao")
        list_l.append(xn)

    all_content = "".join(list_l)  # 拼接所有的section为一个字符串

    all_content = content_filter.sub("", all_content)

    print(all_content)

    time.sleep(2)

七、案例二(文章面向对象版本)

import requests
import re
import time


class Animation:

    def __init__(self):
        self.url = "http://www.animationcritics.com/chinese_aniamtion.html"
        self.header = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36"
        }
        # 正则
        self.main_url_re = re.compile(r'<li style="margin-bottom:10px;">.*?href="(?P<url>.*?)" title="(?P<title>.*?)"',
                                      re.S)
        self.shut_url_re = re.compile(r'来源:</span>(?P<ly>.*?)</span>', re.S)
        self.shut_zuozhe = re.compile(r'作者:</span>(?P<zz>.*?)</span>', re.S)
        self.shut_fabu = re.compile(r'发布时间: </span>(?P<fb>.*?)</span>', re.S)
        self.section_obj = re.compile(r"<section.*?>(?P<content>.*?)</section>", re.S)
        self.content_filter = re.compile(r'<.*?>', re.S)
        self.xiao_niao = re.compile(r'<p data-track=.*?>(?P<xiao>.*?)</p>', re.S)

        self.home_urls = ''
        self.title = ''
        self.laiyun = ''
        self.zuohze = ''
        self.fabu = ''

    # 首页获取页面源代码
    def home(self):
        resp = requests.get(self.url, headers=self.header)
        resp_text = resp.text
        return resp_text

    # 首页url获取
    def home_url(self):
        text_url = self.main_url_re.finditer(self.home())
        for it in text_url:
            self.home_urls = it.group("url")  # 详情页url
            self.title = it.group("title")  # 首页标题

            # 请求拿到详情页内容
            shut_requ = requests.get(self.home_urls, headers=self.header)
            shut_text = shut_requ.text

            # 解析内容数据拿到来源信息

            shut_res = self.shut_url_re.search(shut_text)
            if shut_res:
                self.laiyun = shut_res.group("ly")
            self.laiyun = ''

            # 作者
            shut_zuozhes = self.shut_zuozhe.search(shut_text)
            if shut_zuozhes:
                self.zuozhe = shut_zuozhes.group("zz")
            self.zuozhe = ''

            # 时间
            shut_time = self.shut_fabu.search(shut_text)
            if shut_time:
                self.fabu = shut_time.group("fb")
            self.fabu = ''
            print(self.title + '\t' + self.laiyun + '\t' + self.zuozhe + '\t\t' + self.fabu)

            list_l = []
            shut_content = self.section_obj.finditer(shut_text, re.S)
            for i in shut_content:
                shut_all = i.group("content")
                list_l.append(shut_all)  # 把拿到的section的内容放到列表中

            xiaoniao = self.xiao_niao.finditer(shut_text, re.S)
            for s in xiaoniao:
                xn = s.group("xiao")
                list_l.append(xn)

            all_content = "".join(list_l)  # 拼接所有的section为一个字符串

            all_content = self.content_filter.sub("", all_content)

            print(all_content)

            time.sleep(2)


if __name__ == '__main__':
    p = Animation()
    p.home_url()

八、案例三(爬取博客园)

import requests
import re

url = 'https://www.cnblogs.com/skyy'

headers = {
    "User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36"
}

content = re.compile(r'<span role="heading" aria-level="2">(?P<title>.*?)</span>')

response = requests.get(url, headers=headers)
res_text = response.text
# print(res_text)

# 提取普通文章链接
normal_links = []
for match in re.finditer(r'<a class="postTitle2 vertical-middle" href="(?P<url>.*?)">', res_text):
    urls = match.group('url')
    normal_links.append(urls)

# 提取 pinned-post 链接
for match in re.finditer(r'<a class="postTitle2 vertical-middle pinned-post" href="(?P<res_url>.*?)">', res_text):
    res_urls = match.group('res_url')
    normal_links.append(res_urls)

# 打印文章链接
for url in normal_links:
    print(f"文章首页链接: {url}")
    print("-" * 50)

    response1 = requests.get(url, headers=headers)
    res_text1 = response1.text
    response2 = re.finditer(r'<a href="(?P<urls>.*?)" target="_blank" rel="noopener">(?P<title>.*?)</a>', res_text1)
    for match in response2:
        urls1 = match.group('urls')
        title1 = match.group('title')
        if not urls1.endswith('.html'):
            continue
        else:
            print(f"文章链接: {urls1}")
            print(f"文章标题: {title1}")


            response3 = requests.get(urls1, headers=headers)
            res_text3 = response3.text
            count_mulu = content.search(res_text3)
            if count_mulu:
                mulu = count_mulu.group('title')
                print(f"文章目录名称: {mulu}")
            else:
                mulu = "未知"

九、案例四(爬取博客园面向对象版本)

import requests
import re


class BlogScraper:
    def __init__(self, base_url, user_agent):
        self.base_url = base_url
        self.headers = {
            "User-Agent": user_agent
        }

    def fetch_page(self, url):
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()  # 如果请求失败则抛出异常
        return response.text

    def extract_links(self, html_content):
        normal_links = []
        # 提取普通文章链接
        pattern = r'<a class="postTitle2 vertical-middle" href="(?P<url>.*?)">'
        for match in re.finditer(pattern, html_content):
            urls = match.group('url')  # 提取链接
            normal_links.append(urls)

        # 提取 pinned-post 链接
        pinned_pattern = r'<a class="postTitle2 vertical-middle pinned-post" href="(?P<url>.*?)">'
        for match in re.finditer(pinned_pattern, html_content):
            urls = match.group('url')
            normal_links.append(urls)

        return normal_links

    def extract_article_info(self, url):
        html_content = self.fetch_page(url)  # 获取子url页面内容
        pattern = r'<a href="(?P<urls>.*?\.html)" target="_blank" rel="noopener">(?P<title>.*?)</a>'
        for match in re.finditer(pattern, html_content):
            urls1 = match.group('urls')
            title1 = match.group('title')
            yield urls1, title1

    def extract_directory(self, url):
        html_content = self.fetch_page(url)
        content_pattern = re.compile(r'<span role="heading" aria-level="2">(?P<title>.*?)</span>')
        match = content_pattern.search(html_content)
        if match:
            return match.group('title')
        else:
            return "未知"


if __name__ == "__main__":
    scraper = BlogScraper(
        base_url='https://www.cnblogs.com/skyy',
        user_agent="Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36"
    )

    # 提取首页链接
    home_page_html = scraper.fetch_page(scraper.base_url)
    links = scraper.extract_links(home_page_html)

    # 打印文章链接和相关信息
    for urls in links:
        print(f"文章首页链接: {urls}")
        print("-" * 50)

        # 提取文章内部链接和标题
        for article_url, title in scraper.extract_article_info(urls):
            print(f"文章链接: {article_url}")
            print(f"文章标题: {title}")

            # 提取文章目录
            directory = scraper.extract_directory(article_url)
            print(f"文章目录名称: {directory}")
            print("\n")

  1. lucky ↩︎

posted @ 2022-05-19 12:38  沈忻凯  阅读(92)  评论(0)    收藏  举报