热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

简历信息提取(一)

0引言信息抽取任务旨在从非结构化的自然语言文本中提取结构化信息。在本系列项目中,将讨论如何又好又快地实现一个简历信息提取任务。作为该系列文章的第一篇,
0 引言

信息抽取任务旨在从非结构化的自然语言文本中提取结构化信息。在本系列项目中,将讨论如何又好又快地实现一个简历信息提取任务。

作为该系列文章的第一篇,我们将首先从数据处理着手,探讨Word、PDF格式文档信息提取的一些基本方法。

数据集简介

本文使用的简历数据集是脱敏之后的中文人才简历数据和标注数据。

标注类别包括:姓名、出生年月、性别、电话、最高学历、籍贯、落户市县、政治面貌、毕业院校、工作单位、工作内容、职务、项目名称、项目责任、学位、毕业时间、工作时间、项目时间共18个字段。

在训练数据集中,每个“毕业院校、学位、毕业时间”为一组,以“教育经历”列表给出;每个“工作单位、工作内容、职务、工作时间”为一组,以“工作经历”列表给出;每个“项目名称、项目责任、项目时间”为一组,以“项目经历”列表给出。

训练数据同时给出了PDF和Word格式。

1 word文档信息提取

1.1 认识.docx格式

现在.docx格式是我们最常见到的一种Word文档格式了,它是微软采用类XML格式标准定义的Word文件。

正因如此,相比早期的.doc文件,.docx文件的兼容性大幅提升。

那么,.docx文件又是如何封装的?.docx实际上是一个zip的压缩文件,比如我们任选一个.docx文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tDww3qlB-1636944651045)(https://ai-studio-static-online.cdn.bcebos.com/acc7880fa91247a5a2e3fa8e7c5d3670aacca1de565f4c84ad3eb59072898d34)]

再把它的文件名后缀直接修改为.zip

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nb8vRFMz-1636944651047)(https://ai-studio-static-online.cdn.bcebos.com/14181f975e9848699ec46d5462ea9b87c12ed7c9ccb74022a865ab353d6b25f9)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RG7tYJCb-1636944651048)(https://ai-studio-static-online.cdn.bcebos.com/ae2c0b4c43074dab8f07bda0e0ec7d596809c919ef6f4fb6bae0f83484460c22)]

打开这个压缩包,我们会看到,其实文档的正文,是以XML格式表示的,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h3PlibKu-1636944651049)(https://ai-studio-static-online.cdn.bcebos.com/2ad3cac751834ffc987521534a5f05c07b3866a1f7f9460a8db4367ccea01e13)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m00tpBaH-1636944651050)(https://ai-studio-static-online.cdn.bcebos.com/000a892628ac40dba8aeb042b5e6d951772bd02d00c74c2abf630b42dfab9f03)]

因此,其实除了python-docx库之外,BeautifulSoup也可以用来提取Word文档信息。

1.2 提取word文档信息


1.2.1 用python-docx提取文档信息

python-docx其实在项目PaddleHub机器翻译:文档的批量翻译中提到过

这里不做过多赘述,我们直奔主题,选取一份word简历文档,尝试提取信息。

# 解压缩数据集
!unzip data/data40148/train_20200121.zip

# 安装依赖库
!pip install python-docx

from docx import Document
from docx.shared import Inches

def get_paragraphs_text(path):"""获取所有段落的文本:param path: word路径:return: list类型,如:['Test', 'hello world', ...]"""document = Document(path) # 有的简历是表格式样的,因此,不仅需要提取正文,还要提取表格col_keys = [] # 获取列名col_values = [] # 获取列值index_num = 0# 表格提取中,需要添加一个去重机制fore_str = ""cell_text = ""for table in document.tables:for row_index,row in enumerate(table.rows):for col_index,cell in enumerate(row.cells):if fore_str != cell.text:if index_num % 2==0:col_keys.append(cell.text)else:col_values.append(cell.text)fore_str = cell.textindex_num +=1cell_text += cell.text + '\n'# 提取正文文本paragraphs_text = ""for paragraph in document.paragraphs:# 拼接一个list,包括段落的结构和内容paragraphs_text += paragraph.text + "\n"return cell_text, paragraphs_text

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IjR1FCv3-1636944651051)(https://ai-studio-static-online.cdn.bcebos.com/d65ae7bb5eab45e0901df04f08837eb21e0968a120f6402cac257da88b8d99aa)]

# 提取文档信息,这是一个表格式简历
cell_text, paragraphs_text = get_paragraphs_text('resume_train_20200121/docx/0043e770a330.docx')
print(cell_text, paragraphs_text)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9GJ9ghq-1636944651051)(https://ai-studio-static-online.cdn.bcebos.com/c0cc78f2688c4f9ab4f9934ea2584b946826aafa4d914d8a8246882e94a71f1c)]

# 提取文档信息,这是一个表格正文混搭式简历(会出现一些混乱的情况,因为在这个文件里,标题是以表格形式存在)
cell_text, paragraphs_text = get_paragraphs_text('resume_train_20200121/docx/0052b7958e89.docx')
print(cell_text, paragraphs_text)

杜素宁
MOBILE : 15904130130
E-MAIL:0da08x@163.com
Address:云南省昭通市个人信息民族:汉
籍贯:云南省昭通市
性别:女 年龄: 18
教育经历2008.08-2012.08
北方工业大学
食品科学与工程
学士学位
主要经历
Project Experience1997.06-2010.07
江苏华英企业管理股份有限公司
水处理工程师
1991年12月-2012年09月
和宇健康科技股份有限公司
市场营销专员
2007/05-2010/03
深圳市有棵树科技有限公司
拼多多运营
个人技能
Personal Skills个人荣誉
Personal Honor工作经历:
工作内容:
1.负责部门内日常用品的采购;2.做好与公司内其他部门的对接工作;3.协助部门进行办公环境管理和后勤管理工作;4.销售人员与公司的信息交流,随时保持与市场销售人员的电话沟通,销售政策及公司文件的及时传达。5.领导交办的其他工作工作经历:
工作内容:
1、做好消费宾客的迎、送接待工作,接受宾客各种渠道的预定并加以落实;2、礼貌用语,详细做好预订记录;3、了解和收集宾客的建议和意见并及时反馈给上级领导;4、以规范的服务礼节,树立公司品牌优质,文雅的服务形象。工作经历:
工作内容:
1.负责规定区域的产品销售,做好产品介绍,确认订单,回款等销售相关工作;2.做好客户背景资料调查,竞争对手分析,产品适用性分析;3.按公司规定完成SalesPipeline信息记录吃饭
优秀学生干部

1.2.2 用BeautifulSoup提取Word文档信息

如果说这种方法有什么缺点的话,就是遇到一些艺术字等标题,与后面的文字从语义上是连续的,但是XML格式不连续。

# 安装依赖库
!pip install bs4

from zipfile import ZipFile
from bs4 import BeautifulSoupdocument = ZipFile('resume_train_20200121/docx/0052b7958e89.docx')
xml = document.read("word/document.xml")
wordObj = BeautifulSoup(xml.decode("utf-8"))
texts = wordObj.findAll("w:t")
for text in texts:print(text.text)

杜素宁
MOBILE
:
15904130130
E-MAIL
:
0da08x@163.com
A
ddress
:云南省昭通市
个人信息
民族:

籍贯:
云南省昭通市
性别:
女年龄
: 18教育经历
2008.08-2012.08北方工业大学
食品科学与工程
学士学位主要经历
P
roject Experience
工作经历:
1997.06-2010.07
江苏华英企业管理股份有限公司
水处理工程师
工作内容:
1.负责部门内日常用品的采购;2.做好与公司内其他部门的对接工作;3.协助部门进行办公环境管理和后勤管理工作;4.销售人员与公司的信息交流,随时保持与市场销售人员的电话沟通,销售政策及公司文件的及时传达。5.领导交办的其他工作工作经历:
1991年12月-2012年09月
和宇健康科技股份有限公司
市场营销专员
工作内容:
1、做好消费宾客的迎、送接待工作,接受宾客各种渠道的预定并加以落实;2、礼貌用语,详细做好预订记录;3、了解和收集宾客的建议和意见并及时反馈给上级领导;4、以规范的服务礼节,树立公司品牌优质,文雅的服务形象。工作经历:
2007/05-2010/03
深圳市有棵树科技有限公司
拼多多运营
工作内容:
1.负责规定区域的产品销售,做好产品介绍,确认订单,回款等销售相关工作;2.做好客户背景资料调查,竞争对手分析,产品适用性分析;3.按公司规定完成SalesPipeline信息记录个人技能
PersonalSkills
吃饭个人荣誉
P
ersonal Honor
优秀学生干部

2 使用pdfplumber库提取PDF信息

!pip install pdfplumber

import pdfplumber
import pandas as pdwith pdfplumber.open("resume_train_20200121/pdf/0052b7958e89.pdf") as pdf:page = pdf.pages[0] # 第一页的信息text = page.extract_text()print(text)

杜素宁
MOBILE : 15904130130
E-MAIL:0da08x@163.com Address:云南省昭通市
个人信息 民族:汉 籍贯:云南省昭通市 性别:女 年龄: 18 教育经历 2008.08-2012.08 北方工业大学 食品科学与工程 学士学位 主要经历
Project Experience
工作经历:
1997.06-2010.07 江苏华英企业管理股份有限公司 水处理工程师
工作内容:
1.负责部门内日常用品的采购;2.做好与公司内其他部门的对接工作;3.协助部门进行办公环境管理和后勤管理工作;4.销
售人员与公司的信息交流,随时保持与市场销售人员的电话沟通,销售政策及公司文件的及时传达。5.领导交办的其他工作 工作经历:
1991年12月-2012年 和宇健康科技股份有限公司 市场营销专员
09月
工作内容:
1、做好消费宾客的迎、送接待工作,接受宾客各种渠道的预定并加以落实;2、礼貌用语,详细做好预订记录;3、了解和
收集宾客的建议和意见并及时反馈给上级领导;4、以规范的服务礼节,树立公司品牌优质,文雅的服务形象。 工作经历:
2007/05-2010/03 深圳市有棵树科技有限公司 拼多多运营
工作内容:
1.负责规定区域的产品销售,做好产品介绍,确认订单,回款等销售相关工作;2.做好客户背景资料调查,竞争对手分析,
产品适用性分析;3.按公司规定完成SalesPipeline信息记录

with pdfplumber.open("resume_train_20200121/pdf/0052b7958e89.pdf") as pdf:page = pdf.pages[0] # 第一页的信息table = page.extract_tables()for t in table:# 得到的table是嵌套list类型,转化成DataFrame更加方便查看和分析df = pd.DataFrame(t[1:], columns=t[0])print(t)

[['个人信息', None, None, '', None, None, None], ['民族:汉', '', '籍贯:云南省昭通市', None, '', '性别:女', '年龄: 18']]
[['教育经历', None, None, '', None, None], ['2008.08-2012.08', '', '北方工业大学', None, '食品科学与工程', '学士学位']]
[['主要经历 \nProject Experience', '']]
[['1997.06-2010.07', '江苏华英企业管理股份有限公司', '水处理工程师']]
[['', '1991年12月-2012年', '', '和宇健康科技股份有限公司', '市场营销专员'], [None, '09月', None, None, None]]
[['2007/05-2010/03', '深圳市有棵树科技有限公司', '拼多多运营']]

因此,对于同时有表格(标题用表格表示)和正文交错的文档,pdfplumber也能很好地识别,如果是布局标准的纵向PDF,效果还是非常稳定的。

3 通过PPOCR识别PDF

这个方式看起来有点绕,如果可以直接识别PDF和word,为什么还要转图片用OCR呢?

不过,在很多时候,它还是一个很必要的补充手段,毕竟,如果简历的PDF是那种扫描式的,甚至word文档万一就是个图片……

手段多一些,总是有备无患的。

在本文中,我们尝试的是PP-Structure工具包,它提供了PDF图片表格一键提取解决方案。

# 安装依赖库
# pywt可能要重启内核
!pip install pywt -i https://mirror.baidu.com/pypi/simple

!pip install "paddleocr>=2.2" --no-deps -r requirements.txt

# 安装依赖库
!pip install -U https://paddleocr.bj.bcebos.com/whl/layoutparser-0.0.0-py3-none-any.whl
!pip install PyMuPDF

import datetime
import os
import fitz # fitz就是pip install PyMuPDF
import cv2
import shutil
from paddleocr import PPStructure,draw_structure_result,save_structure_res
import numpy as np

3.1 PDF批量转图片

def pyMuPDF_fitz(pdfPath, imagePath):startTime_pdf2img = datetime.datetime.now() # 开始时间print("imagePath=" + imagePath)pdfDoc = fitz.open(pdfPath)for pg in range(pdfDoc.pageCount):page = pdfDoc[pg]rotate = int(0)# 每个尺寸的缩放系数为4,这将为我们生成分辨率提高4的图像。# 此处若是不做设置,默认图片大小为:792X612, dpi=96zoom_x = 4 # (1.33333333-->1056x816) (2-->1584x1224)zoom_y = 4mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)pix = page.getPixmap(matrix=mat, alpha=False)if not os.path.exists(imagePath): # 判断存放图片的文件夹是否存在os.makedirs(imagePath) # 若图片文件夹不存在就创建pix.writePNG(imagePath + '/' + 'images_%s.png' % pg) # 将图片写入指定的文件夹内endTime_pdf2img = datetime.datetime.now() # 结束时间print('pdf转图片时间=', (endTime_pdf2img - startTime_pdf2img).seconds)

pdfDir = './resume_train_20200121/pdf'def gci(filepath):
#遍历filepath下所有文件 files = os.listdir(filepath) for fi in files:# 将转换的图片保存到对应imgs的对应子目录下 pyMuPDF_fitz(os.path.join(filepath,fi), os.path.join('imgs',fi[:-4]))

# 遍历PDF文件并转换图片。如果读者觉得转换时间比较长,可以提前中止cell。
gci(pdfDir)

3.2 对不同式样简历提取信息

下面这两张简历我们可以看出,对于一些表格式简历,版面分析会将其判定为表格。

其它的简历,可能被认为版面上是图片甚至既没有图片也没有表格。

如果使用PP-Structure,对这几种检测结果,需要分别进行后处理。

import cv2
import layoutparser as lp
image = cv2.imread('imgs/0043e770a330/images_0.png')
image = image[..., ::-1]# 加载模型
model = lp.PaddleDetectionLayoutModel(config_path="lp://PubLayNet/ppyolov2_r50vd_dcn_365e_publaynet/config",threshold=0.5,label_map={0: "Text", 1: "Title", 2: "List", 3:"Table", 4:"Figure"},enforce_cpu=False,enable_mkldnn=True)
# 检测
layout = model.detect(image)# 显示结果
show_img = lp.draw_box(image, layout, box_width=3, show_element_type=True)

show_img

from bs4 import BeautifulSoup
import re

# 表格类的预测结果已经封装在HTML标签中了
table_engine = PPStructure(show_log=True)
img = cv2.imread('imgs/0043e770a330/images_0.png')
result = table_engine(img)
result[0]['res']

# 折行问题需要在后续进一步解决
html = BeautifulSoup(result[0]['res'],'html.parser', from_encoding='utf-8')
list(html.strings)

['姓名','郝淑宁','性别','男','出生日期','2000.04','民族','汉族','联系电话','13602173036','籍贯','黑龙江省双鸭山「邮箱 市','bhluo@live.com','教育背景','毕业时间:','2012.08 - 2016.08','毕业学校:北京政法职业学院','学历/学位:大学本科/学士学位','专业:动物生产','工作经验','时间:1991年07月-2016年12月','部门:研发部','公司:江苏达科信息科技有限公司','职位:渠道商务','1、申请票据,购***,准备和报送会计报表,会报税及报税流程;2、现金及银行收付处理,制作记帐','凭证,银行对帐,单据审核,开具与保管发票;3、协助财会文件的准备、归档和保管;4、固定资产和','工作;','低值易耗品的登记和管理;5、负责与银行、税务等部门的对外联络;6、协助领导完成其他日常事务性','时间:1992年05月-2015年05月','公司:盛趣信息技术有限公司部门:研发部','职位:商场空调工5.5k','1、根据公司制定的目标,制定有效销售计划,开发客户,完成销售任务;2、关注市场变化,收集有效','的市场信息,为公司的销售策略作参考资料依据;3、做好客户管理工作,根据不同客户的需求特点和 相关的信息、数据,提供解决方案;4、统筹客户维系工作,做好客户拜访计划,并按计划进行拜访,','推进项目;5、协助领导做好公司其他工作。','时间:2006.06-2018.06','公司:广州市华粤行仪器有限公司','部门:研发部','职位:Golang开发工程师','1、登记收集资料,整理文件表格;2、辅助就业指导老师为鹏程学员推荐工作;3、发布招聘信息,与','鹏程学员互动;4、对接好企业,为鹏程学员推荐心仪工作到面到岗服务。','项目经验','2001.12-2012.01 1、协助制定市场活动计划,组织落实市场活动;2、负责展会活动策划与相关活动支持,市场推广资料','珠三角最低工资标准的执行、影响与对策研究']

import cv2
import layoutparser as lp
image = cv2.imread('imgs/0052b7958e89/images_0.png')
image = image[..., ::-1]# 加载模型
model = lp.PaddleDetectionLayoutModel(config_path="lp://PubLayNet/ppyolov2_r50vd_dcn_365e_publaynet/config",threshold=0.5,label_map={0: "Text", 1: "Title", 2: "List", 3:"Table", 4:"Figure"},enforce_cpu=False,enable_mkldnn=True)
# 检测
layout = model.detect(image)# 显示结果
show_img = lp.draw_box(image, layout, box_width=3, show_element_type=True)
show_img

with open(os.path.join('data', 'res.txt'), 'w', encoding='utf8') as f:for img in os.listdir('imgs/0043e770a330'):img_path = os.path.join(fi_d,img)img = cv2.imread(img_path)result = table_engine(img)print(result[0]['res'])for box, rec_res in zip(result[0]['res'][0], result[0]['res'][1]):f.write('{}\t{}\n'.format(np.array(box).reshape(-1).tolist(), rec_res))

table_engine = PPStructure(show_log=True)
img = cv2.imread('imgs/0052b7958e89/images_0.png')
result = table_engine(img)

result[0]['res']

for box, rec_res in zip(result[0]['res'][0], result[0]['res'][1]):print(rec_res[0])

杜素宁
MOBILE:15904130130
E-MAIL:0da08x@163.com
Address:云南省昭通市
个人信息
性别:女
民族:汉
籍贯:云南省昭通市
年龄:18
教育经历
北方工业大学
食品科学与工程
2008.08-2012.08
学士学位
主要经历
Project Experience
工作经历:
1997.06-2010.07
江苏华英企业管理股份有限公司
水处理工程师
工作内容:
1.负责部门内日常用品的采购;2.做好与公司内其他部门的对接工作;3.协助部门进行办公环境管理和后勤管理工作;4.销
售人员与公司的信息交流,随时保持与市场销售人员的电话沟通,销售政策及公司文件的及时传达。5.领导交办的其他工作
工作经历:
1991年12月-2012年
和宇健康科技股份有限公司
市场营销专员
09月
工作内容:
1、做好消费宾客的迎、送接待工作,接受宾客各种渠道的预定并加以落实;2、礼貌用语,详细做好预订记录;3、了解和
收集宾客的建议和意见并及时反馈给上级领导;4、以规范的服务礼节,树立公司品牌优质,文雅的服务形象
工作经历:
2007/05-2010/03
深圳市有棵树科技有限公司
拼多多运营
工作内容:
1.负责规定区域的产品销售,做好产品介绍,确认订单,回款等销售相关工作;2.做好客户背景资料调查,竞争对手分析
产品适用性分析;3.按公司规定完成SalesPipeline信息记录

4 小结

在本文中,我们探索并比较了Word、PDF、图片格式的简历文件信息提取方法,在后续项目中,将结合PaddleNLP的信息提取技术,尝试将非结构化的简历文件,提取成标注文件指定的格式,并验证性能。


推荐阅读
  • 本文详细介绍了如何使用 Python 编程语言中的 Scapy 库执行 DNS 欺骗攻击,包括必要的软件安装、攻击流程及代码示例。 ... [详细]
  • 使用R语言进行Foodmart数据的关联规则分析与可视化
    本文探讨了如何利用R语言中的arules和arulesViz包对Foodmart数据集进行关联规则的挖掘与可视化。文章首先介绍了数据集的基本情况,然后逐步展示了如何进行数据预处理、规则挖掘及结果的图形化呈现。 ... [详细]
  • 在CentOS 7中部署Nginx并配置SSL证书
    本文详细介绍了如何在CentOS 7操作系统上安装Nginx服务器,并配置SSL证书以增强网站的安全性。适合初学者和中级用户参考。 ... [详细]
  • 本文介绍了如何使用 Python 的 Pyglet 库加载并显示图像。Pyglet 是一个用于开发图形用户界面应用的强大工具,特别适用于游戏和多媒体项目。 ... [详细]
  • 本文介绍了在解决Hive表中复杂数据结构平铺化问题后,如何通过创建视图来准确计算广告日志的曝光PV,特别是针对用户对应多个标签的情况。同时,详细探讨了UDF的使用方法及其在实际项目中的应用。 ... [详细]
  • 本文介绍如何使用 Arcade 库在 Python 中绘制太阳,包括环境配置、基础图形绘制方法及具体代码示例。 ... [详细]
  • 本文详细介绍了如何在本地环境中安装配置Frida及其服务器组件,以及如何通过Frida进行基本的应用程序动态分析,包括获取应用版本和加载的类信息。 ... [详细]
  • CentOS 7 实验记录:解决磁盘故障案例分析
    本文详细记录了一次在 CentOS 7 系统上遇到的磁盘故障处理过程,主要探讨了因额外磁盘缺失但 fstab 文件中仍保留其挂载配置而导致的问题,并提供了具体的解决方案。 ... [详细]
  • Kubernetes Services详解
    本文深入探讨了Kubernetes中的服务(Services)概念,解释了如何通过Services实现Pods之间的稳定通信,以及如何管理没有选择器的服务。 ... [详细]
  • 探索CNN的可视化技术
    神经网络的可视化在理论学习与实践应用中扮演着至关重要的角色。本文深入探讨了三种有效的CNN(卷积神经网络)可视化方法,旨在帮助读者更好地理解和优化模型。 ... [详细]
  • 本文详细介绍了如何在PHP中使用Memcached进行数据缓存,包括服务器连接、数据操作、高级功能等。 ... [详细]
  • 将XML数据迁移至Oracle Autonomous Data Warehouse (ADW)
    随着Oracle ADW的推出,数据迁移至ADW成为业界关注的焦点。特别是XML和JSON这类结构化数据的迁移需求日益增长。本文将通过一个实际案例,探讨如何高效地将XML数据迁移至ADW。 ... [详细]
  • Hadoop MapReduce 实战案例:手机流量使用统计分析
    本文通过一个具体的Hadoop MapReduce案例,详细介绍了如何利用MapReduce框架来统计和分析手机用户的流量使用情况,包括上行和下行流量的计算以及总流量的汇总。 ... [详细]
  • Python网络编程:深入探讨TCP粘包问题及解决方案
    本文详细探讨了TCP协议下的粘包现象及其产生的原因,并提供了通过自定义报头解决粘包问题的具体实现方案。同时,对比了TCP与UDP协议在数据传输上的不同特性。 ... [详细]
  • 使用Python构建网页版图像编辑器
    本文详细介绍了一款基于Python开发的网页版图像编辑工具,具备多种图像处理功能,如黑白转换、铅笔素描效果等。 ... [详细]
author-avatar
转身-说离别2013
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有