zj3D 6 days ago
parent 7365ebb312
commit 8b9e813ee2

4
.gitignore vendored

@ -2,3 +2,7 @@ log.txt
/test
/.venv
__pycache__
B 高性能模式/log
B 高性能模式/test.py
.vscode/launch.json
.gitignore

@ -48,3 +48,9 @@ for i in range(n):
# 打印频率最高的前10个词
for tf in word_freqs[:10]:
print(tf[0], '-', tf[1])
'''
想到哪里写到哪里
用的最基础的编程思想没有使用 Python 高级语法特性数据结构和算法
'''

@ -23,6 +23,7 @@ with open(testfilepath, encoding='utf8') as f:
# 打印前10个最常见的单词
for word, freq in word_freqs.most_common(10):
print(f"{word}-{freq}")
'''
相比 A01
使用collections.Counter来计数单词频率从而简化了代码并提高了效率

@ -8,6 +8,7 @@ words = re.findall('[a-z]{2,}',
counts = collections.Counter(w for w in words if w not in stopwords)
for (w, c) in counts.most_common(10):
print(w, '-', c)
'''
熟练的软件工程师会如此简单完成任务
后面的例子我们必须变的啰嗦一些不能用这种太 hacker 的写法

@ -1,19 +1,12 @@
"""
根据提供的关键词列表爬取天水市人民政府网站上指定日期内与关键词相关的新闻的标题并将其存储至数据库中
考虑到相关因素因此本代码只爬取前10页的新闻内容即最多100条新闻作为测试
此方法为普通做法即使用requests库通过Post请求爬取网页内容再使用json提取新闻内容
注意本代码中的关键词列表默认为['灾害']日期范围默认为2018年1月1日至2018年12月31日
Args:
此方法为普通做法即使用requests库通过Post请求爬取网页内容再使用json提取新闻内容
注意本代码中的关键词列表默认为['灾害']日期范围默认为2018年1月1日至2018年12月31日
Args:
keywords: 用于搜索新闻的关键词列表
begin_date: 开始日期用于搜索
end_date: 结束日期用于搜索
size: 一次请求返回的新闻或政策的最大数量
Examples:
Examples:
```
main(keywords=['灾害'],
begin_date='2018-01-01',
@ -47,7 +40,7 @@ def main(keywords: List[str], begin_date: str, end_date: str, size: int = 10):
logging.info("开始运行普通爬取")
spider = util.Spider(keywords=keywords,
spider = util.Spider( keywords=keywords,
begin_date=begin_date,
end_date=end_date,
size=size)
@ -68,7 +61,7 @@ def main(keywords: List[str], begin_date: str, end_date: str, size: int = 10):
if __name__ == "__main__":
main(keywords=['灾害'],
main(keywords=['经济'],
begin_date='2018-01-01',
end_date='2018-12-31',
size=10)

@ -0,0 +1,134 @@
### 多线程与异步编程
对于 **I/O 密集型任务****异步编程** 通常比 **多线程** 是更好的选择。异步编程特别适合高并发的 I/O 密集型任务(如 Web 服务器、爬虫、实时通信), 特别是大量并发连接的任务。
多线程比相对编程简单 。
### 场景:**GUI 应用程序**
在 GUI图形用户界面应用程序中主线程负责处理用户交互而其他任务如文件读写、网络请求需要在后台运行以避免阻塞主线程导致界面卡顿。多线程可以与 GUI 主线程共享内存方便更新界面状态。线程间通信简单适合处理后台任务。GUI 框架(如 PyQt、Tkinter通常有自己的事件循环与异步编程的事件循环冲突。
<python code>
import sys
import requests
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel
from PyQt5.QtCore import QThread, pyqtSignal
# 工作线程:负责下载文件
class DownloadThread(QThread):
# 自定义信号,用于通知主线程下载进度
progress_signal = pyqtSignal(str)
def __init__(self, url):
super().__init__()
self.url = url
def run(self):
self.progress_signal.emit("开始下载...")
try:
response = requests.get(self.url, stream=True)
total_size = int(response.headers.get("content-length", 0))
downloaded_size = 0
with open("downloaded_file", "wb") as file:
for chunk in response.iter_content(chunk_size=1024):
file.write(chunk)
downloaded_size += len(chunk)
progress = f"已下载: {downloaded_size / 1024:.2f} KB / {total_size / 1024:.2f} KB"
self.progress_signal.emit(progress)
self.progress_signal.emit("下载完成!")
except Exception as e:
self.progress_signal.emit(f"下载失败: {str(e)}")
# 主窗口
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle("多线程下载示例")
self.setGeometry(100, 100, 300, 150)
# 布局
layout = QVBoxLayout()
# 下载按钮
self.download_button = QPushButton("开始下载", self)
self.download_button.clicked.connect(self.start_download)
layout.addWidget(self.download_button)
# 状态标签
self.status_label = QLabel("点击按钮开始下载", self)
layout.addWidget(self.status_label)
self.setLayout(layout)
def start_download(self):
# 禁用按钮,防止重复点击
self.download_button.setEnabled(False)
self.status_label.setText("准备下载...")
# 创建工作线程
self.download_thread = DownloadThread("https://example.com/large_file.zip")
self.download_thread.progress_signal.connect(self.update_status)
self.download_thread.finished.connect(self.on_download_finished)
self.download_thread.start()
def update_status(self, message):
# 更新状态标签
self.status_label.setText(message)
def on_download_finished(self):
# 下载完成后启用按钮
self.download_button.setEnabled(True)
# 运行应用程序
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
</python code>
以下场景更适合使用 **多线程**
-
1. **与阻塞式 API 交互**。 某些库或 API 是阻塞式的(如某些数据库驱动、硬件接口库),无法直接使用异步编程。在这种情况下,多线程可以避免阻塞主线程。
<code>
import threading
import time
import sqlite3
def query_database():
# 模拟阻塞式数据库查询
conn = sqlite3.connect("example.db")
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
results = cursor.fetchall()
print("查询完成,结果:", results)
conn.close()
def main():
print("主线程开始")
# 创建线程执行数据库查询
thread = threading.Thread(target=query_database)
thread.start()
# 主线程继续执行其他任务
for i in range(5):
print(f"主线程运行中... {i}")
time.sleep(1)
thread.join()
print("主线程结束")
main()
</code>
1. **实时数据处理**。 在实时数据处理场景中(如音频处理、视频流处理),需要快速响应并处理数据,同时保持主线程的响应性。
2. **任务队列与线程池**。在需要处理大量短期任务的场景中(如 Web 服务器的请求处理),使用线程池可以高效地管理任务。
3. **与 C/C++ 扩展交互**。 某些 Python 库是基于 C/C++ 扩展实现的(如 `numpy`、`pandas`),这些扩展可能释放了 GIL允许在多线程中并行运行。
4. **需要共享状态的场景**。 在某些场景中,多个任务需要频繁共享和修改状态(如缓存、计数器),使用多线程可以方便地共享内存。

@ -7,6 +7,9 @@
# 任务
根据提供的关键词列表,爬取天水市人民政府网站上指定日期内与关键词相关的新闻的标题,并将其存储至数据库中。
本代码只爬取前10页的新闻内容即最多100条新闻作为测试。
# 讨论分析
普通做法连续进行了五次测试时间分别为34.231s、34.091s、34.164s、34.226s、33.958s平均时间为34.134s

@ -1,6 +1,3 @@
"""
"""
import re
import time
import functools
@ -12,7 +9,7 @@ from typing import Any, Dict, List
class Spider:
"""
爬虫类
爬虫类
Args:
keywords (List[str]): 用于搜索新闻的关键词列表
@ -79,8 +76,7 @@ class Spider:
def generate_headers(self) -> dict:
"""
生成请求头
生成请求头
Returns:
dict: 请求头
"""
@ -110,6 +106,7 @@ class Spider:
time.sleep(3)
return json.loads(response)
async def fetch_async(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""
异步做法
@ -127,6 +124,7 @@ class Spider:
await asyncio.sleep(3)
return json.loads(response)
def parse(self, data: Dict[str, Any]) -> List[str]:
"""
解析网页内容
@ -146,6 +144,7 @@ class Spider:
# print(title)
return title_list
def save(self, title_list: List[str]):
"""
保存数据
@ -156,11 +155,9 @@ class Spider:
# 时间装饰器
def timeit(func):
"""
计算函数运行时间
计算函数运行时间
Args:
func: 函数
Return:
函数
"""

Loading…
Cancel
Save