数据搜索、挖掘、可视化与简单数据 API 创建
数据爬取与图可视化
-
单深度爬取
- 首先定义单深度爬取,代码如下:
crawl_depth = 1
process = CrawlerProcess({
'LOG_LEVEL': 'ERROR',
'DEPTH_LIMIT': crawl_depth
})
process.crawl(WikipediaSpider)
spider = next(iter(process.crawlers)).spider
spider.max_items_per_page = 5
spider.max_crawl_depth = crawl_depth
process.start()
for pm in spider.linked_pages:
print(pm.depth, pm.link, pm.child_link)
print("-"*80)
- 此代码启动了一个单深度的爬取过程,爬取完成后会打印出页面的深度、链接和子链接信息。
-
构建 NetworkX 图模型
- 为了将爬取的数据转换为 NetworkX 可以使用的图模型,需要创建图并添加节点和边,代码如下:
g = nx.Graph()
nodes = {}
edges = {}
for pm in spider.linked_pages:
if pm.title not in nodes:
nodes[pm.title] = pm
g.add_node(pm.title)
if pm.child_title not in nodes:
g.add_node(pm.child_title)
link_key = pm.title + " ==> " + pm.child_title
if link_key not in edges:
edges[link_key] = link_key
g.add_edge(pm.title, pm.child_title)
- 该代码遍历爬取结果,识别出所有唯一的节点(页面)和它们之间的链接(边),并将其添加到 NetworkX 图中。
-
使用 Matplotlib 绘图
- 接下来使用 Matplotlib 绘制图,代码如下:
plt.figure(figsize=(10,8))
node_positions = nx.spring_layout(g)
nx.draw_networkx_nodes(g, node_positions, g.nodes, node_color='green', node_size=50)
nx.draw_networkx_edges(g, node_positions)
labels = { node: node for node in g.nodes() }
nx.draw_networkx_labels(g, node_positions, labels, font_size=9.5)
plt.show()
- 此代码首先使用 NetworkX 的 `spring_layout` 方法计算节点的位置,然后绘制节点、边和标签,最后显示图形。
增加爬取深度
可以通过修改代码来增加爬取深度,示例代码如下:
crawl_depth = 2
process = CrawlerProcess({
'LOG_LEVEL': 'ERROR',
'DEPTH_LIMIT': crawl_depth
})
process.crawl(WikipediaSpider)
spider = next(iter(process.crawlers)).spider
spider.max_items_per_page = 5
spider.max_crawl_depth = crawl_depth
process.start()
增加爬取深度后,可以看到页面之间更多的相互关系和循环关系。
计算页面间的分离度
-
问题描述
- 计算任意两个页面之间的分离度,即从一个源页面到另一个页面需要经过多少个页面。这是一个图遍历问题,因为两个页面之间可能存在多条路径。
-
使用 A* 算法解决
- NetworkX 提供了内置函数来解决这个问题,示例代码如下:
path = nx.astar_path(g, "Python_(programming_language)", "Dennis_Ritchie")
degrees_of_separation = int((len(path) - 1) / 2)
print("Degrees of separation: {}".format(degrees_of_separation))
for i in range(0, len(path)):
print(" " * i, path[i])
- 该代码使用 `astar_path` 函数计算最短路径,然后根据路径长度计算分离度并打印路径信息。
创建简单 REST API
-
准备工作
- Flask 是一个轻量级的 Web 框架,Flask - RESTful 是 Flask 的扩展,用于创建 REST API。可以使用以下命令安装:
pip install flask
pip install flask-restful
-
创建初始 API
- 初始 API 实现代码如下:
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class JobListing(Resource):
def get(self, job_listing_id):
print("Request for job listing with id: " + job_listing_id)
return {'YouRequestedJobWithId': job_listing_id}
api.add_resource(JobListing, '/', '/joblisting/<string:job_listing_id>')
if __name__ == '__main__':
print("Starting the job listing API")
app.run(debug=True)
- 此代码创建了一个简单的 REST API,当接收到 `/joblisting/<job_listing_id>` 的 GET 请求时,会返回包含请求 ID 的 JSON 数据。
-
运行和测试 API
- 可以使用以下命令运行 API:
python api.py
- 然后使用 `curl` 命令测试 API:
curl localhost:5000/joblisting/1
- 测试结果会返回类似 `{"YouRequestedJobWithId": "1"}` 的 JSON 数据。
集成 REST API 与刮取代码
-
准备工作
-
需要将之前用于刮取 StackOverflow 职位列表的代码封装成一个模块
sojobs,其中scraping.py文件中的get_job_listing_info函数用于获取职位列表信息,代码如下:
-
需要将之前用于刮取 StackOverflow 职位列表的代码封装成一个模块
def get_job_listing(job_listing_id):
print("Got a request for a job listing with id: " + job_listing_id)
req = requests.get("https://stackoverflow.com/jobs/" + job_listing_id)
content = req.text
bs = BeautifulSoup(content, "lxml")
script_tag = bs.find("script", {"type": "application/ld+json"})
job_listing_contents = json.loads(script_tag.contents[0])
desc_bs = BeautifulSoup(job_listing_contents["description"], "lxml")
just_text = desc_bs.find_all(text=True)
joined = ' '.join(just_text)
tokens = word_tokenize(joined)
stop_list = stopwords.words('english')
with_no_stops = [word for word in tokens if word.lower() not in stop_list]
two_grammed = tech_2grams(with_no_stops)
cleaned = remove_punctuation(two_grammed)
result = {
"ID": job_listing_id,
"JSON": job_listing_contents,
"TextOnly": just_text,
"CleanedWords": cleaned
}
return json.dumps(result)
-
集成 API 与刮取代码
-
修改 API 代码以调用
get_job_listing_info函数,代码如下:
-
修改 API 代码以调用
from flask import Flask
from flask_restful import Resource, Api
from sojobs.scraping import get_job_listing_info
app = Flask(__name__)
api = Api(app)
class JobListing(Resource):
def get(self, job_listing_id):
print("Request for job listing with id: " + job_listing_id)
listing = get_job_listing_info(job_listing_id)
print("Got the following listing as a response: " + listing)
return listing
api.add_resource(JobListing, '/', '/joblisting/<string:job_listing_id>')
if __name__ == '__main__':
print("Starting the job listing API")
app.run(debug=True)
-
测试集成后的 API
-
运行 API 后,使用
curl命令测试:
-
运行 API 后,使用
curl localhost:5000/joblisting/122517
- 测试结果会返回包含职位列表详细信息的 JSON 数据。
添加 API 以查找职位列表的技能
-
添加刮取函数
-
在
sojobs模块中添加get_job_listing_skills函数,代码如下:
-
在
def get_job_listing_skills(job_listing_id):
print("Got a request for a job listing skills with id: " + job_listing_id)
req = requests.get("https://stackoverflow.com/jobs/" + job_listing_id)
content = req.text
bs = BeautifulSoup(content, "lxml")
script_tag = bs.find("script", {"type": "application/ld+json"})
job_listing_contents = json.loads(script_tag.contents[0])
skills = job_listing_contents['skills']
return json.dumps(skills)
- 该函数用于获取职位列表的技能信息。
-
修改 API 以调用新函数
-
在 API 代码中添加新的类
JobListingSkills,代码如下:
-
在 API 代码中添加新的类
class JobListingSkills(Resource):
def get(self, job_listing_id):
print("Request for job listing's skills with id: " + job_listing_id)
skills = get_job_listing_skills(job_listing_id)
print("Got the following skills as a response: " + skills)
return skills
api.add_resource(JobListingSkills, '/', '/joblisting/<string:job_listing_id>/skills')
- 此代码定义了一个新的 API 端点,用于获取职位列表的技能信息。
总结
本文介绍了数据爬取、图可视化、计算页面分离度以及创建和扩展简单 REST API 的相关知识和代码实现。通过这些技术,可以更好地处理和分析网页数据,并将数据以 API 的形式提供给其他应用程序使用。
流程图
graph LR
A[开始单深度爬取] --> B[构建 NetworkX 图模型]
B --> C[使用 Matplotlib 绘图]
A --> D[增加爬取深度]
D --> B
B --> E[计算页面分离度]
E --> F[创建简单 REST API]
F --> G[集成 REST API 与刮取代码]
G --> H[添加 API 以查找职位列表的技能]
表格
| 功能 | 代码文件 | 关键函数 |
|---|---|---|
| 单深度爬取 | - | - |
| 构建图模型 | - | - |
| 绘图 | - | - |
| 计算分离度 | 08/07_degrees_of_separation.py | nx.astar_path |
| 创建初始 API | 09/01/api.py | JobListing.get |
| 集成刮取代码 | 09/02/api.py | get_job_listing_info |
| 查找职位技能 | 09/03/api.py | get_job_listing_skills |
数据搜索、挖掘、可视化与简单数据 API 创建
数据存储与缓存:使用 Elasticsearch
在处理大量数据时,数据的存储和缓存至关重要。Elasticsearch 是一个强大的开源搜索和分析引擎,可以用于存储和快速检索数据。下面介绍如何将爬取的数据存储到 Elasticsearch 中,并在需要时进行缓存查询。
-
存储数据到 Elasticsearch
-
首先,确保已经安装并启动了 Elasticsearch。然后,在 Python 中使用
elasticsearch库来连接和操作 Elasticsearch。以下是一个简单的示例代码,将爬取的职位列表数据存储到 Elasticsearch 中:
-
首先,确保已经安装并启动了 Elasticsearch。然后,在 Python 中使用
from elasticsearch import Elasticsearch
# 连接到 Elasticsearch
es = Elasticsearch([{'host': 'localhost', 'port': 9200}])
def store_job_listing_to_es(job_listing_id, job_listing_data):
try:
# 索引名称
index_name = 'job_listings'
doc = {
'id': job_listing_id,
'data': job_listing_data
}
# 存储数据到 Elasticsearch
es.index(index=index_name, id=job_listing_id, body=doc)
print(f"Job listing {job_listing_id} stored in Elasticsearch.")
except Exception as e:
print(f"Error storing job listing {job_listing_id} in Elasticsearch: {e}")
- 在调用 `get_job_listing_info` 函数获取职位列表数据后,可以调用 `store_job_listing_to_es` 函数将数据存储到 Elasticsearch 中。
-
检查 Elasticsearch 缓存
- 在进行新的爬取之前,可以先检查 Elasticsearch 中是否已经存在所需的数据。以下是一个检查缓存的示例代码:
def check_es_cache(job_listing_id):
try:
# 索引名称
index_name = 'job_listings'
# 检查数据是否存在
result = es.get(index=index_name, id=job_listing_id, ignore=[404])
if result.get('found'):
print(f"Job listing {job_listing_id} found in Elasticsearch cache.")
return result['_source']['data']
else:
print(f"Job listing {job_listing_id} not found in Elasticsearch cache.")
return None
except Exception as e:
print(f"Error checking Elasticsearch cache for job listing {job_listing_id}: {e}")
return None
- 在调用 `get_job_listing_info` 函数之前,可以先调用 `check_es_cache` 函数检查缓存。如果数据存在,则直接使用缓存数据;否则,进行新的爬取并存储到 Elasticsearch 中。
优化 API 性能
为了提高 API 的性能和响应速度,可以采取以下几种优化措施:
-
缓存机制
-
除了使用 Elasticsearch 进行数据缓存外,还可以在 API 层实现简单的内存缓存。例如,使用 Python 的
functools.lru_cache装饰器来缓存函数的返回值:
-
除了使用 Elasticsearch 进行数据缓存外,还可以在 API 层实现简单的内存缓存。例如,使用 Python 的
import functools
@functools.lru_cache(maxsize=128)
def get_job_listing_info_cached(job_listing_id):
return get_job_listing_info(job_listing_id)
- 然后在 API 代码中调用 `get_job_listing_info_cached` 函数代替 `get_job_listing_info` 函数。
-
异步处理
-
对于耗时的操作,如爬取数据和存储到 Elasticsearch,可以使用异步编程来提高并发性能。Python 的
asyncio和aiohttp库可以用于实现异步爬取和存储。以下是一个简单的异步爬取示例:
-
对于耗时的操作,如爬取数据和存储到 Elasticsearch,可以使用异步编程来提高并发性能。Python 的
import asyncio
import aiohttp
async def async_get_job_listing(job_listing_id):
async with aiohttp.ClientSession() as session:
url = f"https://stackoverflow.com/jobs/{job_listing_id}"
async with session.get(url) as response:
content = await response.text()
# 后续处理代码...
return content
扩展 API 功能
除了现有的功能,还可以进一步扩展 API 的功能,以满足更多的需求。
-
批量查询
- 可以添加一个新的 API 端点,支持批量查询职位列表信息。以下是一个简单的实现示例:
class JobListingsBatch(Resource):
def get(self, job_listing_ids):
ids = job_listing_ids.split(',')
results = []
for id in ids:
listing = get_job_listing_info(id)
results.append(listing)
return results
api.add_resource(JobListingsBatch, '/', '/joblistings/<string:job_listing_ids>')
- 调用该 API 时,可以传入多个职位列表 ID,用逗号分隔,如 `curl localhost:5000/joblistings/1,2,3`。
-
排序和过滤
- 可以添加排序和过滤功能,让用户可以根据不同的条件对职位列表进行排序和过滤。例如,添加一个新的 API 端点,支持按技能进行过滤:
class JobListingsFilterBySkills(Resource):
def get(self, skills):
skill_list = skills.split(',')
all_listings = []
# 获取所有职位列表
# ...
filtered_listings = []
for listing in all_listings:
listing_skills = get_job_listing_skills(listing['id'])
if any(skill in listing_skills for skill in skill_list):
filtered_listings.append(listing)
return filtered_listings
api.add_resource(JobListingsFilterBySkills, '/', '/joblistings/filter/skills/<string:skills>')
流程图
graph LR
A[开始] --> B[检查 Elasticsearch 缓存]
B -- 数据存在 --> C[使用缓存数据]
B -- 数据不存在 --> D[爬取数据]
D --> E[存储数据到 Elasticsearch]
E --> F[返回数据]
C --> F
G[API 请求] --> H[缓存机制]
H --> I[异步处理]
I --> J[批量查询/排序过滤]
J --> F
表格
| 功能 | 代码实现 | 关键函数 |
|---|---|---|
| 存储数据到 Elasticsearch | - | store_job_listing_to_es |
| 检查 Elasticsearch 缓存 | - | check_es_cache |
| 内存缓存 | - | functools.lru_cache |
| 异步爬取 | - | async_get_job_listing |
| 批量查询 | 新 API 端点 | JobListingsBatch.get |
| 排序和过滤 | 新 API 端点 | JobListingsFilterBySkills.get |
总结
本文详细介绍了数据爬取、图可视化、计算页面分离度、创建和扩展简单 REST API 的相关知识和代码实现。同时,还介绍了如何使用 Elasticsearch 进行数据存储和缓存,以及如何优化 API 性能和扩展 API 功能。通过这些技术和方法,可以更好地处理和分析网页数据,并将数据以灵活、高效的 API 形式提供给其他应用程序使用。希望这些内容对大家在数据处理和 API 开发方面有所帮助。
超级会员免费看

3311

被折叠的 条评论
为什么被折叠?



