从文本中抽取国家名称,是一个命名实体识别的问题。但是因为国家个数有限,用机器学习大材小用,用直接匹配的方法看起来是快速且准确的选择。Python中也有第三方包可以调用,在这里整理一下相关资源。
Python从字符串串中如何提取国家、地区或者城市信息?中列出了两个方法。一个是Python工具包geotext,另一个是使用数据库自己匹配的方法。
geotext(0.4.0)
一个实用的Python工具包,主要是匹配的方法识别地名,包括国家和城市,可以转换为国家的ISO编码。这个包安装简单,速度非常快,处理一个句子长度的文本大概是 1 0 − 5 s 10^{-5}s 10−5s,可以用于大批量数据的处理。github上给的用法例子就很好了,搬运一下。
from geotext import GeoText
places = GeoText("London is a great city")
places.cities
# "London"
# filter by country code
result = GeoText('I loved Rio de Janeiro and Havana', 'BR').cities
# 'Rio de Janeiro'
GeoText('New York, Texas, and also China').country_mentions
# OrderedDict([(u'US', 2), (u'CN', 1)])
这个包的正确率很高,但是召回率不太行。识别出来的国家基本都是正确的,但是对于一些相对模糊的国家缩写识别不出来,比如USA和America都识别不出来,国家名称的变体比如Chinese也识别不出来。
GeoText('USA, America').countries
# []
geograpy3(0.2.2)
geograpy3是geograpy的一个分支,geograpy已经不再维护了,安装也是一堆报错。geograpy3与Python3兼容,geotext能实现的功能它都能实现,但安装稍微麻烦一些,速度也相对慢,处理一个句子长度的文本大概需要 1 0 − 1 s 10^{-1}s 10−1s,大数据量用起来还是比较慢。
说一下使用过程中遇到的问题。
问题
1. sqlite3.OperationalError: no such table: countries
这应该是这个包的一个bug,在github的问题中有提到[BUG]OperationalError: no such table: countries #59,也有看到其它同样的提问geograpy3: sqlite3.OperationalError: no such table。是因为某个需要读取数据的数据库是空的,所以从中读取数据表失败。
解决方法:
- 下载locations.db
- 解压后放在
$HOME/.geograpy3
的文件夹下,并把文件夹中的db文件删除。我用的是windows系统,是在C:\Users\<Username>\.geograpy3
这个路径。
2. nltk下载
诸如‘chunkers/maxent_ne_chunker/english_ace_multiclass.pickle’ not found
此类。
因为我nltk下载总是失败,所以需要手动下载。在nltk_data网站,下载maxent_ne_chunker
压缩包,解压后放在提示缺失的Searched in
任意一个文件夹中就可以了。
3. UnicodeDecodeError: ‘gbk’ codec can’t decode byte 0xa6 in position …
跳转到出错的源代码中,在open
的时候加个encoding=“utf8”
就解决了。
以上就是在初次使用过程中遇到的问题了。
使用方法
还是搬运一下github给出的示例。
方法一
geograpy.get_geoPlace_context
方法可以传入字符串(text参数),也可以传入需要提取信息的网址(url参数)。
- 国家、地区、城市等信息:使用
places.countries
查看国家,类似地可以查看地区places.regions
、查看城市places.cities
和其它places.other
。 - 地点之间关系的信息:使用
places.country_regions
查看按国家划分的地区,类似地可以查看按国家划分的城市places.country_cities
和与地理编码相关的字符串places.address_strings
。不过直观感受这个不太准。 - 国家、地区、城市及频次:使用
places.country_mentions
查看国家出现的次数,类似地还可以获取地区出现次数places.region_mentions
和城市出现次数places.city_mentions
。
这种方法可以识别出“xx国家的”这种所有格形式,比如“Chinese”,但是缩写识别不出来,比如“USA”。
import geograpy
text = "China borders 14 nations and extends across much of East Asia, bordering Vietnam, Laos, and Myanmar (Burma) in Southeast Asia; India, Bhutan, Nepal, Afghanistan, and Pakistan in South Asia; Tajikistan, Kyrgyzstan and Kazakhstan in Central Asia; and Russia, Mongolia, and North Korea in Inner Asia and Northeast Asia. Additionally, China shares maritime boundaries with South Korea, Japan, Vietnam, and the Philippines."
places = geograpy.get_geoPlace_context(text=text)
places.countries
# ['Pakistan', 'Laos', 'Bhutan', 'Kyrgyzstan', 'Tajikistan', 'India', 'Russia', 'Japan', 'Afghanistan', 'South Korea', 'Mongolia', 'North Korea', 'Vietnam', 'Myanmar', 'Philippines', 'Kazakhstan', "People's Republic of China", 'United States of America', 'Indonesia', 'Mexico', 'Georgia', 'Moldova', 'Italy']
places.country_regions
# {'Pakistan': [], 'Laos': [], 'Bhutan': [], 'Kyrgyzstan': [], 'Tajikistan': [], 'India': ['Asia', 'China'], 'Russia': [], 'Japan': [], 'Afghanistan': ['Pakistan'], 'South Korea': ['North Korea', 'South Asia', 'South Korea', 'Bhutan'], 'Mongolia': [], 'North Korea': ['North Korea', 'South Korea'], 'Vietnam': [], 'Myanmar': ['China'], 'Philippines': ['East Asia', 'South Asia', 'South Korea', 'North Korea'], 'Kazakhstan': [], "People's Republic of China": ['Mongolia', 'Inner Asia']}
places.country_mentions
# [("People's Republic of China", 2), ('Myanmar', 2), ('Laos', 1), ('India', 1), ('Bhutan', 1), ('Afghanistan', 1), ('Pakistan', 1), ('Tajikistan', 1), ('Kyrgyzstan', 1), ('Kazakhstan', 1), ('Russia', 1), ('Mongolia', 1), ('North Korea', 1), ('South Korea', 1), ('Japan', 1), ('Vietnam', 1), ('Philippines', 1), ('United States of America', 1), ('Indonesia', 1), ('Mexico', 1), ('Georgia', 1), ('Moldova', 1), ('Italy', 1)]
方法二
先用extraction.Extractor
中的find_geoEntities()
方法找到地理实体,再利用places.PlaceContext
中的set_countries()
方法获得国家,可以pc.countries
获得国家列表,也可以统计出现的次数pc.country_mentions
,这种方法得到的地名更准一点。地区pc.set_regions()
、城市pc.set_cities()
和其它pc.set_other()
也可以查看,值得注意的是,这种方法可以识别一些缩写,比如“USA”,会在其它而非国家的列表中。
from geograpy import extraction, places
text = "China borders 14 nations and extends across much of East Asia, bordering Vietnam, Laos, and Myanmar (Burma) in Southeast Asia; India, Bhutan, Nepal, Afghanistan, and Pakistan in South Asia; Tajikistan, Kyrgyzstan and Kazakhstan in Central Asia; and Russia, Mongolia, and North Korea in Inner Asia and Northeast Asia. Additionally, China shares maritime boundaries with South Korea, Japan, Vietnam, and the Philippines."
e = extraction.Extractor(text=text)
e.find_geoEntities()
pc = places.PlaceContext(e.places)
pc.set_countries()
pc.countries
# ['Kyrgyzstan', 'Nepal', "People's Republic of China", 'South Korea', 'India', 'Myanmar', 'Russia', 'North Korea', 'Kazakhstan', 'Japan', 'Laos', 'Mongolia', 'Bhutan', 'Pakistan', 'Vietnam', 'Afghanistan', 'Tajikistan', 'Philippines']
pc.country_mentions
# [("People's Republic of China", 2), ('Vietnam', 2), ('Myanmar', 2), ('Laos', 1), ('India', 1), ('Bhutan', 1), ('Nepal', 1), ('Afghanistan', 1), ('Pakistan', 1), ('Tajikistan', 1), ('Kyrgyzstan', 1), ('Kazakhstan', 1), ('Russia', 1), ('Mongolia', 1), ('North Korea', 1), ('South Korea', 1), ('Japan', 1), ('Philippines', 1)]