Python正则返回元组列表而非预期列表,爬取JS数组版本信息遇问题
嘿,我来帮你搞定这个问题!你提到用Python的re模块提取JS数组里的版本信息时,得到了元组列表但想要普通列表,结合你爬100多个网页的场景,咱们一步步来解决:
一、为啥会返回元组列表?
通常是因为你的正则表达式里用了多个捕获分组(就是那些括号),或者不小心加了不必要的括号。比如如果你写了类似这样的正则:
pattern = r"Appname\[(\d+)\]='(.*?)'; Appversion\[(\d+)\]='(.*?)'" matches = re.findall(pattern, js_content)
re.findall会把每个分组的结果打包成元组,最后返回一堆元组组成的列表。如果只想要版本字符串的普通列表,或者把名字和版本整理成更实用的格式,咱们可以从两个方向调整。
二、解决方案:两种思路任选
1. 调整正则,直接得到普通列表(推荐)
如果你只需要Appversion的值,就把正则写成只捕获版本部分,去掉多余的括号:
import re from bs4 import BeautifulSoup # 假设你已经用BeautifulSoup拿到了页面内容 soup = BeautifulSoup(page_content, "html.parser") script_tag = soup.find("script", language="Javascript") js_content = script_tag.string # 只匹配Appversion的内容,正则里只有一个捕获分组 version_pattern = r"Appversion\[\d+\]='(.*?)'" versions = re.findall(version_pattern, js_content) # 现在versions就是普通字符串列表啦,比如['6.02 TEST prod', '6.02 TEST ', '06.0001.00']
如果同时需要Appname和Appversion,想把元组转成更友好的格式(比如字典列表),可以这么写:
# 匹配名字和版本,两个有效捕获分组 name_version_pattern = r"Appname\[\d+\]='(.*?)';\s*Appversion\[\d+\]='(.*?)'" matches = re.findall(name_version_pattern, js_content) # 转成字典列表,方便后续处理 app_list = [{"应用名称": name, "版本号": version} for name, version in matches]
2. 直接处理已有的元组列表
如果不想改正则,就对已经得到的元组列表做转换:
# 假设你已经有了tuple_list = [('foobar', '6.02 TEST prod'), ('barfoo', '06.0001.00'), ...] # 只提取版本号,得到普通列表 versions = [item[1] for item in tuple_list] # 或者把所有元素扁平化,变成一个普通列表 flat_list = [content for tuple_item in tuple_list for content in tuple_item]
三、适配你爬取场景的完整脚本示例
结合遍历IP、爬取页面、提取版本、写入文件的流程,给你一个完整的参考片段:
import re import requests from bs4 import BeautifulSoup import time # 你的IP列表,替换成实际的 ip_list = ["192.168.1.1", "192.168.1.2", "192.168.1.3"] output_file = "app_versions_record.txt" with open(output_file, "w", encoding="utf-8") as f: for ip in ip_list: try: url = f"http://{ip}" # 加超时和小延迟,避免给服务器太大压力 response = requests.get(url, timeout=10) response.raise_for_status() time.sleep(0.5) soup = BeautifulSoup(response.text, "html.parser") script_tag = soup.find("script", language="Javascript") if not script_tag: f.write(f"{ip}: 未找到目标JS脚本标签\n") continue js_content = script_tag.string # 兼容可能的单双引号和转义字符 pattern = r"Appname\[(\d+)\]=(['\"])(.*?)\2;\s*Appversion\[(\d+)\]=(['\"])(.*?)\5" matches = re.findall(pattern, js_content) f.write(f"=== 服务器 {ip} 的应用版本信息 ===\n") # 提取名字和版本,跳过索引和引号分组 for _, _, name, _, _, version in matches: f.write(f"应用名称: {name}, 版本号: {version}\n") f.write("\n") except Exception as e: f.write(f"{ip}: 请求失败 - {str(e)}\n") continue
四、几个实用小提示
- 注意JS里的字符串可能用双引号,或者有转义的单引号(比如
I\'m app),上面的正则用了(['\"])来兼容单双引号,\2和\5用来匹配对应的闭合引号,避免匹配出错。 - 如果遇到压缩后的JS代码(没有换行),正则依然有效,因为
\s*可以匹配任意空白字符(空格、换行、制表符都算)。 - 爬100多个IP时,记得加个小延迟(比如
time.sleep(0.5)),不然容易被目标服务器拦截或者造成服务压力。
内容的提问来源于stack exchange,提问作者gloopy
相关产品推荐
相关产品推荐

