1、数据信息准备
首先当然是准备好要目标网站信息了
一、首先需要获取「登录相关信息」(关键!)
如果网站需要登录,脚本必须先模拟登录才能进行后续操作。请按以下步骤获取信息:
打开登录页面:访问 ,进入登录界面(如果未自动跳转,请找到登录入口)。
http://XXXX/XXXX/
打开开发者工具:按 打开浏览器开发者工具,切换到「Network(网络)」标签页,勾选「Preserve log(保留日志)」(避免登录过程中请求被清空)。
F12
手动完成一次登录输入你的账号密码,点击登录按钮,观察「Network」面板中出现的新请求。
找到登录请求在「Network」面板的请求列表中,找到一个POST 类型的请求(通常 URL 包含 “login”“auth” 等关键词,比如 ),点击这个请求,查看右侧详情:
http://XXX/api/login
需要记录:
「Request Headers」中的「User-Agent」:复制下来(脚本需要模拟浏览器标识)。
「Form Data」或「Payload」:登录时提交的参数(例如 username=你的账号&password=你的密码&captcha=验证码,如果有验证码需要特别说明)。
「Request Method」:确认是 POST(大部分登录是 POST)。
「Request URL」:登录请求的接口地址(例如 http://xxx/login)。
二、获取「搜索查询相关信息」
登录后,手动完成一次搜索(例如输入 “AA”,选择 “描述”,点击查询),按以下步骤获取信息:
监控搜索请求保持「Network」面板开启,点击查询按钮后,找到触发的新请求(可能是 POST 或 GET,通常是 XHR 类型的异步请求,在「XHR/fetch」子标签页中更容易找到)。
记录搜索请求的关键信息点击这个搜索请求,查看右侧详情:
「Request URL」:搜索接口的地址(例如 )。「Request Method」:POST 或 GET(决定脚本中参数的传递方式)。「Form Data」或「Query String Parameters」:搜索时提交的参数,需要重点记录:
http://xxx/search
关键字参数名:例如 中的
keyword=偏航。选项参数名:例如选择 “现象描述” 对应的参数,可能是
keyword 或
type=description(需要确认参数名和选项对应的取值,比如 “现象描述” 对应 1,“jira 链接” 对应 2 等)。
category=1
「Response」:查看返回的响应内容(是 JSON 格式还是 HTML 格式?如果是 JSON,结构是什么样的?这决定了后续如何提取数据)。
三、需要你确认的其他信息
登录后是否需要验证码?登录时需要输入验证码,可能需要手动输入或调用验证码识别接口搜索结果的结构
在「Response」中查看:搜索结果对应的数据字段(例如 )。(如果是 HTML 格式,例如
details: [{id: 1, title: "...", xx: "..."}, ...])。”需要获得返回值”对应的数据字段(例如
<div class="report">...</div>)。
report: {bug: 10, task: 5}
2、脚本配置参数
1. 登录相关配置:配置好对应的信息(下述举例)
LOGIN_URL # 登录接口
USERNAME # 你的用户名(固定)
PASSWORD # 你的加密密码(固定)
DEVICE # 设备标识(固定)
SERVICE # 登录后跳转服务(固定)
2. 搜索相关配置:配置好对应的信息(下述举例)
SEARCH_URL # 搜索接口
SEARCH_PARAMS # 包括搜索关键词、返回条数、数据时间区间等
3. 保存路径配置:
SAVE_DIR = "./triangle_crawl_results"
if not os.path.exists(SAVE_DIR):
os.makedirs(SAVE_DIR) # 自动创建文件夹
3、功能函数
1、创建会话
函数功能:
创建一个持久化的HTTP会话对象设置必要的请求头信息返回配置好的会话对象
关键实现点:
使用创建会话对象,保持cookies和连接池设置了
requests.Session()模拟浏览器访问,避免被识别为爬虫设置了
User-Agent为表单提交格式设置了
Content-Type头,这是登录页面URL,有些网站会验证这个头
Referer
def create_session():
"""创建会话(保持登录状态)"""
session = requests.Session()
# 设置浏览器请求头,避免被识别为爬虫
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Referer": "https://xxx/login?service=" + SERVICE
})
return session
小白理解:
就像一本笔记本,可以记住你和网站的对话
Session()就像便利贴,告诉网站一些基本信息
headers
User-Agent:假装是Chrome浏览器(很多网站不喜欢爬虫程序访问)Content-Type:告诉网站我会发送什么样的数据Referer:告诉网站我是从哪个页面过来的(有些网站会检查这个)
为什么需要这些?
保持登录状态:就像你登录后不用每次都输密码伪装成浏览器:避免被网站发现是程序在访问设置正确格式:让网站能正确理解我们发送的数据
使用例子:
# 先准备好"笔记本"
my_session = create_session()
# 然后用这个"笔记本"登录网站
my_session.post("登录网址", data={"用户名":"xxx", "密码":"xxx"})
# 再访问其他页面时,会自动保持登录状态
my_session.get("个人主页网址")
2、登录函数
def login(session):
"""登录函数:输入otp临时口令,完成登录并验证/不需要本步骤可以跳过"""
# 获取实时otp(用户手动输入)
otp = input("请输入30秒内有效的otp临时口令(如111222):").strip()
if not otp.isdigit() or len(otp) != 6:
print("错误:otp必须是6位数字!")
return False
# 构造登录请求参数;举例如下
login_data = {
"username": USERNAME,
"password": PASSWORD,
"add_trust_device": "on",
"otp": otp,
"device": DEVICE,
"service": SERVICE
}
try:
# 发送登录请求
response = session.post(LOGIN_URL, data=login_data, timeout=15)
response.raise_for_status() # 若状态码非200,抛出异常
# 验证登录是否成功(跳转至目标页面即为成功)
test_response = session.get("http://xxx/triangle/", timeout=10)
if "triangle" in test_response.url or test_response.status_code == 200:
print("✅ 登录成功!")
return True
else:
print("❌ 登录失败:未跳转到目标页面")
return False
except Exception as e:
print(f"❌ 登录出错:{str(e)}")
return False
3、爬取搜索结果函数
函数功能:
用于爬取搜索结果的函数通过POST请求发送搜索参数处理并验证返回的JSON数据打印搜索结果的统计信息返回完整数据或错误处理
def crawl_search_data(session):
"""爬取搜索结果:发送搜索请求,返回完整JSON数据"""
try:
print(f"
🔍 正在搜索关键词:{SEARCH_PARAMS['searchQuery']}")
# 发送搜索POST请求(参数需转成JSON字符串)
response = session.post(
SEARCH_URL,
data=json5.dumps(SEARCH_PARAMS), # 适配特殊JSON格式
headers={"Content-Type": "application/json"}, # 搜索接口需JSON格式
timeout=20
)
response.raise_for_status()
# 解析JSON响应
data = response.json()
# 验证数据完整性(包含feedback/reportData即为成功)
required_keys = ["feedback","reportData"]
if all(key in data for key in required_keys):
print(f"✅ 搜索成功!获取到:")
print(f" - 相似结果:{len(data['feedback'])} 条")
print(f" - 统计报告:{len(data['reportData'])} 项")
return data
else:
print(f"❌ 搜索数据不完整,缺少关键字段")
return None
except Exception as e:
print(f"❌ 搜索出错:{str(e)}")
return None
4、保存数据
函数功能:
将爬取的Jira数据保存到Excel文件创建包含2个工作表的Excel文件:
feedbackreportData
自动生成带时间戳的文件名避免覆盖返回生成的Excel文件路径
def save_to_excel(data, keyword):
"""将数据保存到Excel:分工作表(结果、统计报告)"""
# 生成带时间戳的文件名(避免覆盖)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
excel_path = os.path.join(SAVE_DIR, f"triangle_{keyword}_{timestamp}.xlsx")
# 创建Excel工作簿
wb = Workbook()
# -------------------------- 1. 工作表1:用户反馈(feedback)--------------------------
ws1 = wb.active
ws1.title = "结果"
# 表头(对应feedback的字段)
feedback_headers = ["id", "content", "device", "input_date", "project_code",
"similarity", "submitter_role", "key", "status", "url"]
ws1.append(feedback_headers)
# 填充数据
for item in data["feedback"]:
ws1.append([
item["id"],
item["content"],
item["device"],
item["input_date"],
item["project_code"],
item["similarity"],
item["submitter_role"],
item["key"] or "", # 空值处理
item["status"] or "",
item["url"]
])
# -------------------------- 2. 工作表2:统计报告(reportData)--------------------------
ws2 = wb.create_sheet(title="统计报告")
# 统计报告分3类:priority(优先级)、status(状态)、resolution(解决结果)
ws2.append(["统计类型", "分类", "阻断级(Blocker)", "严重级(Critical)", "主要级(Major)",
"其他(Other)", "总计(Total)", "已关闭(Closed)", "处理中(InProgress)",
"已解决(Resolved)", "已验证(Verified)", "待处理(Open)", "按设计(ByDesign)",
"无法复现(CannotReproduce)", "重复(Duplicate)", "外部问题(External)",
"已修复(Fixed)", "非问题(NotAnIssue)", "未解决(Unresolved)")
for report in data["reportData"]:
# 按统计类型填充对应字段(空值填0)
row = [
report["type"], # 统计类型:priority/status/resolution
report["category"], # 分类:如“测试报告”
report.get("blocker", 0),
report.get("critical", 0),
report.get("major", 0),
report.get("other", 0),
report.get("total", 0),
report.get("closed", 0),
report.get("inProgress", 0),
report.get("resolved", 0),
report.get("verified", 0),
report.get("open", 0),
report.get("byDesign", 0),
report.get("cannotReproduce", 0),
report.get("duplicate", 0),
report.get("external", 0),
report.get("fixed", 0),
report.get("notAnIssue", 0),
report.get("unresolved", 0),
]
ws2.append(row)
# 保存Excel文件
wb.save(excel_path)
print(f"
📊 Excel文件已保存:{excel_path}")
return excel_path
def save_to_json(data, keyword):
"""将原始JSON数据保存到文件(便于后续处理)"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
json_path = os.path.join(SAVE_DIR, f"triangle_{keyword}_{timestamp}.json")
with open(json_path, "w", encoding="utf-8") as f:
json5.dump(data, f, ensure_ascii=False, indent=2) # 格式化输出,支持中文
print(f"📄 JSON文件已保存:{json_path}")
return json_path
5、执行函数
if __name__ == "__main__":
# 1. 创建会话
session = create_session()
# 2. 登录(重试3次机会)
login_success = False
for _ in range(3):
if login(session):
login_success = True
break
else:
print(f"🔄 剩余登录重试次数:{2 - _}")
time.sleep(2) # 间隔2秒再重试
if not login_success:
print("
❌ 登录失败次数过多,程序退出")
exit()
# 3. 爬取搜索数据
data = crawl_search_data(session)
if not data:
print("
❌ 未获取到有效数据,程序退出")
exit()
# 4. 保存数据(Excel + JSON)
keyword = SEARCH_PARAMS["searchQuery"]
save_to_excel(data, keyword)
save_to_json(data, keyword)
print("
" + "=" * 60)
print("任务完成!文件已保存至:", SAVE_DIR)
print("=" * 60)
















暂无评论内容