-
Notifications
You must be signed in to change notification settings - Fork 1
/
cnkiGui.py
304 lines (268 loc) · 11 KB
/
cnkiGui.py
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
import requests # 发送请求
import os # 判断文件存在
from time import sleep # 等待间隔
from lxml import etree
from openpyxl import load_workbook, Workbook
from enum import Enum
class WriteMode(Enum):
APPEND = 0 # 追加
OVERWRITE = 1 #重新写入
def main(*args): #base_url search_word book_path article_num(int) , MY_GUI
base_url = args[0] #要爬取的网页链接
# 1.爬取网页
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
}
search_word = args[1]
mode = None
if args[2].endswith('.xlsx'):
book_path = args[2]
mode = WriteMode.APPEND
else:
book_path = args[2] + '/' +search_word + '.xlsx'
mode = WriteMode.OVERWRITE
page_num = int(args[3]/20 + 1)
gui = args[4]
# 调用函数
print("开始爬取.......")
gui.add_text("开始爬取.......")
page_all = []
for page in range(1,page_num+1):
page_text =get_page_text(base_url,headers,search_word,page,gui)
page_info =parse_page_text(page_text,headers,gui)
page_all = page_all + page_info
sheet_name = search_word + "-" + str(page)
print("开始保存:第" + str(page) + "页")
# write_to_excel(book_path,page_info,sheet_name,WriteMode.APPEND)
write_to_excel(book_path,page_info,sheet_name,gui,mode)
# 显示所有文献
gui.show_cnki_article_list(page_all)
print("保存完成!")
gui.add_text("保存完成!")
# url: 需要进行POST请求的URL地址,是一个字符串类型
# headers: HTTP请求头信息,通常是一个字典类型,包含如Content-Type, Authorization等键值对
# search_word: 用于搜索的关键词,是一个字符串类型
# page_num: 需要获取的页面编号,是一个整数类型
# page_text: 返回的页面文本内容,是一个字符串类型
# get_page_text 函数是一个用于向指定URL发送POST请求以获取页面文本内容的函数
def get_page_text(url, headers, search_word,page_num,gui):
if gui.stop_flag== True:
gui.add_text("中止爬取")
return
# 定义一个字典data,用于存放POST请求的参数
data = {
'searchType': 'MulityTermsSearch',
'ArticleType': '',
'ReSearch': '',
'ParamIsNullOrEmpty': 'false',
'Islegal': 'false',
'Content': '',
'Theme': search_word,
'Title': '',
'KeyWd': '',
'Author': '',
'SearchFund': '',
'Originate': '',
'Summary': '',
'PublishTimeBegin': '',
'PublishTimeEnd': '',
'MapNumber': '',
'Name': '',
'Issn': '',
'Cn': '',
'Unit': '',
'Public': '',
'Boss': '',
'FirstBoss': '',
'Catalog': '',
'Reference': '',
'Speciality': '',
'Type': '',
'Subject': '',
'SpecialityCode': '',
'UnitCode': '',
'Year': '',
'AcefuthorFilter': '',
'BossCode': '',
'Fund': '',
'Level': '',
'Elite': '',
'Organization': '',
'Order': '1',
'Page': str(page_num),
'PageIndex': '',
'ExcludeField': '',
'ZtCode': '',
'Smarts': '',
}
# 使用requests库的post方法发送POST请求,传入url, headers和data作为参数
response = requests.post(url=url, headers=headers, data=data)
# 获取响应的文本内容
page_text = response.text
if gui.stop_flag== True:
gui.add_text("中止爬取")
return
return page_text
# list 转 str
def list_to_str(my_list):
# 使用列表推导式将嵌套列表展平
flattened_list = [item for sublist in my_list for item in sublist if isinstance(item, str)]
# 确保展平后的列表中只包含字符串
if all(isinstance(item, str) for item in flattened_list):
my_str = "".join(flattened_list)
return my_str
else:
raise ValueError("List contains non-string items after flattening.")
# url: 一个字符串,表示要从中提取摘要的网页的URL
# abstract: 一个列表,包含从网页中提取的摘要文本。
# 列表中的每个元素都是一个字符串,代表从class为"xx_font"的<div>元素中捕获的一段文本内容
# 从给定的网页URL中提取摘要文本,在parse_page_text调用
def get_abstract(url,headers):
try:
# 发送GET请求
response = requests.get(url, headers=headers)
# 检查请求是否成功
response.raise_for_status()
# 提取网页文本内容
page_text = response.text
# 尝试解析网页内容
try:
tree = etree.HTML(page_text)
except etree.ParseError:
# 处理HTML解析错误
return [], "Failed to parse the HTML content."
# 使用XPath查询提取摘要
try:
abstract = tree.xpath('//div[@class="xx_font"]//text()')
except etree.XPathEvalError:
# 处理XPath查询错误
return [], "Invalid XPath query or no matching elements found."
# 返回摘要文本列表
return abstract, "Success"
except requests.exceptions.RequestException as e:
# 处理网络请求错误
return [], f"Network request failed: {e}"
except Exception as e:
# 处理其他未知异常
return [], f"An unexpected error occurred: {e}"
# xpath解析,接受html,返回[]
# page_text: HTML格式的字符串,通常是从网页上抓取下来的
# page_info: 这是一个二维列表,
# 其中每个内部列表都包含一条从HTML中解析出来的信息
# 包括标题、作者、文献来源、文献类型、出版日期、摘要、关键词、下载量、被引量以及链接
# HTML解析:
# tree = etree.HTML(page_text):
# 使用etree.HTML解析HTML文本,生成一个可以进行XPath查询的对象
# XPath查询:
# item_list = tree.xpath('//div[@class="list-item"]'):
# 查询所有class为"list-item"的<div>元素
# 遍历和提取信息
# parse_page_text 函数的功能是从给定的 HTML 页面文本(page_text)中解析出特定结构的信息,
# 并将这些信息整理成一个列表的列表(page_info)返回
def parse_page_text(page_text,headers,gui):
if gui.stop_flag== True:
gui.add_text("中止爬取")
return
tree = etree.HTML(page_text)
item_list = tree.xpath('//div[@class="list-item"]')
page_info = []
print("解析一页信息开始......")
gui.add_text("解析一页信息开始......")
number = 0 # 文献序号
for item in item_list:
if gui.stop_flag== True:
gui.add_text("中止爬取")
return
# 标题
title = list_to_str(item.xpath(
'./p[@class="tit clearfix"]/a[@class="left"]/@title'))
# 链接
link = 'https:' +\
list_to_str(item.xpath(
'./p[@class="tit clearfix"]/a[@class="left"]/@href'))
# 作者
author = list_to_str(item.xpath(
'./p[@class="source"]/span[1]/@title'))
# 出版日期
date = list_to_str(item.xpath(
'./p[@class="source"]/span[last()-1]/text() | ./p[@class="source"]/a[2]/span[1]/text() '))
# 关键词
keywords = list_to_str(item.xpath(
'./div[@class="info"]/p[@class="info_left left"]/a[1]/@data-key'))
# 摘要
abstract = list_to_str(get_abstract(url=link,headers=headers))
# 文献来源
paper_source = list_to_str(item.xpath(
'./p[@class="source"]/span[last()-2]/text() | ./p[@class="source"]/a[1]/span[1]/text() '))
# 文献类型
paper_type = list_to_str(item.xpath(
'./p[@class="source"]/span[last()]/text()'))
# 下载量
download = list_to_str(item.xpath(
'./div[@class="info"]/p[@class="info_right right"]/span[@class="time1"]/text()'))
# 被引量
refer = list_to_str(item.xpath(
'./div[@class="info"]/p[@class="info_right right"]/span[@class="time2"]/text()'))
item_info = [i.strip() for i in [title, author, paper_source, paper_type, date, abstract, keywords, download, refer, link]]
page_info.append(item_info)
number = number + 1
print("解析 文献" + str(number) + ": "+ title +" 成功......")
gui.add_text("解析 文献" + str(number) + ": "+ title +" 成功......")
print("解析一页信息完成......")
gui.add_text("解析一页信息完成......")
return page_info
# 保存数据
# workbook:一个已存在的Excel工作簿对象。
# info:一个包含多个数据行的列表,其中每一行都是一个包含标题对应信息的列表。
# search_word:一个字符串,用于作为新创建的工作表的名称。
def write_to_excel(book_path, info,sheet_name,gui,mode=WriteMode.OVERWRITE):
if gui.stop_flag== True:
gui.add_text("中止爬取")
return
sheet = None
if not os.path.exists(book_path):
book = Workbook()
# 删除默认创建的Sheet(通常名为'Sheet')
book.remove(book.active)
else:
book = load_workbook(book_path)
# 选择--追加,或者重新写入.xlsx
try:
if mode == WriteMode.OVERWRITE: # 默认重新写入
try:
book.remove(book[sheet_name]) # 删除原有的sheet
except KeyError:
# 工作表不存在时忽略错误
pass
sheet = book[sheet_name] # 新建
except KeyError:
sheet = book.create_sheet(title=sheet_name)
# col = ['title', 'author', 'paper_source', 'paper_type', 'date', 'abstract', 'keywords', 'download', 'refer', 'link'] # 设置表头
col = ['标题', '作者', '文献来源', '类型', '出版日期', '摘要', '关键词', '下载量', '被引量', '链接'] # 设置表头
# 设置表头,只在工作表是新创建的时候写入
if sheet.max_row == 1 and sheet.max_column == 1:
for col_idx, header_name in enumerate(col, start=1):
sheet.cell(row=1, column=col_idx, value=header_name)
# append,参数是一个列表或者元组
for data in info:
if gui.stop_flag== True:
gui.add_text("中止爬取")
return
sheet.append(data)
try:
book.save(book_path)
print("文件保存成功!")
gui.add_text("文件保存成功!")
except PermissionError as pe:
print(f"保存文件时发生权限错误: {pe}")
gui.add_text(f"保存文件时发生权限错误: {pe}")
except Exception as e:
print(f"保存文件时发生错误: {e}")
gui.add_text(f"保存文件时发生错误: {e}")
return True
# if __name__ == "__main__": # 当程序执行时
# from netGui import MY_GUI as gui
# # 基础配置,模拟url,headers
# main()
# print("爬取完毕!")
# gui.add_text("爬取完毕!")