|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
大佬们,我在使用线程池时出现了点问题。其实函数整体分为module_1, module_2两个部分。module1是进行广度遍历并爬取,module2是进行网页提取内容并且去除内容相似度高的网页。我这里使用了多线程,但是速度特别慢。虽然可能代码有点多,但是整体主干就那么点。大家有想法的话,请给我提一下哈,谢谢!
import json
import re
import threading
import time
from concurrent.futures import ThreadPoolExecutor
from queue import Queue
from urllib.parse import urljoin
import jieba
import requests
from bs4 import BeautifulSoup
from simhash import Simhash
from spellchecker import SpellChecker
lock = threading.Lock()
queue = Queue()
def load_stopwords(file_path):
with open(file_path, 'r', encoding='utf-8-sig') as file:
stop_words = [line.strip() for line in file]
return stop_words
stop_words = load_stopwords('A:/搜索引擎系统/停用词表.txt')
def to_text_html(num, url, html):
# with lock:
try:
html = html.replace("\n", "")
with open(f'A:/搜索引擎系统/all_information/web{num}.txt', 'w', encoding='utf-8-sig') as file:
s = str(num) + '\n' + url + '\n' + html
file.write(s)
except requests.exceptions.RequestException as e:
# 其他请求异常,打印错误信息
print(f"请求 {url} 出现错误: {e}")
return
def to_text_KeyInfor(num, current_url, title, content, links):
# with lock:
data = {
"id": num,
"url": current_url,
"title": title,
"content": content,
"links": links
}
with open(f'A:/搜索引擎系统/get_title_content_links/web{num}.txt', 'w', encoding='utf-8-sig') as file:
json.dump(data, file, ensure_ascii=False)
# 获取单个网页所有超链接
def get_all_hrefs(text: str, current_url):
all_href = []
text.encode('utf-8-sig')
# 使用BeautifulSoup来解析网页源码
soup = BeautifulSoup(text, 'lxml')
# 获取所有a标签下的所有href属性
all_a_tags = soup.find_all('a')
for a_tag in all_a_tags:
href = a_tag.get('href')
if href and not href.startswith(('#', 'javascript:')) and not href.endswith(('.pdf', '.mp4', '.apk')):
# 将爬取到的超链接由相对路径改为绝对路径
absolute_url = urljoin(current_url, href)
all_href.append(absolute_url)
# 将得到的链接返回
return all_href
def calculate_hamming_similarity(hash1, hash2):
similarity = 1 - bin(hash1 ^ hash2).count('1') / 64
return similarity
def compare_hash(content):
new_hash = Simhash(content).value
if len(processed_hashes) == 0:
processed_hashes.append(new_hash)
return True
for pro_hash in processed_hashes:
similarity = calculate_hamming_similarity(pro_hash, new_hash)
if similarity > 0.7:
return False
processed_hashes.append(new_hash)
return True
def is_chinese(word):
for char in word:
if '\u4e00' <= char <= '\u9fff': # 中文字符的Unicode范围
return True
return False
def remove_gibberish_words(words):
spell = SpellChecker()
filtered_words = []
for word in words:
if not is_chinese(word): # 检查单词是否为中文
if spell.known([word]): # 对于非中文单词进行拼写检查
filtered_words.append(word)
else:
filtered_words.append(word)
return filtered_words
def Separate_words(content):
# 使用正则表达式的 sub 方法将标点符号替换为空格
content = re.sub("[^\w\s]+", "", content)
content = list(jieba.cut(content, cut_all=False, use_paddle=True))
content = remove_gibberish_words(content)
content = [element for element in content if element.strip() != '']
content = [word for word in content if word.lower() not in stop_words]
return content
def get_title_and_content(current_url, html):
soup = BeautifulSoup(html, 'html.parser')
title = []
if soup.find('title') != None:
title = soup.find('title').get_text()
content = soup.get_text()
content = content.replace('\n', '')
links = get_all_hrefs(html, current_url)
return title, content, links
def module_2(current_url, res_text):
num = len(result) + 1
url = current_url
html = res_text
title, content, links = get_title_and_content(url, html)
content = Separate_words(content)
if compare_hash(content):
to_text_KeyInfor(num, current_url, title, content, links)
return True
return False
def module_1():
# 获取队列头元素
current_url = queue.get()
# 设置异常捕获器,避免异常终止程序
try:
# 发送请求,并设置超时时间为1秒
response = requests.get(url=current_url, headers=headers, timeout=1)
if not response.ok:
return
response.encoding = 'utf-8'
except requests.exceptions.RequestException as e:
# 请求异常,打印错误信息
print(f"请求 {current_url} 出现错误: {e}")
return
# 对共享列表进行增添操作时,一定要上锁避免并发问题
with lock:
if len(result) >= target:
return
res_text = response.text
# 查重和分词操作
if module_2(current_url, res_text) == False:
return
result.append(current_url)
# 将结果写入文件
to_text_html(len(result), current_url, response.text)
print(f'{len(result)}. href = {current_url}')
# 获取一个网页中的所有超链接
all_hrefs = get_all_hrefs(res_text, current_url)
# 上锁,避免线程安全问题
with lock:
for href in all_hrefs:
# 将https替换为http避免重复
new_url = href.replace("https", "http")
if new_url not in visited:
queue.put(new_url)
visited.add(new_url)
if __name__ == '__main__':
# 计时
start_time = time.time()
start_url = 'https://baike.baidu.com/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.82'
}
# 设定爬取数量
target = 480
# visited为浏览过的网站
visited = set()
# 存储已处理网页的 Simhash 值
processed_hashes = list()
# result接收最后的结果
result = []
queue.put(start_url)
visited.add(start_url)
# 创建线程池,提高速度
with ThreadPoolExecutor(48) as pool:
while len(result) < target:
# 执行主函数
pool.submit(module_1)
# module_1()
# 通过在任务执行完毕后使用time.sleep(),可以让线程在暂停的时间内进行休眠,这样其他线程就有机会执行任务,提高了整体的并发效率。
time.sleep(0.1)
# 将线程池关闭,并且取消未完成的请求
pool.shutdown(cancel_futures=True)
# while len(result) < target:
# module_1()
end_time = time.time()
print(end_time - start_time)
问题分析:
根据代码,你的程序主要分为两个模块:module_1和module_2。module_1负责爬取网页并进行处理,module_2负责提取网页内容并去除相似度高的网页。
在多线程的实现中,你使用了线程池ThreadPoolExecutor来管理线程,并通过submit方法提交任务。但是你发现,多线程并没有明显提升程序的运行速度。
问题原因和解决方案:
1. IO密集型任务:根据代码,你的任务主要是请求网页并获取响应。这属于IO密集型任务,即主要消耗时间在等待网络请求的响应上。多线程并不能提供明显的性能提升,因为多线程在等待网络响应时仍然会阻塞,无法同时进行其他任务。
解决方案:考虑使用异步编程框架,如asyncio库,结合异步的网络请求库(如aiohttp)来提高程序的性能。异步编程可以更好地利用CPU资源,在等待IO操作时可以切换到其他任务,从而提高程序的并发性能。
2. 任务调度与线程数量:你的代码中使用了48个线程进行任务的调度和执行。线程并不是越多越好,过多的线程会导致线程切换开销增加,反而降低程序的性能。
解决方案:根据实际情况调整线程数量。可以尝试逐步增加线程数量,找到最优的线程数量。同时,可以通过合理的任务调度策略来提高程序的并发性能。
3. 其他优化措施:除了上述两点,还可以考虑以下优化措施:
- 使用连接池:建议使用连接池来管理网络请求的连接,可以提高网络请求的效率。
- 减少锁的使用:锁的使用会导致线程串行执行,降低并发性能。可以尝试减少锁的使用,或者使用更细粒度的锁来提高并发性能。
- 优化算法和数据结构:对于一些耗时较长的操作,可以考虑优化算法和数据结构,减少时间复杂度,提高程序性能。
希望以上分析和解决方案对你有帮助!如果还有其他问题,请随时提问。
球一个最佳答案谢谢啦!这对我非常重要!
回答可能有误,多多谅解!
|
|