整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

C# - 使用正则提取,提取组匹配的字符串 120

头条创作挑战赛#

字符串提取

1 通过Regex类的Match("要提取的字符串","匹配规则")方式提取第一个匹配的子字符串,返回类型一个是Match类的对象,通过其Value属性得到匹配的结果

注意:做字符串提取时,一般都不加^与$;字符串提取操作只是在原字符串中找到匹配规则的一部分子字符串,匹配规则加^与$就是完全匹配(注意:贪婪模式),除非整个原字符串完全匹配规则,否则都不加^与$进行限定

提取第一个匹配的字符串

2 通过Reges类的Matches("要提取的字符串","匹配规则")方法得到所有匹配规则的子字符串,返回类型是一个MatchCollection类型的集合

 string str = "中国中央气象台2023年7月31日发布称\"卡努\"加强为8级的强台风级";
 //提取所有与年月日有关的字符串
 string reg = @"\d+(年|月|日)";
 MatchCollection mts = Regex.Matches(str, reg);
 foreach (Match mt in mts)
 {
     Console.WriteLine(mt.Value);
 }
Console.ReadKey();

提取所有匹配的字符串

字符串提取组

通过为匹配规则加()实现分组提取(可用于统计分类信息)

通过match.Groups[]来获取提取组;注意:索引为0的第1组存储的是整个匹配字符串,获取的提取组应该从索引1开始

在Regex.Match();与Regex.Matches();方法中都可用于提取组

//读取1.html文件到内存中,包含邮箱地址
string str = File.ReadAllText(@"d:\1.html");
//提取                         登陆名                   哪个邮箱          .com/.cn ...
string email = @"([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9]+)(\.[a-zA-Z0-9]+)+";
MatchCollection mts = Regex.Matches(str, email);
            #region 提取组
            //foreach (Match mt in mts)
            //{
            //    //判断当前 mt的值 是否匹配成功
            //    if (mt.Success)
            //    {
            //        //mt.Groups 获取所有分组结果
            //        //分组依据看匹配规则中有几个()
            //        //从mt.Groups[1]算起,用于提取组
            //        //获取整个匹配结果
            //        Console.WriteLine(mt.Groups[0]);//zs316@163.com
            //        //获取登陆名
            //        Console.WriteLine(mt.Groups[1]);//zs316
            //        //获取是哪个邮箱
            //        Console.WriteLine(mt.Groups[2]);//163
            //        //邮箱后缀
            //        Console.WriteLine(mt.Groups[3]);//.com
            //    } 
            //} 
            #endregion
//============================================
//声明使用哪个统计总数的变量
int count_163 = 0, count_qq = 0, count_sina = 0, others = 0;
//循环匹配规则获取的结果集合
foreach (Match mt in mts)
{
    //判断当前分组属于哪个邮箱
    switch (mt.Groups[2].Value)
    {
        case "163":
            count_163++;
            break;
        case "qq":
            count_qq++;
            break;
        case "sina":
            count_sina++;
            break;
        default:
            others++;
            break;
    }
}
Console.WriteLine("使用163邮箱的共有: {0} 个", count_163);
Console.WriteLine("使用qq邮箱的共有: {0} 个", count_qq);
Console.WriteLine("使用sina邮箱的共有: {0} 个", count_sina);
Console.WriteLine("使用其他邮箱的共有: {0} 个", others);
Console.ReadKey();

获取分组信息

用三种方式对网页信息进行提取

  • XPath语法和lxml模块
  • BeautifulSoup4
  • 正则表达式

豆瓣电影

#coding=utf-8
import requests
from lxml import etree

url = 'https://movie.douban.com/cinema/nowplaying/nanjing/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/83.0.4090.0 Safari/537.36 Edg/83.0.467.0 ',
    'Referer': 'https://movie.douban.com/'
}
response = requests.get(url, headers=headers)
html = response.text

htmlElement = etree.HTML(html)
ul = htmlElement.xpath("//ul[@class='lists']")[0]

movies = []
lis = ul.xpath('./li')
for li in lis:
    title = li.xpath('@data-title')[0]
    director = li.xpath('@data-director')[0]
    actors = li.xpath('@data-actors')[0]
    thumbnail = li.xpath('.//img/@src')[0]

    movie = {
        'title': title,
        'director': director,
        'actors': actors,
        'thumbnail': thumbnail
    }
    movies.append(movie)

print(movies)

output:[{'title': '奇妙王国之魔法奇缘', 'director': '陈设', 'actors': '卢瑶 / 张洋 / 陈新玥', 'thumbnail': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2577837112.jpg'}, {'title': '大红包', 'director': '李克龙', 'actors': '包贝尔 / 李成敏 / 贾冰', 'thumbnail': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2581346773.jpg'}, {'title': '金禅降魔', 'director': '彭发 王凯 程中豪', 'actors': '释小龙 / 胡军 / 姚星彤', 'thumbnail': 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2564190636.jpg'}, {'title': '82号古宅', 'director': '袁杰', 'actors': '葛天 / 扈天翼 / 黄心娣', 'thumbnail': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2586838530.jpg'}, {'title': '亲亲哒', 'director': '马雍', 'actors': '马良博一 / 卢小路 / 尹恒', 'thumbnail': 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2579189777.jpg'}, {'title': '六月的秘密', 'director': '王暘', 'actors': '郭富城 / 苗苗 / 吴建飞', 'thumbnail': 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2522497098.jpg'}, {'title': '秘密访客', 'director': '陈正道', 'actors': '郭富城 / 段奕宏 / 张子枫', 'thumbnail': 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2579398648.jpg'}, {'title': '无名狂', 'director': '李云波', 'actors': '张晓晨 / 隋咏良 / 上白', 'thumbnail': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2574800433.jpg'}, {'title': '刺杀小说家', 'director': '路阳', 'actors': '雷佳音 / 杨幂 / 董子健', 'thumbnail': 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2580314674.jpg'}]

拉钩网职位信息

#coding=utf-8
import requests
from lxml import etree

url = 'https://www.lagou.com/zhaopin/PHP/?labelWords=label'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/83.0.4090.0 Safari/537.36 Edg/83.0.467.0 ',
    'Referer': 'https://www.lagou.com/'
}
response = requests.get(url, headers=headers)
html = response.text

htmlElement = etree.HTML(html)
ul = htmlElement.xpath("//ul[@class='item_con_list']")[1]

jobs = []
lis = ul.xpath('./li')
for li in lis:
    position = li.xpath('@data-positionname')[0]
    company = li.xpath('@data-company')[0]
    salary = li.xpath('@data-salary')[0]
    website = li.xpath(".//a/@href")[0]
    education = li.xpath(".//div[@class='li_b_l']//text()")[3]

    job = {
        'position': position,
        'company': company,
        'salary': salary,
        'website': website,
        'education': education
    }
    jobs.append(job)

print(jobs)

output:[{'position': '高级PHP开发工程师', 'company': '广州微用科技', 'salary': '10k-18k', 'website': 'https://www.lagou.com/jobs/6813986.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验3-5年 / 大专\n '}, {'position': '高级PHP开发工程师', 'company': '互爱(北京)科技股份有限公司', 'salary': '25k-40k', 'website': 'https://www.lagou.com/jobs/6585859.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验3-5年 / 不限\n '}, {'position': 'php开发工程师', 'company': '金山云', 'salary': '20k-40k', 'website': 'https://www.lagou.com/jobs/6871122.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验5-10年 / 不限\n '}, {'position': 'php开发工程师', 'company': '央视频融媒体', 'salary': '12k-20k', 'website': 'https://www.lagou.com/jobs/6873952.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验5-10年 / 本科\n '}, {'position': 'php开发工程师', 'company': '顺丰同城科技', 'salary': '20k-40k', 'website': 'https://www.lagou.com/jobs/5788250.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验1-3年 / 本科\n '}, {'position': 'php开发工程师', 'company': '红布林', 'salary': '15k-30k', 'website': 'https://www.lagou.com/jobs/6963665.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验3-5年 / 本科\n '}, {'position': 'php开发工程师', 'company': '吉城美家', 'salary': '7k-14k', 'website': 'https://www.lagou.com/jobs/5553859.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验3-5年 / 本科\n '}, {'position': 'php开发工程师', 'company': '有咖互动', 'salary': '10k-15k', 'website': 'https://www.lagou.com/jobs/6959578.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验1-3年 / 本科\n '}, {'position': 'php开发工程师', 'company': '微拍堂', 'salary': '20k-30k', 'website': 'https://www.lagou.com/jobs/6972670.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验3-5年 / 本科\n '}, {'position': 'PHP开发工程师', 'company': '广州市搜游网络科技', 'salary': '20k-40k', 'website': 'https://www.lagou.com/jobs/6959547.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验3-5年 / 大专\n '}, {'position': 'PHP高级开发工程师', 'company': '广州市搜游网络科技', 'salary': '25k-45k', 'website': 'https://www.lagou.com/jobs/6943052.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验3-5年 / 大专\n '}, {'position': 'PHP高级开发工程师', 'company': '明源云', 'salary': '15k-25k', 'website': 'https://www.lagou.com/jobs/4619045.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验3-5年 / 本科\n '}, {'position': 'PHP开发工程师', 'company': '微通', 'salary': '8k-16k', 'website': 'https://www.lagou.com/jobs/501604.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验不限 / 不限\n '}, {'position': 'php开发工程师', 'company': '掌阅', 'salary': '15k-25k', 'website': 'https://www.lagou.com/jobs/6237409.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验3-5年 / 本科\n '}, {'position': 'php开发工程师', 'company': '经传多赢', 'salary': '10k-18k', 'website': 'https://www.lagou.com/jobs/6820316.html?show=0d9c9879671444a5926680abc20322be', 'education': '经验1-3年 / 本科\n '}]

中国天气网

# coding=utf-8
import requests
from bs4 import BeautifulSoup
from pyecharts.charts import Bar
from pyecharts import options as opts
from pyecharts.globals import ThemeType

Temp = []
def parsePages(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/83.0.4090.0 Safari/537.36 Edg/83.0.467.0 '
    }
    response = requests.get(url, headers=headers)
    text =response.content.decode('utf-8')
    # soup = BeautifulSoup(text, 'lxml')
    soup = BeautifulSoup(text, 'html5lib')
    conMidtab = soup.find('div', attrs={'class': 'conMidtab'})
    tables = conMidtab.find_all('table')
    for table in tables:
        trs = table.find_all('tr')[2:]
        for index, tr in enumerate(trs):
            infors = list(tr.stripped_strings)
            city = infors[1] if index == 0 else infors[0]
            minTemp = infors[-2]
            Temp.append({'city': city, 'minTemp': int(minTemp)})

def main():
    urls = ['hb', 'db', 'hd', 'hz', 'hn', 'xn', 'xb', 'gat']
    for url in urls:
        url = f'http://www.weather.com.cn/textFC/{url}.shtml'
        parsePages(url)

    Temp.sort(key=lambda data:data['minTemp'])
    data = Temp[:10]
    cities = list(map(lambda x: x['city'], data))
    minTemp = list(map(lambda x:x['minTemp'], data))
    chart = Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, page_title='中国最低气温排行榜'))
    chart.add_xaxis(cities)
    chart.add_yaxis('最低气温', minTemp)
    chart.render()

if __name__ == '__main__':
    main()

古诗文网

# coding=utf-8
import requests
import re

def parsePages(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/83.0.4090.0 Safari/537.36 Edg/83.0.467.0 '
    }
    response = requests.get(url, headers=headers)
    text = response.text
    rTitles = re.compile(r'<div class="cont">.*?<b>(.*?)</b>', re.S)
    rDynasties = re.compile(r'<div class="cont">.*?<p class="source"><a.*?>(.*?)</a>', re.S)
    rAuthors = re.compile(r'<div class="cont">.*?<p class="source"><a.*?>.*?<a.*?>(.*?)</a>', re.S)
    rContsons = re.compile(r'<div class="cont">.*?<div class="contson".*?>(.*?)</div>', re.S)
    titles = re.findall(rTitles, text)
    dynasties = re.findall(rDynasties, text)
    authors = re.findall(rAuthors, text)
    contsons = re.findall(rContsons, text)
    contents = []
    for contson in contsons:
        x = re.sub(r'<.*?>', '', contson)
        contents.append(x)

    poems = []
    for title, dynasty, author, content in zip(titles, dynasties, authors, contents):
        poems.append({
            'title': title,
            'dynasty': dynasty,
            'author': author,
            'content': content
        })

    print(poems)

def main():
    url = 'https://www.gushiwen.org/default_1.aspx'
    parsePages(url)

if __name__ == '__main__':
    main()

[{'title': '满江红·夜雨凉甚忽动从戎之兴', 'dynasty': '宋代', 'author': '刘克庄', 'content': '\n金甲雕戈,记当日、辕门初立。磨盾鼻、一挥千纸,龙蛇犹湿。铁马晓嘶营壁冷,楼船夜渡风涛急。有谁怜、猿臂故将军,无功级。平戎策,从军什。零落尽,慵收拾。把茶经香传,时时温习。生怕客谈榆塞事,且教儿诵花间集。叹臣之壮也不如人,今何及。\n'}, {'title': '阮郎归·绍兴乙卯大雪行鄱阳道中', 'dynasty': '宋代', 'author': '向子諲', 'content': '\n江南江北雪漫漫,遥知易水寒。同云深处望三关,断肠山又山。天可老,海能翻,消除此恨难。频闻遣使问平安,几时鸾辂还?\n'}, {'title': '碧瓦', 'dynasty': '宋代', 'author': '范成大', 'content': '\n碧瓦楼头绣幙遮,赤栏桥外绿溪斜。无风杨柳漫天絮,不雨棠梨满地花。\n'}, {'title': '减字浣溪沙·秋水斜阳演漾金', 'dynasty': '宋代', 'author': '贺铸', 'content': '\n秋水斜阳演漾金,远山隐隐隔平林。几家村落几声砧。记得西楼凝醉眼,昔年风物似如今。只无人与共登临。\n'}, {'title': '次韵公实雷雨', 'dynasty': '宋代', 'author': '洪炎', 'content': '\n惊雷势欲拔三山,急雨声如倒百川。但作奇寒侵客梦,若为一震静胡烟。田园荆棘漫流水,河洛腥膻今几年。拟叩九关笺帝所,人非大手笔非椽。\n'}, {'title': '浣溪沙·雨歇梧桐泪乍收', 'dynasty': '清代', 'author': '纳兰性德', 'content': '\n雨歇梧桐泪乍收,遣怀翻自忆从头。摘花销恨旧风流。帘影碧桃人已去,屧痕苍藓径空留。两眉何处月如钩?\n'}, {'title': '交趾怀古', 'dynasty': '清代', 'author': '曹雪芹', 'content': '\n铜铸金镛振纪纲,声传海外播戎羌。马援自是功劳大,铁笛无烦说子房。\n'}, {'title': '野老', 'dynasty': '唐代', 'author': '杜甫', 'content': '\n野老篱前江岸回,柴门不正逐江开。渔人网集澄潭下,贾客船随返照来。长路关心悲剑阁,片云何意傍琴台。王师未报收东郡,城阙秋生画角哀。\n'}, {'title': '碧城三首', 'dynasty': '唐代', 'author': '李商隐', 'content': '\n碧城十二曲阑干,犀辟尘埃玉辟寒。阆苑有书多附鹤,女床无树不栖鸾。星沉海底当窗见,雨过河源隔座看。若是晓珠明又定,一生长对水晶盘。\n对影闻声已可怜,玉池荷叶正田田。不逢萧史休回首,莫见洪崖又拍肩。紫凤放娇衔楚佩,赤鳞狂舞拨湘弦。鄂君怅望舟中夜,绣被焚香独自眠。\n七夕来时先有期,洞房帘箔至今垂。玉轮顾兔初生魄,铁网珊瑚未有枝。检与神方教驻景,收将凤纸写相思。武皇内传分明在,莫道人间总不知。\n'}, {'title': '水龙吟·听兮清佩琼瑶些', 'dynasty': '宋代', 'author': '辛弃疾', 'content': '\n用“些语”再题瓢泉,歌以饮客,声韵甚谐,客皆为之釂。\n听兮清佩琼瑶些。明兮镜秋毫些。君无去此,流昏涨腻,生蓬蒿些。虎豹甘人,渴而饮汝,宁猿猱些。大而流江海,覆舟如芥,君无助、狂涛些。路险兮山高些。块予独处无聊些。冬槽春盎,归来为我,制松醪些。其外芳芬,团龙片凤,煮云膏些。古人兮既往,嗟予之乐,乐箪瓢些。\n'}]


04_HttpRunner通用_02_提取数据_02_regex方式


在 httprunner 中可以通过正则表达式进行文本内容的提取:

  • 从响应的实体中进行正则提取
  • 响应的实体必须是“JSON”或者“HTML文本”类型
  • 格式为: <左边界>(提取内容的正则表达式)<右边界>
  • 常用提取的正则: (.*) 代表边界里面可以是“任意字符出现任意次数”



\httprunner-2.5.5\httprunner\response.py:

在源码函数的注释中说明,通过正则方式提取时,可以从响应实体 JSON 或者 HTML 文本中提取

HttpRunner 源码中对 正则提取方式 的描述


提取规则(类似LR的处理):

  • 从实体文本中找到待提取的内容
  • 界定其左右边界
  • 将待提取内容用正则表达式方式代替并置入括号中


案例

  1. 目的:从返回响应的 HTML 文本中,将某图片的标签代码提取出来
  2. 特点:
  • 该图片在一对 <td> 标签中,可以分别作为识别的左右边界(要具备唯一性)
  • 左侧 <td> 标签中有宽度属性值,正好3个数字
- config:
    name: 用例 - 测试进销存系统
  
- test:
    name: 步骤 - 打开登录页面

    request:
      url: http://localhost/myweb/jxc/index.asp
      method: GET

    extract:
      # 从以下响应 HTML 中提取 img 标签内容
      # <td width="322"><img src="images/login_02.gif" width="323" height="340"></td>
      - x: <td width="\d{3}">(.*)</td>

    validate:
      # 验证提取是否正确(判断提取和预期的编码字符串是否相等)。  
      - eq: [$x, <img src="images/login_02.gif" width="323" height="340">]
      


以上案例中提取说明:

  • 前面(左边界)是:<td width="\d{3}">,其中 \d 代表数字,{3}代表出现3次,即此处需要出现3个数字
  • 后面(右边界)是:</td>
  • 中间括号中内容即为需要提取的字符串,其中“.”代表任意字符,“*”代表出现任意次数

正则提取规则详细分析


运行测试用例,查看测试报告中的日志信息,可以看到提取成功。

测试报告中展现提取结果 - 成功