如何从python中的字符串中提取月份和年份?

Posted

技术标签:

【中文标题】如何从python中的字符串中提取月份和年份?【英文标题】:How can i extract month and year from a string in python? 【发布时间】:2020-09-18 10:40:02 【问题描述】:

输入文本

text = "Wipro Limited | Hyderabad, IN                Dec 2017 – Present
Project Analyst 

Infosys | Delhi, IN                Apr 2017 – Nov 2017 
Software Developer 

HCL Technologies | Hyderabad, IN                Jun 2016 – Mar 2017 
Software Engineer  
"

我为此编写了一个代码,但它显示在每个提取的单词的列表中,并且无法执行任何操作。

regex = re.compile('(?P<month>[a-zA-Z]+)\s+(?P<year>\d4)\s+\–\s+(?P<month1>[a-zA-Z]+)\s+(?P<year1>\d4)')
mat = re.findall(regex, text)
mat

查看代码:https://regex101.com/r/mMlgYp/1。 我希望像下面这样的输出来预览日期并做出改变,然后计算总经验: 此处“当前”或“截止日期”应考虑当前月份和年份。

import time
Present = time.strftime("%m-%Y")
Present 
# output: '05-2020'
#Desired output
Extracted dates: 
[('Dec 2017 - Present'),
 ('Apr 2017 - Nov 2017'),
 ('Jun 2016 - Mar 2017')]# and so on ...should display all the search results 

First experience: 1.9 years 
second experience: 8 months
third experience: 7 months
# and so on ...should display all the search results 
Total experience: 3.4 years

请帮我解决这个问题,我是编程语言和 NLP、正则表达式的新手。

【问题讨论】:

【参考方案1】:

您可能最终希望在数据框中使用它,因为您将它标记为 pandas(请参阅Andrej's answer),但无论哪种方式,您都可以使用插值从字符串中解析日期:

fr"(?i)((?:months) *\d4) *(?:-|–) *(present|(?:months) *\d4)"

其中months 是所有可能的月份名称和缩写的交替组。

import calendar
import re
from datetime import datetime
from dateutil.relativedelta import relativedelta

text = """Wipro Limited | Hyderabad, IN                Dec 2017 – Present
Project Analyst 

Infosys | Delhi, IN                Apr 2017 – Nov 2017 
Software Developer 

HCL Technologies | Hyderabad, IN                Jun 2016 – Mar 2017 
Software Engineer  
"""

def parse_date(x, fmts=("%b %Y", "%B %Y")):
    for fmt in fmts:
        try:
            return datetime.strptime(x, fmt)
        except ValueError:
            pass

months = "|".join(calendar.month_abbr[1:] + calendar.month_name[1:])
pattern = fr"(?i)((?:months) *\d4) *(?:-|–) *(present|(?:months) *\d4)"
total_experience = None

for start, end in re.findall(pattern, text):
    if end.lower() == "present":
        today = datetime.today()
        end = f"calendar.month_abbr[today.month] today.year"

    duration = relativedelta(parse_date(end), parse_date(start))

    if total_experience:
        total_experience += duration
    else: 
        total_experience = duration

    print(f"start-end (duration.years years, duration.months months)")

if total_experience:
    print(f"total experience:  total_experience.years years, total_experience.months months")
else:
    print("couldn't parse text")

输出:

Dec 2017-May 2020 (2 years, 5 months)
Apr 2017-Nov 2017 (0 years, 7 months)
Jun 2016-Mar 2017 (0 years, 9 months)
total experience:  3 years, 9 months

【讨论】:

嗨@ggorlen,非常感谢您的快速响应。它非常适合上面的代码,但是当我将简历解析为文本并将这个文本文件传递给上面的代码时,它会抛出一个错误。 ibb.co/w42sQ6m。 (附图片)。错误:AttributeError:“NoneType”对象没有属性“years”。提前致谢。 你能把文字发到s吗?可能的原因是恢复字符串与此处显示的不同导致正则表达式失败,因此re.findall 未返回任何结果,total_experience 从未设置。 @ggorlen,请查看附件图片。 ibb.co/9p36RHG 请发帖as text。我无法复制和粘贴此内容或查看空格等。尽管如此,很明显添加在原始格式上运行良好的^(.+?) *\| *(.+?) * 不适用于此版本。如果您将其连同与 companylocation 相关的任何内容一起删除,它应该可以工作。 好的,谢谢。看我的更新。请记住,正则表达式无法解析任意格式的文本,因此编写可以从一堆随机 pdf 中提取日期范围的代码有点困难(不可能?)。它们必须具有相当良好的格式,并且尝试在所有边缘情况下编写可能是一项无休止的任务(两位数年份而不是四位数年份,'05 而不是 2005,不同的空格和分隔符。 ..).【参考方案2】:
import re
import numpy as np
import pandas as pd

text = '''Wipro Limited | Hyderabad, IN                Dec 2017 – Present
Project Analyst

Infosys | Delhi, IN                Apr 2017 – Nov 2017
Software Developer

HCL Technologies | Hyderabad, IN                Jun 2016 – Mar 2017
Software Engineer
'''

def pretty_format(monthts):
    return f'monthts/12:.1f years' if monthts > 11 else f'monthts:.1f months'

data = []
for employer, d1, d2 in re.findall(r'(.*?)\s*\|.*([A-Z][a-z]2 [12]\d3) – (?:([A-Z][a-z]2 [12]\d3)|Present)', text):
    data.append('Employer': employer, 'Begin': d1, 'End': d2 or np.nan)

df = pd.DataFrame(data)
df['Begin'] = pd.to_datetime(df['Begin'])
df['End'] = pd.to_datetime(df['End'])

df['Experience'] = ((df['End'].fillna(pd.to_datetime('now')) - df['Begin']) / np.timedelta64(1, 'M')).apply(pretty_format)
print(df)

total = np.sum(df['End'].fillna(pd.to_datetime('now')) - df['Begin']) / np.timedelta64(1, 'M')
print()
print(f'Total experience = pretty_format(total)')

打印:

           Employer      Begin        End  Experience
0     Wipro Limited 2017-12-01        NaT   2.5 years
1           Infosys 2017-04-01 2017-11-01  7.0 months
2  HCL Technologies 2016-06-01 2017-03-01  9.0 months

Total experience = 3.8 years

【讨论】:

OP 有 3.4 年,你有 3.8 年,我有 3.9 年。 @ggorlen 我认为这是四舍五入的问题,当我在格式中使用.2f 时,我得到3.83 years也死了 哦,等等,我得到了 3 年零 9 个月,这与小数点不同。我觉得一年的小部分很奇怪,所以我会坚持使用月份格式。 嗨@Andrej Kesely,非常感谢您的快速回复。感谢您应用后期阶段所需的数据框。对于上面的代码,它工作得很好,但是当我将简历解析为文本并将文本文件传递到上面的代码中时,它会抛出一个错误。 ibb.co/FwyQRmS。 (附图片)。错误:KeyError: 'Begin' 在处理上述异常的过程中,发生了另一个异常:

以上是关于如何从python中的字符串中提取月份和年份?的主要内容,如果未能解决你的问题,请参考以下文章

如何从熊猫数据框中提取日期/年份/月份?

如何在包含单词、三个字母月份和两位数字年份的字符串中搜索月份和年份并将它们转换为 SQL 中的日期?

如何从 DataFrame 的日期列中提取月份名称和年份

需要从redshift中的日期列中划分月份和年份

从 zoo::yearmon 对象中提取月份和年份

如何在sequelize ORM中使用从时间戳中选择并提取日期到月份和年份?