HttpClient和HtmlUnit的比较总结以及使用技巧(一)

article/2025/10/14 21:47:37

       大家在做爬虫、网页采集、通过网页自动写入数据时基本上都接触过这两个组件(权且称之为组件吧),网上入门资料已经很多了,我想从实际的应用角度谈谈我对于这两个组件的看法,并记录在博客中,以便日后翻阅,欢迎大家批评指正。

        本文主要比较两者的优劣性以及介绍应用中的使用技巧,推荐一些入门资料以及非常实用的辅助工具,希望能对大家有所帮助。

        大家有任何疑问或者建议希望留言给我,一起交流学习。

下面我们首先来看下2个组件的区别和优劣性:

HtmlUnit:

HtmlUnit本来是一款自动化测试的工具,它采用了HttpClient和java自带的网络api结合来实现,它与HttpClient的不同之处在于,它比HttpClient更“人性化”。

 在写HtmlUnit代码的时候,仿佛感觉到的就是在操作浏览器而非写代码,得到页面(getPage)-- 寻找到文本框(getElementByID || getElementByName || getElementByXPath 等等)-- 输入文字(type,setValue,setText等等)-- 其他一些类似操作 -- 找到提交按钮 -- 提交 -- 得到新的Page,这样就非常像一个人在后台帮你操作浏览器一样,而你要做的就是告诉他如何操作以及需要填入哪些值。

      优点:

         一、网页的模拟化

首先说说HtmlUnit相对于HttpClient的最明显的一个好处,HtmlUnit更好的将一个网页封装成了一个对象,如果你非要说HttpClient返回的接口HttpResponse实际上也是存储了一个对象那也可以,但是HtmlUnit不仅保存了这个网页对象,更难能可贵的是它还存有这个网页的所有基本操作甚至事件。这就是说,我们对于操作这个网页可以像在jsp中写js一样,这是非常方便的,比如:你想某个节点的上一个节点,查找所有的按钮,查找样式为“bt-style”的所有元素,对于某些元素先进行一些改造,然后再转成String,或者我直接得到这个网页之后操作这个网页,完成一次提交都是非常方便的。这意味着你如果想分析一个网页会来的非常的容易,比如我附上一段百度新闻高级搜索的代码:

         

	// 得到浏览器对象,直接New一个就能得到,现在就好比说你得到了一个浏览器了WebClient webclient = new WebClient();// 这里是配置一下不加载css和javaScript,配置起来很简单,是不是webclient.getOptions().setCssEnabled(false);webclient.getOptions().setJavaScriptEnabled(false);// 做的第一件事,去拿到这个网页,只需要调用getPage这个方法即可HtmlPage htmlpage = webclient.getPage("http://news.baidu.com/advanced_news.html");// 根据名字得到一个表单,查看上面这个网页的源代码可以发现表单的名字叫“f”final HtmlForm form = htmlpage.getFormByName("f");// 同样道理,获取”百度一下“这个按钮final HtmlSubmitInput button = form.getInputByValue("百度一下");// 得到搜索框final HtmlTextInput textField = form.getInputByName("q1");// 最近周星驰比较火呀,我这里设置一下在搜索框内填入”周星驰“textField.setValueAttribute("周星驰");// 输入好了,我们点一下这个按钮final HtmlPage nextPage = button.click();// 我把结果转成StringString result = nextPage.asXml();System.out.println(result);


然后你可以把得到的result结果复制到一个文本,然后用浏览器打开该文本,是不是想要的东西(如图结),很简单对吧,为什么会感觉简单,因为它完全符合我们操作浏览器的习惯,当然最终它也是用HttpClient和其它一些工具类实现的,但是这样的封装是非常人性化和令人惊叹的。


Htmlunit可以有效的分析出 dom标签,并且可以有效的运行页面上的js以便得到一些需要执行JS才能得到的值,你仅仅需要做的就是执行executeJavaScript()这个方法而已,这些都是HtmlUnit帮我们封装好,我们要做的仅仅是告诉它需要做什么。

                WebClient webclient = new WebClient();HtmlPage htmlpage = webclient.getPage("you url");htmlpage.executeJavaScript("the function name you want to execute");


对于使用Java程序员来说,对对象的操作就再熟悉不过了,HtmlUnit所做的正是帮我们把网页封装成一个对象,一个功能丰富的,透明的对象。


二、网络响应的自动化处理

HtmlUnit拥有强大的响应处理机制,我们知道:常见的404是找不到资源,100等是继续,300等是跳转...我们在使用HttpClient的时候它会把响应结果告诉我们,当然,你可以自己来判断,比如说,你发现响应码是302的时候,你就在响应头去找到新的地址并自动再跳过去,发现是100的时候就再发一次请求,你如果使用HttpClient,你可以这么去做,也可以写的比较完善,但是,HtmlUnit已经较为完整的实现了这一功能,甚至说,他还包括了页面JS的自动跳转(响应码是200,但是响应的页面就是一个JS),天涯的登录就是这么一个情况,让我们一起来看下。

                  

/*** @author CaiBo* @date 2014年9月15日 上午9:16:36* @version $Id$* */
public class TianyaTest {/*** */public static void main(String[] args) throws Exception {// 这是一个测试,也是为了让大家看的更清楚,请暂时抛开代码规范性,不要纠结于我多建了一个局部变量等// 得到认证https的浏览器对象HttpClient client = getSSLInsecureClient();// 得到我们需要的post流HttpPost post = getPost();// 使用我们的浏览器去执行这个流,得到我们的结果HttpResponse hr = client.execute(post);// 在控制台输出我们想要的一些信息showResponseInfo(hr);}private static void showResponseInfo(HttpResponse hr) throws ParseException, IOException {System.out.println("响应状态行信息:" + hr.getStatusLine());System.out.println("---------------------------------------------------------------");System.out.println("响应头信息:");Header[] allHeaders = hr.getAllHeaders();for (int i = 0; i < allHeaders.length; i++) {System.out.println(allHeaders[i].getName() + ":" + allHeaders[i].getValue());}System.out.println("---------------------------------------------------------------");System.out.println("响应正文:");System.out.println(EntityUtils.toString(hr.getEntity()));}// 得到一个认证https链接的HttpClient对象(因为我们将要的天涯登录是Https的)// 具体是如何工作的我们后面会提到的private static HttpClient getSSLInsecureClient() throws Exception {// 建立一个认证上下文,认可所有安全链接,当然,这是因为我们仅仅是测试,实际中认可所有安全链接是危险的SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {return true;}}).build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);return HttpClients.custom().//setSSLSocketFactory(sslsf)//// .setProxy(new HttpHost("127.0.0.1", 8888)).build();}// 获取我们需要的Post流,如果你是把我的代码复制过去,请记得更改为你的用户名和密码private static HttpPost getPost() {HttpPost post = new HttpPost("https://passport.tianya.cn/login");// 首先我们初始化请求头post.addHeader("Referer", "https://passport.tianya.cn/login.jsp");post.addHeader("Host", "passport.tianya.cn");post.addHeader("Origin", "http://passport.tianya.cn");// 然后我们填入我们想要传递的表单参数(主要也就是传递我们的用户名和密码)// 我们可以先建立一个List,之后通过post.setEntity方法传入即可// 写在一起主要是为了大家看起来方便,大家在正式使用的当然是要分开处理,优化代码结构的List<NameValuePair> paramsList = new ArrayList<NameValuePair>();/* * 添加我们要的参数,这些可以通过查看浏览器中的网络看到,如下面我的截图中看到的一样* 不论你用的是firebut,httpWatch或者是谷歌自带的查看器也好,都能查看到(后面会推荐辅助工具来查看)* 要把表单需要的参数都填齐,顺序不影响*/paramsList.add(new BasicNameValuePair("Submit", ""));paramsList.add(new BasicNameValuePair("fowardURL", "http://www.tianya.cn"));paramsList.add(new BasicNameValuePair("from", ""));paramsList.add(new BasicNameValuePair("method", "name"));paramsList.add(new BasicNameValuePair("returnURL", ""));paramsList.add(new BasicNameValuePair("rmflag", "1"));paramsList.add(new BasicNameValuePair("__sid", "1#1#1.0#a6c606d9-1efa-4e12-8ad5-3eefd12b8254"));// 你可以申请一个天涯的账号 并在下两行代码中替换为你的用户名和密码paramsList.add(new BasicNameValuePair("vwriter", "ifugletest2014"));// 替换为你的用户名paramsList.add(new BasicNameValuePair("vpassword", "test123456"));// 你的密码// 将这个参数list设置到post中post.setEntity(new UrlEncodedFormEntity(paramsList, Consts.UTF_8));return post;}}


执行上面这个Main函数你会得到以下结果:



我们看到,响应码确实是200,表明成功了,其实这个响应相当于是302,它是需要跳转的,只不过它的跳转写到了body部分的js里面而已。

<script>location.href="http://passport.tianya.cn:80/online/loginSuccess.jsp?fowardurl=http%3A%2F%2Fwww.tianya.cn%2F94693372&userthird=&regOrlogin=%E7%99%BB%E5%BD%95%E4%B8%AD......&t=1410746182629&k=8cd4d967491c44c5eab1097e0f30c054&c=6fc7ebf8d782a07bb06624d9c6fbbf3f";
</script>

它这是一个页面上的跳转

那这个时候如果你使用HttpClient就头疼了(当然也是可以处理的,后面讲到)。如果你使用的是HtmlUnit,整个过程显得简单轻松。

public class TianyaTestByHtmlUnit {public static void main(String[] args) throws Exception {WebClient webClient = new WebClient();// 拿到这个网页HtmlPage page = webClient.getPage("http://passport.tianya.cn/login.jsp");// 填入用户名和密码HtmlInput username = (HtmlInput) page.getElementById("userName");username.type("ifugletest2014");HtmlInput password = (HtmlInput) page.getElementById("password");password.type("test123456");// 提交HtmlButton submit = (HtmlButton) page.getElementById("loginBtn");HtmlPage nextPage = submit.click();System.out.println(nextPage.asXml());}
}
这样简单的几行代码就完成了。
     


三、并行控制 和串行控制

        既然HtmlUnit封装了那么多的底层api和hHttpClient操作,那么它有没有给我们提供自定义各种响应策略和监听整个执行过程的方法呢?,答案是肯定的。由于HtmlUnit提供的监听和控制方法比较多,我说几个大家可能接触比较少,但很有用的方法。其他的类似于:设置CSS有效,设置不抛出JS异常,设置使用SSL安全链接,诸如此类,大家通过webClient.getOptions().set***,就可以设置了,这种大家都比较熟了。

        (1)首先来看一下JS错误处理监听机制,我们打开HtmlUnit源码可以看到(该源码位置在JavaScriptEngine类中的handleJavaScriptException方法处)

  protected void handleJavaScriptException(final ScriptException scriptException, final boolean triggerOnError) {// Trigger window.onerror, if it has been set.final HtmlPage page = scriptException.getPage();if (triggerOnError && page != null) {final WebWindow window = page.getEnclosingWindow();if (window != null) {final Window w = (Window) window.getScriptObject();if (w != null) {try {w.triggerOnError(scriptException);}catch (final Exception e) {handleJavaScriptException(new ScriptException(page, e, null), false);}}}}// 这里尝试去取我们设置的JavaScript错误处理器final JavaScriptErrorListener javaScriptErrorListener = getWebClient().getJavaScriptErrorListener();if (javaScriptErrorListener != null) {javaScriptErrorListener.scriptException(page, scriptException);}// Throw a Java exception if the user wants us to.if (getWebClient().getOptions().isThrowExceptionOnScriptError()) {throw scriptException;}// Log the error; ScriptException instances provide good debug info.LOG.info("Caught script exception", scriptException);}


也就是说我们它在发现JS错误的时候会自动去寻找我们是否有处理器,有的话就会用我们设置的处理器来处理,要在webClient里加一个处理器也非常的方便。使用:

webClient.setJavaScriptErrorListener(new 你自己的JavaScriptErrorListener());即可。自己的JavaScriptErrorListener也很好实现,直接继承JavaScriptErrorListener接口即可,然后你就可以在javaScript出错时自行处理,你可以选择分析它的url、修正它的url、重新再获取或者直接忽略等等。有js错误处理器,当然也还有别的了,这一类型的我就只说一个了。为了防止有小白不明白,我还是贴出一个简单的实现好了。

/*** @author CaiBo* @date 2014年8月12日 上午12:32:08* @version $Id: WaiJavaScriptErrorListener.java 3943 2014-08-12 03:54:25Z CaiBo $* */
public class WaiJavaScriptErrorListener implements JavaScriptErrorListener {public WaiJavaScriptErrorListener() {}@Overridepublic void scriptException(HtmlPage htmlPage, ScriptException scriptException) {// TODO Auto-generated method stub}@Overridepublic void timeoutError(HtmlPage htmlPage, long allowedTime, long executionTime) {// TODO Auto-generated method stub}@Overridepublic void malformedScriptURL(HtmlPage htmlPage, String url, MalformedURLException malformedURLException) {// TODO Auto-generated method stub}@Overridepublic void loadScriptError(HtmlPage htmlPage, URL scriptUrl, Exception exception) {// TODO Auto-generated method stub}public static void main(String[] args) {WebClient webClient = new WebClient();webClient.setJavaScriptErrorListener(new WaiJavaScriptErrorListener());}
}
Main方法处实现了JS错误自定义处理的webClient

(2)链接响应监听

       很多时候我们想看看HtmlUnit到底去拿了什么东西,或者说我想对它拿的东西过滤一下,再或者我想把它拿到的某些东西存起来,那这个时候响应监听就很必要了。比如说一个最简单的响应监听。

/*** @author CaiBo* @date 2014年9月15日 上午10:59:30* @version $Id$**/
public class SimpleConectionListener extends FalsifyingWebConnection {private static final Logger LOGGER = LoggerFactory.getLogger(SimpleConectionListener.class);public SimpleConectionListener(WebClient webClient) throws IllegalArgumentException {super(webClient);}@Overridepublic WebResponse getResponse(WebRequest request) throws IOException {// 得到了这个响应,你想怎么处理就怎么处理了,不多写了WebResponse response = super.getResponse(request);String url = response.getWebRequest().getUrl().toString();if (LOGGER.isDebugEnabled()) {LOGGER.debug("下载文件链接:" + url);}if (check(url)) {return createWebResponse(response.getWebRequest(), "", "application/javascript", 200, "Ok");}return response;}private boolean check(String url) {// TODO 加入你自己的判断什么的return false;}}
这样我们就实现了一个自己的监听器,虽然比较简陋。现在我们把它设置到我们的webClient里面去。

                WebClient webClient = new WebClient();// 如果你好奇这里仅仅传进去没有返回,怎么webClient就改变了,你可以到这个实例化里面看下就明白了new WebConnectionListener(webClient);// 这个webClient在上一步之后,已经被监听了webClient.getPage("someUrl");

结果就如上图所示了。

 HtmlUnit还有其他许多并、串行控制方法,统一cookie,统一连接池等等,就不一一叙述了。


四、强大的缓存机制
为什么第一次获取一个网页可能会比较慢,但是第二次来拿就特别快呢?在HtmlUnit源码webClient类中的loadWebResponseFromWebConnection方法中我们可以看到。
 final WebResponse fromCache = getCache().getCachedResponse(webRequest);final WebResponse webResponse;if (fromCache != null) {webResponse = new WebResponseFromCache(fromCache, webRequest);}else {try {webResponse = getWebConnection().getResponse(webRequest);}catch (final NoHttpResponseException e) {return new WebResponse(responseDataNoHttpResponse_, webRequest, 0);}getCache().cacheIfPossible(webRequest, webResponse, null);}
当然,它还有许多别的缓存机制来加快我们的访问速度,减少带宽压力。

劣势:

相对于HttpClient来说,HtmlUnit的优点大致就这么多了,那相对于HttpClient来说,短程距离上(访问量小的情况下),HtmlUnit的性能是不如HttpClient的,这也很容易理解,HtmlUnit把HttpClient封装了一层嘛,在短程距离行不如HttpClient就很正常了,在具体的业务下,那就要看程序员水平了。



写太长我自己容易疏忽,大家看着也累,所以第一篇就只谈一下HtmlUnit的优势和劣势了,下一篇将讲述HttpClient的优势和劣势,之后再对他们进行详细比较以及介绍技巧。



http://chatgpt.dhexx.cn/article/L75kpaV4.shtml

相关文章

htmlUnit的读取js渲染的页面

htmlUnit的读取js渲染的页面 我这边有个这么的需求&#xff0c;要求前端每次上版本的时候&#xff0c;有一个版本号。然后后端去爬前端&#xff0c;比较禅道的版本发布计划&#xff0c;如果发布计划和生产上的版本号不一致&#xff0c;就告警。 这个跟后端的jar包&#xff0c…

HtmlUnit

HtmlUnit是什么&#xff1f;看下介绍&#xff1a; A java GUI-Less browser, which allows high-level manipulation of web pages, such as filling forms and clicking links; just getPage(url), find a hyperlink, click() and you have all the HTML, JavaScript, and A…

HtmlUnit使用

htmlunit 是一款开源的java 页面分析工具&#xff0c;读取页面后&#xff0c;可以有效的使用htmlunit分析页面上的内容。项目可以模拟浏览器运行&#xff0c;被誉为java浏览器的开源实现。是一个没有界面的浏览器&#xff0c;运行速度迅速。是junit的扩展之一&#xff0c;它采用…

【转】 HtmlUnit简介

HtmlUnit简介&#xff1a; 引自 http://www.javaeye.com/topic/166702 HtmlUnit 是 JUnit 的扩展测试框架之一。HtmlUnit 将返回文档模拟成 HTML&#xff0c;这样您便可以直接处理这些文档了。HtmlUnit 使用例如 table、form 等标识符将测试文档作为 HTML 来处理。它同样需要遵…

HtmlUnit(Java) - 快速入门学习 - 无界面浏览器

文章目录 1. 概述2. 注意2.0 js解析问题2.1 关闭HtmlUnit日志 3. 使用3.1 抓取IT之家周榜内容 - 单页面3.2 抓取IT之家周榜第九篇文章的内容 - 双页面3.3 模拟用户操作 - &#xff08;这个功能个人感觉非常非常的鸡肋&#xff0c;只能用于非常简单的JS&#xff0c;但是一般网站…

【零基础】快速入门爬虫框架HtmlUnit

迅速的HtmlUnit htmlunit是一款开源的web页面分析工具&#xff0c;理论上来说htmlunit应用于网页的自动化测试&#xff0c;但是相对来说更多人使用它来进行小型爬虫的快速开发。使用htmlunit进行爬虫开发不仅是其运行速度快&#xff0c;更重要的是此框架上手更为容易&#xff0…

保研之路——哈深计算机预推免

哈深计算机预推免 个人情况高校复试参与情况哈工深计算机学院直硕&#xff08;7.20&#xff09;结语 嗯&#xff01;抱着不白花这么多路费住宿费的初衷准备写一个保研经验贴&#xff0c;希望学弟学妹少花点钱吧orz 我的战术大概是只要学校给我发了邀请我就去&#xff08;除了时…

哈工大计算机学院统一复试划线,哈工大计算机专业,复试比例101%,擦线党没戏了...

原标题&#xff1a;哈工大计算机专业&#xff0c;复试比例101%&#xff0c;擦线党没戏了 这几天哈工大各院系公布了复试线及复试名单&#xff0c;其中「计算机学院」的复试考生比例居然是101%。 哈工大2020年计算机专业的考研复试线为320分(学术学位)&#xff0c;以及专业学位的…

【2024考研】哈工大计算机考研854会改成408吗?优缺点分析?怎么复习?哈工大卓越工程师学院点击就送吗?

文章目录 1.哈工大计算机考研专业课的发展历史2.哈工大卓越工程师学院点击就送吗&#xff1f;3.哈工大计算机854会改成408吗&#xff1f;4.哈工大计算机854的优缺点分析4.1 优点4.2 缺点 5.哈工大计算机854怎么复习&#xff1f;5.1 计算机系统(CSAPP)复习指导5.2 计算机网络复习…

【考研】哈尔滨工业大学计算机考研854复习资料

哈尔滨工业大学计算机考研854复习资料 0. 考研初试&复试经验贴1. 初试专业课复习资料资源1.1 复习资料下载1.2 复习资料清单 2. 初试专业课复习资料使用指南2.1 CSAPP资料2.2 数据结构资料2.3 计算机网络资料 更新历史&#xff1a; 2022年4月22日完成初稿2022年5月7日加入…

计算机排名哈工大第三,国内计算机高校排名:哈工大稳坐第2,浙江大学第3,西电第8...

QS世界大学计算机H指数(H指数为混合量化指标&#xff0c;可用于评估研究人员的学术产出量和学术产出水平)前50名中&#xff0c;中国有9所(包括香港大学3所)上榜&#xff0c;前100名中有清华大学、哈工大学、浙江大学等14所世界一流大学建设高校&#xff0c;学校有西安电子科技大…

哈工大深圳计算机就业质量报告,多所高校公布毕业生平均年薪,南京大学和哈工大(深圳)数据亮眼...

原标题:多所高校公布毕业生平均年薪,南京大学和哈工大(深圳)数据亮眼 对于高中学子而言,在将来报考大学时,除了根据自己的兴趣爱好之外,还比较关注对应高校的就业率以及就业质量。毕竟将来大学毕业后还是要走向社会就业的。所以就业后的薪资待遇就成了大家乐意关注的一个点…

哈工大2021秋机器学习期末试题

哈工大2021秋机器学习期末试题 刚刚经历了机器学习复习和考试&#xff0c;这过程简直是太折磨了。 这门课的期末考试往年题还是很有参考价值的。所以我在考试的时候抽了点时间把期末题记了一下&#xff0c;希望能对学弟、学妹&#xff08;如果有的话&#xff09;考前复习有所…

哈尔滨工程大学计算机学院保研政策,哈工大保研条件(哈工大2019保研政策)

哈工大保研条件(哈工大2019保研政策) 2020-05-08 10:50:43 共10个回答 去年保研的确实增加了,主要是总校的增加了(40%多),深圳和威海的基本上变化不大,保研率没有超过40%.但是值得特别注意的是,复试中取消了前25%免除笔试的原则.以至于好多总校和威海前25%的同学虽进入复试,却因…

2020哈工大(威海)计算机夏令营面试

2020哈工大威海计算机夏令营面试 6月底收到哈工大(威海)的入营通知&#xff0c;当时我正在参加中南大学的夏令营&#xff0c;因为我校往届也有好多学长学姐保研到哈威&#xff0c;所以入营概率相对较大&#xff0c;也算是我夏令营拿到的为数不多的入营&#xff0c;另外三个分别…

哈工程计算机系保研率,武汉理工VS哈工程,20保研率哈工程高,哪所值得考?学长精准分析...

对于国内的很多工科高校&#xff0c;由于主打的专业都是国民经济不可或缺的专业&#xff0c;但由于近几年实体经济不景气&#xff0c;很多考生都转而报考计算机相关专业、财经类专业&#xff0c;以前很吃香的航空航天类、电气类、材料类专业都不再受热捧&#xff0c;但这类学校…

哈师大计算机学院宿舍,新生攻略|哈师大所有的“秘密”都在这了

原标题:新生攻略|哈师大所有的“秘密”都在这了 你好,我是哈师大17级的新生,我想全面的了解一下咱们学校,应该去哪了解呀? 这你可算是问对人了,听说最近哈尔滨师范大学学生会的官方微信平台出了非常全的新生攻略呢,快关注它们的公众号(hsdxsh)看看吧! 哇!这么棒!我要…

2020哈工程上岸初复试经验

2020哈工程上岸初复试经验 写在前面&#xff1a; 2020年注定是在研究生考试中被记住的一年&#xff0c;这一年考研的同学们都经历了太多的考验&#xff0c;有幸运的&#xff0c;也有不幸的。经过了漫长的等待&#xff0c;终于&#xff0c;拟录取名单下来了&#xff0c;我也很幸…

2019哈工大计算机考研复试,哈工大计算机专业,复试比例101%,擦线党没戏了...

这几天哈工大各院系公布了复试线及复试名单&#xff0c;其中「计算机学院」的复试考生比例居然是101%。 哈工大2020年计算机专业的考研复试线为320分(学术学位)&#xff0c;以及专业学位的电子信息&#xff0c;也是320分&#xff0c;哈工大公布的校线也是这个分数&#xff0c;院…

哈工大计算机报深圳还是本部,哈工大本部和哈工大深圳哪个比较好?

眼下各省市的高考成绩陆续公布&#xff0c;考生和家长们进入了填报志愿的关键时期&#xff0c;这个时候我们会发现一个问题&#xff1a;有些学校会有很多分校或分校区&#xff0c;比如东北大学和东北大学秦皇岛分校、山东大学和山东大学威海校区等&#xff0c;这些校区和分校跟…