06-爬虫数据提取-正则表达式

什么是正则表达式

通俗理解:按照一定的规则,从某个字符串中匹配出想要的数据。这个规则就是正则表达式

re模块是python自带的标准库,不需要安装,直接导入

import re

 

1.单字符匹配

点(.):匹配任意的字符(除了'\n')

\d:匹配任意的数字

\D:匹配任意的非数字

\s:匹配的是空白字符(包括:\n,\t,\r和空格)

\S:非空白字符

\w:匹配的是a-z和A-Z以及数字和下划线

[]组合的方式,只要满足中括号中的某一项都算匹配成功

# 匹配单个字符match,只是相对于第一个位置

import re

text = "abc"
# 1.match匹配单个字符串"a"(match方法从text的第一个位置进行匹配,符合则返回,不符合返回None并不会往下一个位置进行匹配)
result = re.match("a",text)
# 2.group返回默认分组
print(result.group())


# 点(.):匹配任意的字符(除了'\n')
text2 = "+bc"
result2 = re.match(".",text2)
print("匹配任意的字符:",result2.group())

# \d:匹配任意的数字
text3 = "1b3"
result3 = re.match("\d",text3)
print("匹配任意的数字:",result3.group())

# \D:匹配任意的非数字
text4 = "&2c"
result4 = re.match("\D",text4)
print("匹配任意的非数字:",result4.group())

# \s:匹配的是空白字符(包括:\n,\t,\r和空格)
text5 = "\tab"
result5 = re.match("\s",text5)
print("匹配的是空白字符:------{}------".format(result5.group()))

# \S:非空白字符
text6 = "abc"
result6 = re.match("\S",text6)
print("匹配非空白字符:",result6.group())

# \w:匹配的是a-z和A-Z以及数字和下划线
text7 = "ABC"
result7 = re.match("\w",text7)
print("匹配的是a-z和A-Z以及数字和下划线:",result7.group())

# \W:匹配的是和\w相反的
text8 = "#BC"
result8 = re.match("\W",text8)
print("匹配的是和\w相反的:",result8.group())

# []组合的方式,只要满足中括号中的某一项都算匹配成功
text9 = "1bc"
result9 = re.match("[1bc]",text9)
print("[]组合的方式:",result9.group())

result9_2 = re.match("[\s\d]",text9)
print("[]组合的方式:",result9_2.group())

 2.多字符匹配

# 匹配多个字符

import re

text = "abc"
result = re.match("ab",text)
print(result.group())

# *:匹配任意多个字符
text1 = "ab+c"
result1 = re.match("\w*",text1)
print("匹配任意多个字符:",result1.group())

# +:匹配1个或者多个字符
text2 = "abc"
result2 = re.match("\w+",text2)
print("匹配1个或者多个字符:",result2.group())

# ?:匹配前一个字符0个或者1个
text3 = "a1b2c+"
result3 = re.match("\w?",text3)
print("匹配前一个字符0个或者1个:",result3.group())

# {m}:匹配m个字符
text4 = "abc123"
result4 = re.match("\w{5}",text4)
print("匹配m个字符:",result4.group())

# {m,n}:匹配m-n之间的个数的字符(当满足最大条件,默认最大条件)
text5 = "abc123"
result5 = re.match("\w{2,4}",text5)
print("匹配m个字符:",result5.group())

实例:使用正则表达式验证手机号码、邮箱、URL、身份证号

# 正则表达式案例

import re

def text():
    while True:
        print("----------欢迎开始正则表达式测试-----------")
        print("1.验证手机号码")
        print("2.验证邮箱")
        print("3.验证URL")
        print("4.验证身份证号")
        print("5.退出")
        
        num = input("请输入要选择的数字:")

        if num == "1":
            # 1.验证手机号码:手机号码的规则是以1开头,第二位可以是34587,后面九位就可以任意随意($在正则里面表达是以空白字符结尾)
            text = input("请输入要验证的手机号码:")
            result = re.match("1[34587]\d{9}$",text)
            example = "手机号{}验证正确...".format(result.group()) if result else "手机号验证错误..."
            print(example)
        
        if num == "2":
            # 2.验证邮箱:邮箱的规则是邮箱名称是用数字、英文字母、下划线组成,然后是@符号,后面就是域名
            text = input("请输入要验证的邮箱:")
            result = re.match("\w+@[a-z0-9]+\.[a-z]+",text)
            example = "邮箱{}验证正确...".format(result.group()) if result else "邮箱验证错误..."
            print(example)

        if num == "3":
            # 3.验证URL:URL的规则前面是http或者https或者ftp,然后再加上一个冒号,再加上一个斜杠,再后面就是可以出现任意非空白字符了
            text = input("请输入要验证的URL:")
            result = re.match("(http|https|ftp)://\S+",text)
            example = "URL{}验证正确...".format(result.group()) if result else "URL验证错误..."
            print(example)
        
        if num == "4":
            # 4.验证身份证:身份证的规则是,总共18位,前面17位都是数字,后面一位可以是数字,也可以是小写的x,也可以是大写的X
            text = input("请输入要验证的身份证号:")
            result = re.match("\d{17}[\dxX]",text)
            example = "身份证号{}验证正确...".format(result.group()) if result else "身份证号验证错误..."
            print(example)
        
        if num == "5":
            print("感谢您的使用,下次再见...")
            break
            
if __name__ == "__main__":
    text()
        

3.正则表达式-开始、结束、贪婪和非贪婪
^:以...开头
$:以...结尾
|:匹配多个字符串或者表达式
贪婪
非贪婪

# 正则表达式-开始、结束、贪婪和非贪婪

import re

# ^:以...开头:
text = "hello world"
result = re.search("^hello",text)
print("^开头:",result.group())

result_2 = re.match("hello",text)
print("match:",result_2.group())


# $:以...结尾:
text = "hello world"
result = re.search("world$",text)
print("$结尾:",result.group())

text = ""
result = re.search("^$",text)
print("$结尾:",result.group())


# |:匹配多个字符串或者表达式:



# 贪婪和非贪婪:
text = "12345"

result = re.search("\d+",text)
print("贪婪:",result.group())

result = re.search("\d+?",text)
print("非贪婪:",result.group())


# 案例1:提取html标签名称:
# text = "<h1>这是标题</h1>"
text = "<head>这是标题</head>"
result = re.search("<.+?>",text)
print(result.group())


# 案例2:验证一个字符是不是0-100之间的数字:
# 01
text = "0"
result = re.match("0$|[1-9]\d?$|100$",text)
print(result.group())

text = "6"
result = re.match("0$|[1-9]\d?$|100$",text)
print(result.group())

text = "99"
result = re.match("0$|[1-9]\d?$|100$",text)
print(result.group())

4.转义字符和原生字符
python中的转义字符
正则表达式中的转义字符
原生字符串和正则表达式

import re

# python中的转义字符(r原生字符串)
text = "hello\tworld"
print("转义前",text)
text_2 = r"hello\tworld"
print("转义后",text_2)

# 正则表达式中的转义字符($在正则里面表达是以空白字符结尾)
text = "apple price is $99,range price is $88"
result = re.findall("$\d+",text)
print("转义前",result)
result_2 = re.findall("\$\d+",text)
print("转义后",result_2)

# 原生字符串和正则表达式
# 正则表达式字符串的解析规则:
# 1.先把这个字符串放在python语言层面进行解析
# 2.把python语言层面解析的结果再放到正则表达式层面进行解析
text = "\cba c"

# \\\\c => python语言层面\\c => 正则表达式层面\c
result1 = re.match("\\\\c",text)
print("原生字符串和正则表达式1",result1.group())
result_2 = re.match(r"\\c",text)
print("原生字符串和正则表达式2",result_2.group())

5.正则表达式:分组
findall:查找所有满足条件的
sub:根据规则替换其它字符串
split:根据规则分割字符串
compile:编译正则表达式

# search
import re

# 分组
# group()/group(0)匹配整个分组
# group(1)匹配第一个分组 group(2)匹配第二个分组
text = "apple price is $99,range price is $88"
result = re.search(".+\$\d+.+\$\d+",text)
print(result.group())

result_1 = re.search(".+(\$\d+).+(\$\d+)",text)
print("第一个分组:{0}\t第二个分组:{1}".format(result_1.group(1),result_1.group(2)))

# findall:查找所有满足条件的
text = "apple price is $99,range price is $88"
result = re.findall(r"\$\d+",text)
print("findall:",result)

# sub:根据规则替换其它字符串
# sub方法比replace方法强大的多,sub使用正则进行替换
text = "nihao zhongguo hello world"
result = text.replace(" ","-")
print("字符串replace替换:",result)

result_2 = re.sub(r" ","-",text)
print("re中的sub替换:",result_2)

# sub测试
html = """
<div class="job-detail">
    <p>1. 3年以上相关开发经验 ,全日制统招本科以上学历</p>
    <p>2. 精通一门或多门开发语言(Python,C,Java等),其中至少有一门有3年以上使用经验</p>
    <p>3. 熟练使用ES/mysql/mongodb/redis等数据库;</p>
    <p>4. 熟练使用django、tornado等web框架,具备独立开发 Python/Java 后端开发经验;</p>
    <p>5. 熟悉 Linux / Unix 操作系统&nbsp;</p>
    <p>6. 熟悉 TCP/IP,http等网络协议</p>
    <p>福利:</p>
    <p>1、入职购买六险一金(一档医疗+公司全额购买商业险)+开门红+全额年终奖(1年13薪,一般会比一个月高)</p>
    <p>2、入职满一年有2次调薪调级机会</p>
    <p>3、项目稳定、团队稳定性高,团队氛围非常好(汇合员工占招行总员工比例接近50%);</p>
    <p>4、有机会转为招商银行内部员工;</p>
    <p>5、团队每月有自己的活动经费,法定节假日放假安排;</p>
    <p>6、办公环境优良,加班有加班费(全额工资为计算基数,加班不超过晚上10点,平日加班为时薪1.5倍,周末加班为日薪2倍,周末加班也可优先选择调休,管理人性化)。</p>
</div>
"""
new_html = re.sub(r"<.+?>","",html)
print("sub获取html内容:",new_html)


# split:根据规则分割字符串(得到list列表)
text = "nihao zhongguo,hello world"
result_list = re.split(r" |,",text)
print("split分割:",result_list)

# compile:编译正则表达式(提高效率,直接把编译好的正则表达式发送给服务器)
text = "apple price is 34.56"
# ?匹配0次或1次   *匹配0次或无数次
r = re.compile(r"\d+\.?\d*")
print("compile编译正则表达式:",r)
result = re.search(r,text)
print("打印:",result.group())

# 如果想要再正则表达式中加注释,需要在后面加re.VERBOSE
r2 = re.compile(r"""
\d+ # 整数部分
\.? # 小数点
\d* # 小数部分
""",re.VERBOSE)
print("compile编译正则表达式:",r2)
result_2 = re.search(r2,text)
print("打印:",result_2.group())

实战-赶集网租房信息爬虫(利用requests)

# 赶集网爬虫实战

import requests
from fake_useragent import UserAgent
import re
import time

# url = "http://nc.ganji.com/zufang/pn2/"

# 用列表保存总page_urls
page_urls = []

user_agent = UserAgent()
headers = {
    "User-Agent":user_agent.chrome,
    "cookie":"ganji_xuuid=2ee320a9-26d9-449d-fb1a-fe9dab1f0f52.1611720150767; ganji_uuid=6987060722968396958522; _gl_tracker=%7B%22ca_source%22%3A%22www.baidu.com%22%2C%22ca_name%22%3A%22-%22%2C%22ca_kw%22%3A%22-%22%2C%22ca_id%22%3A%22-%22%2C%22ca_s%22%3A%22seo_baidu%22%2C%22ca_n%22%3A%22-%22%2C%22ca_i%22%3A%22-%22%2C%22sid%22%3A54450163782%7D; GANJISESSID=ompcp0chpheb6gjnau2f68r433; citydomain=nc; __utmc=32156897; __utma=32156897.318273985.1611720168.1611819400.1611821517.3; __utmz=32156897.1611821517.3.3.utmcsr=nc.ganji.com|utmccn=(referral)|utmcmd=referral|utmcct=/; __utmt=1; ganji_login_act=1611821604762; __utmb=32156897.4.10.1611821517"
}

# 获取详情页面信息
def parse_page(page_urls,f):
    page = 1
    for page_url in page_urls:

        count = 1
        print("正在爬取赶集网58南昌租房信息第{}页内容:".format(page),page_url)
        
        resp = requests.get(page_url,headers=headers)
        text = resp.content.decode("utf-8")
        
        # 利用正则表达式,获取a标签下的标题(re.DOTALL指的是"."可以代表所有字符,包括换行)
        houses = re.findall(r"""
            <div.+?ershoufang-list".+?<a.+?js-title.+?>(.+?)</a> # 获取房源的标题
            .+?<dd.+?dd-item.+?<span>(.+?)</span> # 获取房源的户型
            .+?<span.+?<span>(.+?)</span> # 获取房源的面积
            .+?<span.+?<span>(.+?)</span> # 获取房源的朝向
            .+?<div.+?price.+?<span.+?num">(.+?)</span> # 获取房源的租金
        """,text,re.VERBOSE|re.DOTALL)
        
        for house in houses:
            print("正在获取第{0}页第{1}条的内容:{2}".format(page,count,house))
            # 保存数据内容
            house = ",".join(house)+"\n"
            f.write(house)
            count += 1
        

        # time.sleep()推迟调用线程的运行
        time.sleep(1)
        page += 1
        

def main():
    # 保存文件到"F:/爬虫/第三章-xpath库/快代理.csv" (newline=""消除多余空行,添加ignore忽略报错)
    with open(r"F:/爬虫/第三章-xpath库/赶集网南昌租房信息.csv","a",encoding="gbk",newline="") as f:
    
        # 准备需要爬取页面的url
        page = int(input("请输入要爬取赶集网58南昌租房信息前几页内容:"))
        for i in range(1,page+1):
            page_url = "http://nc.ganji.com/zufang/pn{}/".format(i)
            page_urls.append(page_url)

        # 保存数据标题信息
        title = "标题,户型,面积,朝向,租金\n"
        f.write(title)
        
        # 调用,获取详情页面
        parse_page(page_urls,f)


if __name__ == "__main__":
    main()

总结
1.如果想要让"."代表所有的字符,那么就需要在函数后面加re.DOTALL来表示,否则不会代表\n,也就是换行
2.获取数据的时候,都要用非贪婪模式.+?
3.如果正则写的不对,那么获取不到结果,程序会假死,这时候可以把你刚刚写的正则删掉,重新运行下,看程序还会不会假死,如果不会假死,说明正则写的有问题,不要去钻牛角尖,换一个思路

 

 

posted @ 2021-03-06 12:56  马铃薯1  阅读(263)  评论(0编辑  收藏  举报