使用NLP和ML来提取和构造Web数据

原文链接:https://blog.csdn.net/fendouaini/article/details/109374462

作者|Conner Brew 编译|VK 来源|Towards Data Science

介绍

在本文中,我们将创建一个基于战争研究所(ISW)的结构化文档数据库。ISW为外交和情报专业人员提供信息产品,以加深对世界各地发生的冲突的了解。

要查看与本文相关联的原始代码和Notebook,请访问以下链接:https://colab.research.google.com/drive/1pTrOXW3k5VQo1lEaahCo79AHpyp5ZdfQ?usp=sharing

要访问Kaggle上托管的最终结构化数据集,请访问以下链接:https://www.kaggle.com/connerbrew2/isw-web-scrape-and-nlp-enrichment

本文将是一个关于web抽取、自然语言处理(NLP)和命名实体识别(NER)的练习。对于NLP,我们将主要使用开源Python库NLTK和Spacy。

本文旨在演示web提取和NLP的一个用例,而不是关于这两种技术使用的全面初学者教程。如果你是NLP或web提取的新手,我建议你遵循不同的教程,或者浏览Spacy、BeautifulSoup和NLTK文档页面。

# 导入库

import requests
import nltk
import math
import re
import spacy
import regex as re
import pandas as pd
import numpy as np
import statistics as stats
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import json

你需要从NLTK下载一些包。

from bs4 import BeautifulSoup
from nltk import *
nltk.download(‘stopwords’)
nltk.download(‘punkt’)
from nltk.corpus import stopwords

#在大多数环境中,你需要安装NER-D。

!pip install ner-d
from nerd import ner

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.cluster import MiniBatchKMeans
from sklearn.feature_extraction.text import TfidfVectorizer

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

初始化变量

首先,我们将初始化最终结构化数据中需要的数据字段。对于每个文档,我要提取标题、发布日期、人名、地名和其他各种信息。我们还将增强文档中已经存在的信息—例如,我们将使用文档中的地名来获取相关的坐标,这对于以后可视化数据非常有用。

# 初始化最终数据集的数据字段

dates=[]
titles=[]
locations=[]
people=[]
key_countries=[]
content_text=[]
links=[]
coord_list=[]
mentioned_countries=[]
keywords=[]
topic_categories=[]

为后面的主题模型初始化簇变量

cluster_keywords=[]
cluster_number=[]

使用SPACY库初始化NLP对象

nlp = spacy.load(“en_core_web_sm”)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

提取href

我们将从ISW的生产库中提取文档。首先,我们将抓取“浏览”页面以获取每个产品的单独href链接。然后我们将这些链接存储在一个列表中,供提取函数稍后访问。

# #从ISW浏览页面获取产品链接

urls=[‘http://www.understandingwar.org/publications?page={}’.format(i) for i in range(179)]
hrefs=[]

def get_hrefs(page,class_name):
page=requests.get(page)
soup=BeautifulSoup(page.text,‘html.parser’)
container=soup.find_all(‘div’,{‘class’:class_name})
container_a=container[0].find_all(‘a’)
links=[container_a[i].get(‘href’) for i in range(len(container_a))]
for link in links:
if link[0]==’/’:
hrefs.append(‘http://www.understandingwar.org’+link)

for url in urls:
get_hrefs(url,‘view-content’)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Web爬取

我们将要编写的前几个函数是相当简单的文本提取。本教程不是关于BeautifulSoup用法的教程,要了解Python中的web爬取,请查看这里的文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/

获得日期

对于我们的第一个函数,我们将提取发布日期。它扫描从产品网页中提取的html文档,并找到一个类为“submitted”的字段。这是我们的生产日期。

获得标题

接下来,我们需要产品名称。同样,这个字段被方便地标记为“title”类。

获取所有文本

最后,我们将提取文档的全文。当我提取文本时,我通常遵循“先提取,后过滤”的web提取方式。这意味着,在最初的文本提取中,我对文本执行最少的过滤和处理。我更愿意在以后的分析中进行处理,因为这是必要的。但是,如果你想更进一步,你可能希望对提取的文本进行比下面函数演示的更多的预处理。

对于我的get_contents函数,我坚持最基本的原则——我在黑名单中列出了一些不想被提取的文本。然后从页面中提取所有文本并将其附加到一个临时字符串中,该字符串又被附加到列表content_text中。

# 提取发布数据

def get_date(soup):
try:
data=soup.find(‘span’,{‘class’:‘submitted’})
content=data.find(‘span’)
date=content.get(‘content’)
dates.append(date)
except Exception:
dates.append(’’)
pass

提取产品标题

def get_title(soup):
try:
title=soup.find(‘h1’,{‘class’:‘title’}).contents
titles.append(title[0])
except Exception:
titles.append(’’)
pass

提取产品的文本内容

def get_contents(soup):
try:
parents_blacklist=[’[document]’,‘html’,‘head’,
‘style’,‘script’,‘body’,
‘div’,‘a’,‘section’,‘tr’,
‘td’,‘label’,‘ul’,‘header’,
‘aside’,]
content=’’
text=soup.find_all(text=True)

for t in text:if t.parent.name not in parents_blacklist and len(t) > 10:content=content+t+' 'content_text.append(content)

except Exception:
content_text.append(’’)
pass

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

自然语言处理

接下来,我们将找出产品中引用了哪些国家。有很多API可以用于检查国家的文本内容,但这里我们将使用一个简单的方法:列出世界上所有国家的列表。这个列表来自维基百科:https://en.wikipedia.org/wiki/Lists_of_countries_and_territories

在函数得到all_mentioned_countries 后,它使用基本统计分析来确定哪些国家最突出——这些国家最有可能成为文件叙述的焦点。为此,该函数计算整个文档中提到一个国家的次数,然后查找比平均值提到次数多的国家。然后将这些国家追加到key_countries列表中。

# 在正文中引用所有国家的列表。
# 如果文本中的一个单词与列表中的一个国家匹配,那么它将被添加到国家列表中。 

def get_countries(content_list):
iteration=1
for i in range(len(content_list)):
print(‘Getting countries’,iteration,’/’,len(content_list))
temp_list=[]
for word in word_tokenize(content_list[i]):
for country in country_list:
if word.lower().strip() == country.lower().strip():
temp_list.append(country)

counted_countries=dict(Counter(temp_list))
temp_dict=dict.fromkeys(temp_list,0)
temp_list=list(temp_dict)
if len(temp_list)==0:temp_list.append('Worldwide')
mentioned_countries.append(temp_list)# 计算每个国家被提及的次数,然后对照平均值检查每次计数。 
# 如果一个国家被提及的次数超过了平均次数,它就会作为一个关键字被记录。keywords=[]
for key in counted_countries.keys():if counted_countries[key] > np.mean(list(counted_countries.values())):keywords.append(key)
if len(keywords) != 0:key_countries.append(keywords)
else:key_countries.append(temp_list)
iteration+=1<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li></ul></pre> 

命名实体识别:地点

接下来,我们要丰富我们的数据。最终,结构化数据的目标通常是执行某种分析或可视化——在这种国际冲突信息的情况下,将信息按地理位置绘制出来是很有价值的。为此,我们需要与文档对应的坐标。

找到地名

首先,我们将使用自然语言处理(NLP)和命名实体识别(NER)从文本中提取地名。

NLP是机器学习的一种形式,计算机算法使用语法和语法规则来学习文本中单词之间的关系。通过这种学习,NER能够理解某些单词在句子或段落中所起的作用。本教程并不打算全面介绍NLP—对于这样的资源,请查看:https://medium.com/@ODSC/an-introduction-to-natural-language-processing-nlp-8e476d9f5f59

从外部API获取坐标

为了找到地名的坐标,我们将使用 Open Cage API查询坐标;你可以在这里创建一个免费帐户并接收API密钥。还有许多其他流行的地理api可供选择,但通过反复试验,我发现Open-Cage在中东地区有着最好的性能。

首先,我们迭代从文档中检索到的每个地名,并在Open Cage中查询它。一旦完成这项工作,我们将对比Open Cage与先前创建的mentioned_countries 列表。这将确保我们检索的查询结果位于正确的位置。

# 使用NLP提取地名,然后查询open-cage API以获得绘图所需的坐标

插入你自己的OpenCage API key:

geo_api_key=‘Insert Your API Key Here’

def get_coords(content_list):
iteration=1
for i in range(len(content_list)):
print(‘Getting coordinates’,iteration,’/’,len(content_list))
temp_list=[]
text=content_list[i]

# 应用一个NER算法,从python库'ner-d'中查找地名。doc=nlp(text)
location=[X.text for X in doc.ents if X.label_ == 'GPE']
location_dict=dict.fromkeys(location,0)
location=list(location_dict)# 查询位置。for l in location:try: request_url='https://api.opencagedata.com/geocode/v1/json?q={}&amp;key={}'.format(l,geo_api_key)page=requests.get(request_url)data=page.json()for n in range(len(data)):# 这行代码检查查询结果中的国家是否与mentioned_countries之一相匹配。如果不是,那么查询结果很可能是假正例。if data['results'][n]['components']['country'] in mentioned_countries[i]:lat=data['results'][n]['geometry']['lat']lng=data['results'][n]['geometry']['lng']coordinates={'Location': l,'Lat': lat,'Lon': lng}temp_list.append(coordinates)breakelse:continue    except Exception: continue
coord_list.append(temp_list)
iteration+=1<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li><li style="color: rgb(153, 153, 153);">42</li><li style="color: rgb(153, 153, 153);">43</li><li style="color: rgb(153, 153, 153);">44</li></ul></pre> 

命名实体识别:人

接下来,我们将提取文档中提到的人的姓名。为此,我们将再次使用NER-d python库中的NER算法。

获取全名

在最终的结构化数据中,我只想要全名。只找到“Jack”或“John”的数据,会不会令人困惑?为此,我们将再次使用一些基本的统计数据。当提到全名时,函数将跟踪全名,通常是在文本的开头。

当后面提到部分名称时,它将引用全名列表,以标识部分名称引用的是谁。例如,如果一篇新闻文章是这样写的:“乔·拜登正在竞选总统。乔是前总统奥巴马的副总统,我们知道乔指的是拜登,因为他的全名在文中早些时候已经给出。此函数将以相同的方式运行。

重复的名字

如果出现了重复的情况,该函数将使用前面用于国家/地区函数的相同统计数据。它将测量一个名字被提及的次数,并将其作为最有可能的名字。例如:乔·拜登和他的儿子亨特·拜登都是受欢迎的美国政治家。乔·拜登是前副总统。拜登现在正在与现任总统唐纳德·特朗普竞选总统”。根据文本的统计重点,这篇文章显然是关于乔·拜登,而不是亨特·拜登。

验证名字

一旦函数计算出所有提到的全名,它将把它们添加到一个列表中。然后,它将查询维基百科中的每个名字,以验证它是否是值得包含在结构化数据中的有影响力的人的名字。

def get_people(content_list):iteration=1

使用NER在文本中查找人名。

for i in range(len(content_list)):
print(‘Getting people’,iteration,’/’,len(content_list))
temp_list=[]
text=content_list[i]
doc=nlp(text)
persons=[X.text for X in doc.ents if X.label_ == ‘PERSON’]
persons_dict=dict.fromkeys(persons,0)
persons=list(persons_dict)

full_names=[]
for person in persons: if len(word_tokenize(person)) &gt;= 2:string_name=re.sub(r"[^a-zA-Z0-9]+", ' ', person).strip()full_names.append(string_name)final_names=[]
for person in persons:for name in full_names:tokens=word_tokenize(name)for n in range(len(tokens)):if person==tokens[n]:final_names.append(name)for name in full_names:final_names.append(name)name_dict=dict.fromkeys(final_names,0)
final_names=list(name_dict)
valid_names=[]for name in final_names:page=requests.get('https://en.wikipedia.org/wiki/'+name)if page.status_code==200:valid_names.append(name)people.append(valid_names)
iteration+=1<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li><li style="color: rgb(153, 153, 153);">23</li><li style="color: rgb(153, 153, 153);">24</li><li style="color: rgb(153, 153, 153);">25</li><li style="color: rgb(153, 153, 153);">26</li><li style="color: rgb(153, 153, 153);">27</li><li style="color: rgb(153, 153, 153);">28</li><li style="color: rgb(153, 153, 153);">29</li><li style="color: rgb(153, 153, 153);">30</li><li style="color: rgb(153, 153, 153);">31</li><li style="color: rgb(153, 153, 153);">32</li><li style="color: rgb(153, 153, 153);">33</li><li style="color: rgb(153, 153, 153);">34</li><li style="color: rgb(153, 153, 153);">35</li><li style="color: rgb(153, 153, 153);">36</li><li style="color: rgb(153, 153, 153);">37</li><li style="color: rgb(153, 153, 153);">38</li><li style="color: rgb(153, 153, 153);">39</li><li style="color: rgb(153, 153, 153);">40</li><li style="color: rgb(153, 153, 153);">41</li></ul></pre> 

关键词提取:TF-IDF

我们的下一个任务是从文本中提取关键字。最常见的方法是使用一种称为TF-IDF的方法。TF-IDF模型测量单个文档中单词的使用频率,然后将其与整个文档语料库中的平均使用率进行比较。

如果一个术语在单个文档中频繁使用,并且很少在整个文档语料库中使用,那么该术语很可能表示该特定文档特有的关键字。这篇文章并不是一篇关于TF-IDF模型的全面概述。要了解更多信息,请查看这篇关于Medium的文章:https://medium.com/datadriveninvestor/tf-idf-in-natural-language-processing-8db8ef4a7736

首先,我们的函数将创建通常所说的“词袋”。这将跟踪每个文档中使用的每个单词。然后,它将计算每个文档中每个单词的每次使用次数—单词频率(TF)。然后,它计算逆文档频率(IDF)。然后将这些值写入矩阵中的坐标,然后对矩阵进行排序,以帮助我们找到最有可能表示文档的单词。

# 第一个函数通过降低字符大小写和删除特殊字符对文本进行预处理。

def pre_process(text):
text=text.lower()
text=re.sub("</?.*?>"," <> “,text)
text=re.sub(”(\d|\W)+"," ",text)
return text

这个函数将矩阵映射到坐标。TF-IDF函数将频率分数映射到矩阵,然后需要对这些矩阵进行排序,以帮助我们找到关键字。

def sort_coo(coo_matrix):
tuples = zip(coo_matrix.col, coo_matrix.data)
return sorted(tuples, key=lambda x: (x[1], x[0]), reverse=True)

与上面一样,这是一个帮助函数,一旦频率映射到矩阵,它将帮助排序和选择关键字。

这个函数专门帮助我们根据TF-IDF统计数据选择最相关的关键字

def extract_topn_from_vector(feature_names, sorted_items, topn=10):
sorted_items = sorted_items[:topn]
score_vals = []
feature_vals = []

for idx, score in sorted_items:fname = feature_names[idx]score_vals.append(round(score, 3))feature_vals.append(feature_names[idx])results= {}
for idx in range(len(feature_vals)):results[feature_vals[idx]]=score_vals[idx]
return results

#最后一个函数包含了上述helper函数,它对正文应用TF-IDF算法,根据使用频率查找关键字。

def get_keywords(content_list):
iteration=1
processed_text=[pre_process(text) for text in content_list]
stop_words=set(stopwords.words(‘english’))
cv=CountVectorizer(max_df=0.85,stop_words=stop_words)
word_count_vector=cv.fit_transform(processed_text)

tfidf_transformer=TfidfTransformer(smooth_idf=True,use_idf=True)
tfidf_transformer.fit(word_count_vector)

feature_names=cv.get_feature_names()

for i in range(len(processed_text)):
print(‘Getting Keywords’,iteration,’/’,len(content_list))
doc=processed_text[i]
tf_idf_vector=tfidf_transformer.transform(cv.transform([doc]))
sorted_items=sort_coo(tf_idf_vector.tocoo())
keys=extract_topn_from_vector(feature_names,sorted_items,10)
keywords.append(list(keys.keys()))
iteration+=1

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

主题模型

NLP中最常见的任务之一就是主题模型。这是一种聚类形式,它尝试根据文档的文本内容自动对文档进行分类。在这个具体的例子中,我想一眼就知道ISW涉及哪些主题。通过根据文本内容对文档进行分类,我可以轻松地对文档的主要思想有一个大致的了解。

向量化

对于这个例子,我将使用k-means聚类算法来进行主题建模。首先,我将再次使用TF-IDF算法对每个文档进行向量化。向量化是一个机器学习术语,指的是将非数字数据转换成计算机可以用来执行机器学习任务的数字空间数据。

优化

一旦文档被向量化,helper函数就会检查簇的最佳数量。(k表示k-means的k)。在本例中,最佳数目是50。一旦我找到了最佳的数字,在这个例子中,我注释掉了这行代码,并手动将参数调整为等于50。这是因为我正在分析的数据集不会经常更改,所以我可以期望随着时间的推移,最佳簇的数量会保持不变。对于变化更频繁的数据,你应该返回最佳的簇数量作为变量-这将帮助你的聚类算法自动设置其最佳参数。我在我的时间序列分析文章中展示了一个例子。

聚类

每个簇完成后,我将每个簇的编号(1–50)保存到簇编号的列表中,而组成每个簇的关键字保存到cluster_keywords的列表中。这些簇关键字稍后将用于向每个主题簇添加标题。

# 该函数根据各种“k”参数检查聚类算法,以找到“k”的最优值。

def find_optimal_clusters(data, max_k):
iters = range(2, max_k+1, 2)

sse = []
for k in iters:sse.append(MiniBatchKMeans(n_clusters=k, init_size=1024, batch_size=2048,random_state=20).fit(data).inertia_)print('Fit {} clusters'.format(k))f, ax = plt.subplots(1, 1)
ax.plot(iters, sse, marker='o')
ax.set_xlabel('Cluster Centers')
ax.set_xticks(iters)
ax.set_xticklabels(iters)
ax.set_ylabel('SSE')
ax.set_title('SSE by Cluster Center Plot')

从内容列表中获取关键词来帮助对主题模型的分类

def get_top_keywords(data, clusters, labels, n_terms):
df = pd.DataFrame(data.todense()).groupby(clusters).mean()
for i,r in df.iterrows():
cluster_keywords.append(’,’.join([labels[t] for t in np.argsort®[-n_terms:]]))

应用于主题建模的内容列表

def get_topics(content_list):
processed_text=[pre_process(text) for text in content_list]
stop_words=set(stopwords.words(‘english’))
cv=CountVectorizer(max_df=0.85,stop_words=stop_words)
word_count_vector=cv.fit_transform(processed_text)

tfidf_transformer=TfidfTransformer(smooth_idf=True,use_idf=True)
tfidf_transformer.fit(word_count_vector)

feature_names=cv.get_feature_names()
vector=tfidf_transformer.transform(cv.transform(processed_text))

#find_optimal_clusters(vector,50)

clusters = MiniBatchKMeans(n_clusters=50, init_size=1024, batch_size=2048, random_state=20).fit_predict(vector)
for cluster in clusters:
cluster_number.append(int(cluster))

get_top_keywords(vector, clusters, cv.get_feature_names(), 20)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

放在一起

最后,我们将提取我们的数据。使用我们之前得到的href列表,现在是将所有提取函数应用于web内容的时候了。

# 遍历从“browse”中提取的href,提取相关内容

iteration=1

前几个函数依赖于原始提取的web内容作为参数。这些都是基本的web抓取技术。

for href in hrefs:
print(‘Web scraping: iteration’,iteration,’/’,len(hrefs))
page=requests.get(href)
soup=BeautifulSoup(page.text,‘html.parser’)
links.append(href)
get_date(soup)
get_title(soup)
get_contents(soup)
iteration+=1

下面这些函数依赖于文本主体作为参数。

这些是基于nlp的函数。

注意:由于查询外部API,

需要一个超时来阻止服务器过载。

这部分代码的运行时间很长。

get_countries(content_text)
get_coords(content_text)
get_people(content_text)
get_keywords(content_text)
get_topics(content_text)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

丰富主题模型

我们的下一个问题是:我们的簇为我们提供了一个与每个簇相关联的单词列表,但是簇的名称仅仅是数字。这使我们有机会绘制一个词云或其他有趣的可视化图,可以帮助我们理解每个簇,但对于结构化数据集中的一目了然的理解来说,它并没有那么有用。另外,我认为有些文档可能属于多个主题类别。k-means不支持多重聚类,因此我必须手动识别这些文档。首先,我将打印前几行关键字,以了解我正在处理的数据。

与每个主题相关联的一些关键字。我们将使用这些关键字将簇分类到预定义的类别中。

在对各种技术进行了大量实验之后,我决定采用一种非常简单的方法。我扫描了与每个簇相关的每个关键字列表,并在每个与特定主题相关的关键字中记录了重要的关键字。在这个阶段,领域知识是关键。例如,我知道,ISW文件中的阿勒颇几乎肯定提到了叙利亚内战。对于你的数据,如果你缺乏适当的领域知识,你可能需要做进一步的研究,咨询你团队中的其他人,或者定义一个更高级的编程方法来命名簇。

然而,对于这个例子,简单的方法很有效。在记录了簇列表中存在的几个重要关键字之后,我自己制作了几个列表,其中包含了与结构化数据中我想要的最终主题类别相关联的关键字。该函数简单地将每个簇的关键字列表与我创建的列表进行比较,然后根据列表中的匹配项分配主题名称。然后将这些最后的主题附加到主题类别列表中。

#搜索与主题对应的关键词列表,与聚类词库交叉引用,为每篇文章分配一个主题类别。

oir=[‘OIR Iraq’,‘yezidis’,‘mosul’,‘peshmerga’,‘isis’,‘iraq’,‘sinjar’,‘baghdad’,‘maliki’,
‘daquq’,‘anbar’,‘isf’,‘abadi’,‘malaki’,‘ramadi’,‘iraqi’,‘fallujah’,‘dabiq’]

terrorism=[‘Terrorism’,‘jihadi’,‘islamic’,‘salafi’,‘qaeda’,
‘caliphate’,‘isis’,‘terrorist’,‘terrorism’]

syrian_conflict=[‘Syrian Conflict’,‘sana’,‘syria’,‘assad’,
‘idlib’,‘afrin’,‘aleppo’]

russia=[‘Russia’,‘russia’,‘belarus’,‘slavic’,‘kremlin’,‘russian’,
‘minsk’,‘ukraine’,‘putin’]

iran=[‘Iran’,‘iran’,‘iranian’,‘proxy’,‘militias’,‘militia’,‘marjah’]

turkey=[‘Turkey’,‘erdogan’,‘turkish’,‘turkey’]

ors=[‘ORS’,‘kabul’,‘ghani’,‘pakistan’,‘afghan’,‘afghanistan’,
‘taliban’,‘ansf’,‘karzai’,‘helmand’]

africa=[‘Africa’,‘libya’,‘libyan’,‘egypt’,‘egyptian’,‘africa’,‘african’]

cat_list=[oir,terrorism,syrian_conflict,russia,iran,turkey,ors,africa]

topic_dict={}

for i in range(len(cluster_keywords)):
temp_list=[]
for n in nltk.word_tokenize(cluster_keywords[i]):
for item in cat_list:
if n in item:
temp_list.append(item[0])

temp_dict=dict.fromkeys(temp_list,0)
temp_list=list(temp_dict)

topic_dict[i] = temp_list

for num in cluster_number:
topic_categories.append(topic_dict[num])

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

数据库创建

最后一步是将我们提取的所有数据集中起来。对于这些数据,我更喜欢JSON格式。这是因为我想以不同的方式组织某些类型的数据—例如,locations字段将包含地名、纬度和经度的字典列表。在我看来,JSON格式是将这种格式化的数据存储到本地磁盘的最有效的方法。我还在文档数据库MongoDB中备份了这个数据库的副本,但这不是本文的重点。

#将一个空列表初始化

db=[]

for i in range(len(hrefs)):
countries={
‘focus area’: key_countries[i],
‘all mentioned countries’: mentioned_countries[i]
}

将函数中定义的所有列表添加到新的存储列表中

doc={
‘_id’: len(hrefs) - i,
‘title’: titles[i],
‘date’: dates[i],
‘places’: coord_list[i],
‘people’: people[i],
‘keywords’: keywords[i],
‘countries’: countries,
‘full text’: content_text[i],
‘url’: links[i],
‘topic cluster’: cluster_number[i],
‘categories’: topic_categories[i]
}

db.append(doc)

将列表保存为谷歌驱动器内的.JSON数据存储文件(用于演示目的)

with open (’/content/drive/My Drive/Colab Notebooks/isw_products.json’, ‘w’) as fout:
json.dump(db, fout)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

摘要

现在我们结束了!我们从网页中提取链接,然后使用这些链接从网站中提取更多内容。我们使用这些内容,然后使用外部api、ML簇算法和NLP来提取和增强这些信息。TF-IDF向量化、关键字提取和主题模型,这些是NLP的基石。如果你有更多问题或需要信息,请联系我们,祝你在未来的NLP中好运!

原文链接:https://towardsdatascience.com/something-from-nothing-use-nlp-and-ml-to-extract-and-structure-web-data-3f49b2f72b13

欢迎关注磐创AI博客站: http://panchuang.net/

sklearn机器学习中文官方文档: http://sklearn123.com/

欢迎关注磐创博客资源汇总站: http://docs.panchuang.net/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/479163.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何评价一个推荐系统的好坏?

文 | Nemo知乎本文已获作者授权&#xff0c;禁止二次转载现如今&#xff0c;推荐系统几乎无处不在。电商购物&#xff0c;有猜你喜欢。资讯阅读&#xff0c;有个性推荐。听歌看电影&#xff0c;都能识别你的兴趣。就连工作社交&#xff0c;也会提示你可能认识的人...推荐系统火…

论文浅尝 | GEOM-GCN: Geometric Graph Convolutional Networks

论文笔记整理&#xff1a;毕祯&#xff0c;浙江大学硕士&#xff0c;研究方向&#xff1a;知识图谱、自然语言处理。动机消息传递神经网络&#xff08;MPNN&#xff09;已成功应用于现实世界中的各种应用中。但是MPNN聚合器的两个基本弱点限制了它们表示图结构数据的能力&#…

ScrollView嵌套EditText联带滑动的解决办法

本篇文章的相关内容需结合上文&#xff1a;从ScrollView嵌套EditText的滑动事件冲突分析触摸事件的分发机制以及TextView的简要实现和冲突的解决办法 在说完了如何解决ScrollView嵌套EditText的滑动事件冲突之后&#xff0c;我们接下来说一下如何实现它们两者之间的联带滑动。什…

数据库智能运维探索与实践

从自动化到智能化运维过渡时&#xff0c;美团DBA团队进行了哪些思考、探索与实践&#xff1f;本文根据赵应钢在“第九届中国数据库技术大会”上的演讲内容整理而成&#xff0c;部分内容有更新。 背景 近些年&#xff0c;传统的数据库运维方式已经越来越难于满足业务方对数据库的…

论文浅尝 | PAKDD2020 - 利用支持集中匹配信息的 few shot 事件分类方法

论文笔记整理&#xff1a;申时荣&#xff0c;东南大学博士生。来源&#xff1a;PAKDD 2020链接&#xff1a;https://arxiv.xilesou.top/pdf/2002.05295.pdf1.介绍&#xff1a;事件分类是一个重要的信息抽取任务&#xff0c;其目的是根据事件的提及实例对事件类别进行分类。目前…

梯度下降法的神经网络容易收敛到局部最优,为什么应用广泛?

文 | Summer Clover知乎本文已获作者授权&#xff0c;禁止二次转载这是Deep Learning Theory里很基本也很核心的一个问题。在这个问题上&#xff0c;初学者容易被入门教学误导&#xff0c;非此研究方向的业内人士也容易有过时的认知。首先问题描述不够准确。更准确的说法是&…

机器学习在美团配送系统的实践:用技术还原真实世界

在2018 AI开发者大会&#xff08;AI NEXTCon&#xff09;上&#xff0c;美团配送AI方向负责人何仁清&#xff0c;分享了美团在即时配送领域中机器学习技术的最新进展&#xff0c;以及如何通过大数据和机器学习手段&#xff0c;建立对线下真实世界各种场景的感知能力&#xff0c…

LeetCode 99. 恢复二叉搜索树(中序遍历)

1. 题目 二叉搜索树中的两个节点被错误地交换。 请在不改变其结构的情况下&#xff0c;恢复这棵树。 你能想出一个只使用常数空间的解决方案吗&#xff1f; 2. 解题 循环中序遍历&#xff08;栈&#xff09;&#xff0c;记录不满足的节点&#xff0c;交换其valO(n)O(n)O(n)…

AAAI21 | Seq2Seq模型成为“复读机”的原因找到了?

文 | 苏剑林编 | Sheryc_王苏单位 | 追一科技去年笔者写过博文《如何应对Seq2Seq中的"根本停不下来"问题&#xff1f;》[1]&#xff0c;里边介绍了一篇论文中对Seq2Seq解码不停止现象的处理&#xff0c;并指出那篇论文只是提了一些应对该问题的策略&#xff0c;并没有…

论文浅尝 | ICLR 2020 - 图神经网络的预训练策略

论文笔记整理&#xff1a;杨帆&#xff0c;浙江大学计算机学院。动机现有的用于图结构的预训练方法要么只关注node-level&#xff0c;导致在图表示空间没有区分度&#xff0c;要么只关注graph-level&#xff0c;导致在节点表示空间没有区分度。一种优质的节点表示应该保证不仅在…

常见的距离算法和相似度计算方法

原文链接&#xff1a;https://zhuanlan.zhihu.com/p/138107999 首发于算法加油站写文章常见的距离算法和相似度计算方法奋发的菜鸟酱​华东师范大学 计算机技术博士在读91 人赞同了该文章注&#xff1a;不定时更新1.常见的距离算法1.1 欧几里得距离&#xff08;Euclidean Dista…

美团AI全景图:吃喝玩乐背后的黑科技

很多人都会发现日常生活已经越来越离不开美团了&#xff0c;这个互联网平台涵盖了吃、住、行、游、购、娱……能帮我们做很多事情&#xff0c;非常接地气。黄色的美团外卖骑手&#xff0c;橙色的摩拜单车&#xff0c;还有美团和大众点评的Logo……会不时出现在各个角落&#xf…

LeetCode 547. 朋友圈(图的遍历BFS DFS)

文章目录1. 题目2. 解题2.1 BFS 广度优先2.2 DFS 深度优先1. 题目 问有几个连通网络 2. 解题 2.1 BFS 广度优先 参考图的数据结构 class Solution { public:int findCircleNum(vector<vector<int>>& M) {int n M.size(), groups 0, i;bool visited[n] …

开源开放 | 《大词林》开源 75 万核心实体和围绕核心实体的细粒度概念、关系列表...

1《大词林》简介《大词林》(http://101.200.120.155/)是由哈尔滨工业大学社会计算与信息检索研究中心推出&#xff0c;由我中心秦兵教授和刘铭副教授主持开发&#xff0c;是一个自动构建的大规模开放域中文知识库。自2014年11月推出第一版《大词林》&#xff0c;《大词林》共经…

推荐几个不错的CUDA入门教程(非广告)

文 | godweiyang最近因为项目需要&#xff0c;入坑了CUDA&#xff0c;又要开始写很久没碰的C了。对于CUDA编程以及它所需要的GPU、计算机组成、操作系统等基础知识&#xff0c;我基本上都忘光了&#xff0c;因此也翻了不少教程。这里简单整理一下&#xff0c;给同样有入门需求的…

logging.getLogger(logger)

https://www.cnblogs.com/bjdxy/archive/2012/12/03/2799322.html logging模块学习笔记&#xff1a;logger 对象、日志等级 logger&#xff1a;日志对象&#xff0c;logging模块中最基础的对象&#xff0c;用logging.getLogger(name)方法进行初始化&#xff0c;name可以不填。通…

美团DB数据同步到数据仓库的架构与实践

背景 在数据仓库建模中&#xff0c;未经任何加工处理的原始业务层数据&#xff0c;我们称之为ODS(Operational Data Store)数据。在互联网企业中&#xff0c;常见的ODS数据有业务日志数据&#xff08;Log&#xff09;和业务DB数据&#xff08;DB&#xff09;两类。对于业务DB数…

论文浅尝 | AAAI2020 - 基于生成对抗的知识图谱零样本关系学习

论文笔记整理&#xff1a;耿玉霞&#xff0c;浙江大学直博生。研究方向&#xff1a;知识图谱&#xff0c;零样本学习等。来源&#xff1a;AAAI2020论文链接&#xff1a;https://arxiv.org/pdf/2001.02332.pdf本文是发表在AAAI2020上的一篇基于生成对抗网络进行知识图谱零样本关…

LeetCode 1184. 公交站间的距离

1. 题目 环形公交路线上有 n 个站&#xff0c;按次序从 0 到 n - 1 进行编号。我们已知每一对相邻公交站之间的距离&#xff0c;distance[i] 表示编号为 i 的车站和编号为 (i 1) % n 的车站之间的距离。 环线上的公交车都可以按顺时针和逆时针的方向行驶。 返回乘客从出发点…

追剧计划第三弹!UC Berkeley出品,全栈深度学习!

关注卖萌屋比较早的小伙伴&#xff0c;大概还记得2020年初时我们组织的斯坦福大学CS224N自然语言处理公开课追剧计划&#xff0c;以及后来的斯坦福大学CS520知识图谱公开课追剧活动。尽管活动已经结束很长一段时间&#xff0c;但是仍然有小伙伴后台问“什么时候开始下一波追剧哇…